mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-23 08:03:48 +01:00
Merge branch 'dev' of github.com:danielyxie/bitburner into feature/grafting
This commit is contained in:
commit
dfca624e35
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@ -3,9 +3,9 @@ name: CI
|
|||||||
on:
|
on:
|
||||||
# Triggers the workflow on push or pull request events but only for the dev branch
|
# Triggers the workflow on push or pull request events but only for the dev branch
|
||||||
push:
|
push:
|
||||||
branches: [ dev ]
|
branches: [dev]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ dev ]
|
branches: [dev]
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
# Allows you to run this workflow manually from the Actions tab
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@ -20,7 +20,7 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16.13.1
|
node-version: 16.13.1
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
- name: Install npm dependencies
|
- name: Install npm dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Build the production app
|
- name: Build the production app
|
||||||
@ -34,11 +34,25 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16.13.1
|
node-version: 16.13.1
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
- name: Install npm dependencies
|
- name: Install npm dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Run linter
|
- name: Run linter
|
||||||
run: npm run lint:report
|
run: npm run lint:report
|
||||||
|
prettier:
|
||||||
|
name: Prettier
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 16.13.1
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 16.13.1
|
||||||
|
cache: "npm"
|
||||||
|
- name: Install npm dependencies
|
||||||
|
run: npm ci
|
||||||
|
- name: Run prettier check
|
||||||
|
run: npm run format:report
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -48,7 +62,7 @@ jobs:
|
|||||||
uses: actions/setup-node@v2
|
uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16.13.1
|
node-version: 16.13.1
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
- name: Install npm dependencies
|
- name: Install npm dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
|
34
dist/vendor.bundle.js
vendored
34
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
2
dist/vendor.bundle.js.map
vendored
2
dist/vendor.bundle.js.map
vendored
File diff suppressed because one or more lines are too long
@ -347,7 +347,7 @@ Kills all scripts on the current server.
|
|||||||
ls
|
ls
|
||||||
^^
|
^^
|
||||||
|
|
||||||
$ ls [dir] [| grep pattern]
|
$ ls [dir] [--grep pattern]
|
||||||
|
|
||||||
Prints files and directories on the current server to the Terminal screen.
|
Prints files and directories on the current server to the Terminal screen.
|
||||||
|
|
||||||
@ -358,19 +358,21 @@ followed by the files (also in alphabetical order).
|
|||||||
The :code:`dir` optional parameter allows you to specify the directory for which to display
|
The :code:`dir` optional parameter allows you to specify the directory for which to display
|
||||||
files.
|
files.
|
||||||
|
|
||||||
The :code:`| grep pattern` optional parameter allows you to only display files and directories
|
The :code:`--grep pattern` optional parameter allows you to only display files and directories
|
||||||
with a certain pattern in their names.
|
with a certain pattern in their names.
|
||||||
|
|
||||||
|
The :code:`-l` optional parameter allows you to force each item onto a single line.
|
||||||
|
|
||||||
Examples::
|
Examples::
|
||||||
|
|
||||||
// List files/directories with the '.script' extension in the current directory
|
// List files/directories with the '.script' extension in the current directory
|
||||||
$ ls | grep .script
|
$ ls -l --grep .script
|
||||||
|
|
||||||
// List files/directories with the '.js' extension in the root directory
|
// List files/directories with the '.js' extension in the root directory
|
||||||
$ ls / | grep .js
|
$ ls / -l --grep .js
|
||||||
|
|
||||||
// List files/directories with the word 'purchase' in the name, in the :code:`scripts` directory
|
// List files/directories with the word 'purchase' in the name, in the :code:`scripts` directory
|
||||||
$ ls scripts | grep purchase
|
$ ls scripts -l --grep purchase
|
||||||
|
|
||||||
|
|
||||||
lscpu
|
lscpu
|
||||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -108,6 +108,7 @@
|
|||||||
"cy:run": "cypress run",
|
"cy:run": "cypress run",
|
||||||
"doc": "npx api-extractor run && npx api-documenter markdown && rm input/bitburner.api.json && rm -r input",
|
"doc": "npx api-extractor run && npx api-documenter markdown && rm input/bitburner.api.json && rm -r input",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
|
"format:report": "prettier -c .",
|
||||||
"start": "http-server -p 8000",
|
"start": "http-server -p 8000",
|
||||||
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
"start:dev": "webpack-dev-server --progress --env.devServer --mode development",
|
||||||
"start:dev-fast": "webpack-dev-server --progress --env.devServer --mode development --fast true",
|
"start:dev-fast": "webpack-dev-server --progress --env.devServer --mode development --fast true",
|
||||||
|
@ -58,7 +58,9 @@ function bitNodeFinishedState(): boolean {
|
|||||||
const wd = GetServer(SpecialServers.WorldDaemon);
|
const wd = GetServer(SpecialServers.WorldDaemon);
|
||||||
if (!(wd instanceof Server)) return false;
|
if (!(wd instanceof Server)) return false;
|
||||||
if (wd.backdoorInstalled) return true;
|
if (wd.backdoorInstalled) return true;
|
||||||
return Player.bladeburner !== null && Player.bladeburner.blackops.hasOwnProperty(BlackOperationNames.OperationDaedalus);
|
return (
|
||||||
|
Player.bladeburner !== null && Player.bladeburner.blackops.hasOwnProperty(BlackOperationNames.OperationDaedalus)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasAccessToSF(player: PlayerObject, bn: number): boolean {
|
function hasAccessToSF(player: PlayerObject, bn: number): boolean {
|
||||||
@ -564,7 +566,7 @@ export const achievements: IMap<Achievement> = {
|
|||||||
...achievementData["SLEEVE_8"],
|
...achievementData["SLEEVE_8"],
|
||||||
Icon: "SLEEVE8",
|
Icon: "SLEEVE8",
|
||||||
Visible: () => hasAccessToSF(Player, 10),
|
Visible: () => hasAccessToSF(Player, 10),
|
||||||
Condition: () => Player.sleeves.length === 8,
|
Condition: () => Player.sleeves.length === 8 && Player.sourceFileLvl(10) === 3,
|
||||||
},
|
},
|
||||||
INDECISIVE: {
|
INDECISIVE: {
|
||||||
...achievementData["INDECISIVE"],
|
...achievementData["INDECISIVE"],
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,6 @@ import Tooltip from "@mui/material/Tooltip";
|
|||||||
import { Settings } from "../../Settings/Settings";
|
import { Settings } from "../../Settings/Settings";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
|
||||||
|
|
||||||
const useStyles = makeStyles(() =>
|
const useStyles = makeStyles(() =>
|
||||||
createStyles({
|
createStyles({
|
||||||
portal: {
|
portal: {
|
||||||
@ -70,7 +69,7 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
|
|||||||
if (props.level === 2) {
|
if (props.level === 2) {
|
||||||
cssClass = classes.level2;
|
cssClass = classes.level2;
|
||||||
}
|
}
|
||||||
cssClass = `${classes.portal} ${cssClass}`
|
cssClass = `${classes.portal} ${cssClass}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -86,12 +85,10 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{Settings.DisableASCIIArt ? (
|
{Settings.DisableASCIIArt ? (
|
||||||
<Button
|
<Button onClick={() => setPortalOpen(true)} sx={{ m: 2 }} aria-description={bitNode.desc}>
|
||||||
onClick={() => setPortalOpen(true)}
|
<Typography>
|
||||||
sx={{ m: 2 }}
|
BitNode-{bitNode.number.toString()}: {bitNode.name}
|
||||||
aria-description={bitNode.desc}
|
</Typography>
|
||||||
>
|
|
||||||
<Typography>BitNode-{bitNode.number.toString()}: {bitNode.name}</Typography>
|
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<IconButton
|
<IconButton
|
||||||
@ -114,9 +111,7 @@ function BitNodePortal(props: IPortalProps): React.ReactElement {
|
|||||||
flume={props.flume}
|
flume={props.flume}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{Settings.DisableASCIIArt && (
|
{Settings.DisableASCIIArt && <br />}
|
||||||
<br/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -173,19 +168,29 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
if (Settings.DisableASCIIArt) {
|
if (Settings.DisableASCIIArt) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.values(BitNodes).filter((node) => {
|
{Object.values(BitNodes)
|
||||||
|
.filter((node) => {
|
||||||
console.log(node.desc);
|
console.log(node.desc);
|
||||||
return node.desc !== 'COMING SOON';
|
return node.desc !== "COMING SOON";
|
||||||
}).map((node) => {
|
})
|
||||||
|
.map((node) => {
|
||||||
return (
|
return (
|
||||||
<BitNodePortal key={node.number} n={node.number} level={nextSourceFileFlags[node.number]} enter={enter} flume={props.flume} destroyedBitNode={destroyed} />
|
<BitNodePortal
|
||||||
)
|
key={node.number}
|
||||||
|
n={node.number}
|
||||||
|
level={nextSourceFileFlags[node.number]}
|
||||||
|
enter={enter}
|
||||||
|
flume={props.flume}
|
||||||
|
destroyedBitNode={destroyed}
|
||||||
|
/>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<CinematicText lines={[
|
<CinematicText
|
||||||
|
lines={[
|
||||||
"> Many decades ago, a humanoid extraterrestrial species which we call the Enders descended on the Earth...violently",
|
"> Many decades ago, a humanoid extraterrestrial species which we call the Enders descended on the Earth...violently",
|
||||||
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
|
"> Our species fought back, but it was futile. The Enders had technology far beyond our own...",
|
||||||
"> Instead of killing every last one of us, the human race was enslaved...",
|
"> Instead of killing every last one of us, the human race was enslaved...",
|
||||||
@ -207,10 +212,11 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
"> Welcome to the Bitverse...",
|
"> Welcome to the Bitverse...",
|
||||||
"> ",
|
"> ",
|
||||||
"> (Enter a new BitNode using the image above)",
|
"> (Enter a new BitNode using the image above)",
|
||||||
]} />
|
]}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
} else {
|
}
|
||||||
return (
|
return (
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
<>
|
<>
|
||||||
@ -268,6 +274,4 @@ export function BitverseRoot(props: IProps): React.ReactElement {
|
|||||||
]} />
|
]} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
|
||||||
return <></>;
|
|
||||||
}
|
}
|
||||||
|
@ -318,9 +318,8 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (this.contracts.hasOwnProperty(name)) {
|
if (this.contracts.hasOwnProperty(name)) {
|
||||||
action.name = name;
|
action.name = name;
|
||||||
return action;
|
return action;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
case "operation":
|
case "operation":
|
||||||
case "operations":
|
case "operations":
|
||||||
case "op":
|
case "op":
|
||||||
@ -329,9 +328,8 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (this.operations.hasOwnProperty(name)) {
|
if (this.operations.hasOwnProperty(name)) {
|
||||||
action.name = name;
|
action.name = name;
|
||||||
return action;
|
return action;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
case "blackoperation":
|
case "blackoperation":
|
||||||
case "black operation":
|
case "black operation":
|
||||||
case "black operations":
|
case "black operations":
|
||||||
@ -343,9 +341,8 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (BlackOperations.hasOwnProperty(name)) {
|
if (BlackOperations.hasOwnProperty(name)) {
|
||||||
action.name = name;
|
action.name = name;
|
||||||
return action;
|
return action;
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
case "general":
|
case "general":
|
||||||
case "general action":
|
case "general action":
|
||||||
case "gen":
|
case "gen":
|
||||||
@ -1529,7 +1526,8 @@ export class Bladeburner implements IBladeburner {
|
|||||||
this.startAction(player, this.action);
|
this.startAction(player, this.action);
|
||||||
if (this.logging.general) {
|
if (this.logging.general) {
|
||||||
this.log(
|
this.log(
|
||||||
`Rested in Hyperbolic Regeneration Chamber. Restored ${BladeburnerConstants.HrcHpGain
|
`Rested in Hyperbolic Regeneration Chamber. Restored ${
|
||||||
|
BladeburnerConstants.HrcHpGain
|
||||||
} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`,
|
} HP and gained ${numeralWrapper.formatStamina(staminaGain)} stamina`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1578,7 +1576,9 @@ export class Bladeburner implements IBladeburner {
|
|||||||
if (factionExists(bladeburnersFactionName)) {
|
if (factionExists(bladeburnersFactionName)) {
|
||||||
const bladeburnerFac = Factions[bladeburnersFactionName];
|
const bladeburnerFac = Factions[bladeburnersFactionName];
|
||||||
if (!(bladeburnerFac instanceof Faction)) {
|
if (!(bladeburnerFac instanceof Faction)) {
|
||||||
throw new Error(`Could not properly get ${FactionNames.Bladeburners} Faction object in ${FactionNames.Bladeburners} UI Overview Faction button`);
|
throw new Error(
|
||||||
|
`Could not properly get ${FactionNames.Bladeburners} Faction object in ${FactionNames.Bladeburners} UI Overview Faction button`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (bladeburnerFac.isMember) {
|
if (bladeburnerFac.isMember) {
|
||||||
const favorBonus = 1 + bladeburnerFac.favor / 100;
|
const favorBonus = 1 + bladeburnerFac.favor / 100;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useRef, useEffect } from "react";
|
import React, { useState, useRef, useEffect } from "react";
|
||||||
import { IBladeburner } from "../IBladeburner";
|
import { IBladeburner } from "../IBladeburner";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
import { IPlayer } from "../../PersonObjects/IPlayer";
|
import { IPlayer } from "../../PersonObjects/IPlayer";
|
||||||
import Paper from "@mui/material/Paper";
|
import Paper from "@mui/material/Paper";
|
||||||
@ -76,7 +77,7 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) {
|
if (event.key === KEY.ENTER) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (command.length > 0) {
|
if (command.length > 0) {
|
||||||
props.bladeburner.postToConsole("> " + command);
|
props.bladeburner.postToConsole("> " + command);
|
||||||
@ -88,7 +89,7 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
|
|
||||||
const consoleHistory = props.bladeburner.consoleHistory;
|
const consoleHistory = props.bladeburner.consoleHistory;
|
||||||
|
|
||||||
if (event.keyCode === 38) {
|
if (event.key === KEY.S) {
|
||||||
// up
|
// up
|
||||||
let i = consoleHistoryIndex;
|
let i = consoleHistoryIndex;
|
||||||
const len = consoleHistory.length;
|
const len = consoleHistory.length;
|
||||||
@ -108,7 +109,7 @@ export function Console(props: IProps): React.ReactElement {
|
|||||||
setCommand(prevCommand);
|
setCommand(prevCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === 40) {
|
if (event.key === KEY.DOWNARROW) {
|
||||||
const i = consoleHistoryIndex;
|
const i = consoleHistoryIndex;
|
||||||
const len = consoleHistory.length;
|
const len = consoleHistory.length;
|
||||||
|
|
||||||
|
@ -64,6 +64,9 @@ export function UnlockUpgrade(corporation: ICorporation, upgrade: CorporationUnl
|
|||||||
if (corporation.funds < upgrade[1]) {
|
if (corporation.funds < upgrade[1]) {
|
||||||
throw new Error("Insufficient funds");
|
throw new Error("Insufficient funds");
|
||||||
}
|
}
|
||||||
|
if(corporation.unlockUpgrades[upgrade[0]] === 1){
|
||||||
|
throw new Error(`You have already unlocked the ${upgrade[2]} upgrade!`);
|
||||||
|
}
|
||||||
corporation.unlock(upgrade);
|
corporation.unlock(upgrade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import Button from "@mui/material/Button";
|
|||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import { BuyBackShares } from '../Actions';
|
import { BuyBackShares } from '../Actions';
|
||||||
import { dialogBoxCreate } from '../../ui/React/DialogBox';
|
import { dialogBoxCreate } from '../../ui/React/DialogBox';
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -69,7 +70,7 @@ export function BuybackSharesModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) buy();
|
if (event.key === KEY.ENTER) buy();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -11,6 +11,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
setDivisionName: (name: string) => void;
|
setDivisionName: (name: string) => void;
|
||||||
@ -53,7 +54,7 @@ export function ExpandIndustryTab(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) newIndustry();
|
if (event.key === KEY.ENTER) newIndustry();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onIndustryChange(event: SelectChangeEvent<string>): void {
|
function onIndustryChange(event: SelectChangeEvent<string>): void {
|
||||||
|
@ -7,6 +7,7 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -45,7 +46,7 @@ export function GoPublicModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) goPublic();
|
if (event.key === KEY.ENTER) goPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -7,6 +7,7 @@ import { useCorporation } from "./Context";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -32,7 +33,7 @@ export function IssueDividendsModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) issueDividends();
|
if (event.key === KEY.ENTER) issueDividends();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -8,6 +8,7 @@ import { useCorporation } from "./Context";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IEffectTextProps {
|
interface IEffectTextProps {
|
||||||
shares: number | null;
|
shares: number | null;
|
||||||
@ -93,7 +94,7 @@ export function IssueNewSharesModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) issueNewShares();
|
if (event.key === KEY.ENTER) issueNewShares();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -5,6 +5,7 @@ import { Modal } from "../../ui/React/Modal";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -25,7 +26,7 @@ export function LimitProductProductionModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) limitProductProduction();
|
if (event.key === KEY.ENTER) limitProductProduction();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -9,6 +9,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -165,7 +166,7 @@ export function MakeProductModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) makeProduct();
|
if (event.key === KEY.ENTER) makeProduct();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,7 @@ import { useCorporation, useDivision } from "./Context";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IBulkPurchaseTextProps {
|
interface IBulkPurchaseTextProps {
|
||||||
warehouse: Warehouse;
|
warehouse: Warehouse;
|
||||||
@ -68,7 +69,7 @@ function BulkPurchaseSection(props: IBPProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) bulkPurchase();
|
if (event.key === KEY.ENTER) bulkPurchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
@ -123,7 +124,7 @@ export function PurchaseMaterialModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) purchaseMaterial();
|
if (event.key === KEY.ENTER) purchaseMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -6,6 +6,7 @@ import { Modal } from "../../ui/React/Modal";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
function initialPrice(mat: Material): string {
|
function initialPrice(mat: Material): string {
|
||||||
let val = mat.sCost ? mat.sCost + "" : "";
|
let val = mat.sCost ? mat.sCost + "" : "";
|
||||||
@ -46,7 +47,7 @@ export function SellMaterialModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) sellMaterial();
|
if (event.key === KEY.ENTER) sellMaterial();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -9,6 +9,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||||
import Switch from "@mui/material/Switch";
|
import Switch from "@mui/material/Switch";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
function initialPrice(product: Product): string {
|
function initialPrice(product: Product): string {
|
||||||
let val = product.sCost ? product.sCost + "" : "";
|
let val = product.sCost ? product.sCost + "" : "";
|
||||||
@ -58,7 +59,7 @@ export function SellProductModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) sellProduct();
|
if (event.key === KEY.ENTER) sellProduct();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,7 @@ import TextField from "@mui/material/TextField";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import { Money } from "../../ui/React/Money";
|
import { Money } from "../../ui/React/Money";
|
||||||
import { SellShares } from "../Actions";
|
import { SellShares } from "../Actions";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@ -68,7 +69,7 @@ export function SellSharesModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) sell();
|
if (event.key === KEY.ENTER) sell();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,7 @@ import Typography from "@mui/material/Typography";
|
|||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -57,7 +58,7 @@ export function ThrowPartyModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) throwParty();
|
if (event.key === KEY.ENTER) throwParty();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -29,67 +29,51 @@ export function Effect(tpe: FragmentType): string {
|
|||||||
switch (tpe) {
|
switch (tpe) {
|
||||||
case FragmentType.HackingChance: {
|
case FragmentType.HackingChance: {
|
||||||
return "+x% hack() success chance";
|
return "+x% hack() success chance";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.HackingSpeed: {
|
case FragmentType.HackingSpeed: {
|
||||||
return "+x% faster hack(), grow(), and weaken()";
|
return "+x% faster hack(), grow(), and weaken()";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.HackingMoney: {
|
case FragmentType.HackingMoney: {
|
||||||
return "+x% hack() power";
|
return "+x% hack() power";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.HackingGrow: {
|
case FragmentType.HackingGrow: {
|
||||||
return "+x% grow() power";
|
return "+x% grow() power";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Hacking: {
|
case FragmentType.Hacking: {
|
||||||
return "+x% hacking skill";
|
return "+x% hacking skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Strength: {
|
case FragmentType.Strength: {
|
||||||
return "+x% strength skill";
|
return "+x% strength skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Defense: {
|
case FragmentType.Defense: {
|
||||||
return "+x% defense skill";
|
return "+x% defense skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Dexterity: {
|
case FragmentType.Dexterity: {
|
||||||
return "+x% dexterity skill";
|
return "+x% dexterity skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Agility: {
|
case FragmentType.Agility: {
|
||||||
return "+x% agility skill";
|
return "+x% agility skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Charisma: {
|
case FragmentType.Charisma: {
|
||||||
return "+x% charisma skill";
|
return "+x% charisma skill";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.HacknetMoney: {
|
case FragmentType.HacknetMoney: {
|
||||||
return "+x% hacknet production";
|
return "+x% hacknet production";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.HacknetCost: {
|
case FragmentType.HacknetCost: {
|
||||||
return "x% cheaper hacknet cost";
|
return "x% cheaper hacknet cost";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Rep: {
|
case FragmentType.Rep: {
|
||||||
return "+x% reputation from factions and companies";
|
return "+x% reputation from factions and companies";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.WorkMoney: {
|
case FragmentType.WorkMoney: {
|
||||||
return "+x% work money";
|
return "+x% work money";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Crime: {
|
case FragmentType.Crime: {
|
||||||
return "+x% crime money";
|
return "+x% crime money";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FragmentType.Bladeburner: {
|
case FragmentType.Bladeburner: {
|
||||||
return "+x% all bladeburner stats";
|
return "+x% all bladeburner stats";
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Error("Calling effect for fragment type that doesn't have an effect " + tpe);
|
throw new Error("Calling effect for fragment type that doesn't have an effect " + tpe);
|
||||||
|
@ -6,6 +6,7 @@ import { Modal } from "../../ui/React/Modal";
|
|||||||
import { use } from "../../ui/Context";
|
import { use } from "../../ui/Context";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
import { FactionNames } from "../data/FactionNames";
|
import { FactionNames } from "../data/FactionNames";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
@ -38,7 +39,7 @@ export function CreateGangModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyUp(event: React.KeyboardEvent): void {
|
function onKeyUp(event: React.KeyboardEvent): void {
|
||||||
if (event.keyCode === 13) createGang();
|
if (event.key === KEY.ENTER) createGang();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -7,12 +7,7 @@ import { useGang } from "./Context";
|
|||||||
|
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Tooltip from "@mui/material/Tooltip";
|
import Tooltip from "@mui/material/Tooltip";
|
||||||
import {
|
import { Table, TableBody, TableCell, TableRow } from "@mui/material";
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableRow,
|
|
||||||
} from "@mui/material";
|
|
||||||
|
|
||||||
import { numeralWrapper } from "../../ui/numeralFormat";
|
import { numeralWrapper } from "../../ui/numeralFormat";
|
||||||
import { GangMember } from "../GangMember";
|
import { GangMember } from "../GangMember";
|
||||||
@ -37,8 +32,6 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
|||||||
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
cha: props.member.calculateAscensionMult(props.member.cha_asc_points),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const gang = useGang();
|
const gang = useGang();
|
||||||
const data = [
|
const data = [
|
||||||
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
[`Money:`, <MoneyRate money={5 * props.member.calculateMoneyGain(gang)} />],
|
||||||
@ -78,14 +71,38 @@ export function GangMemberStats(props: IProps): React.ReactElement {
|
|||||||
</Typography>
|
</Typography>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Table sx={{ display: 'table', mb: 1, width: '100%' }}>
|
<Table sx={{ display: "table", mb: 1, width: "100%" }}>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
<StatsRow name="Hacking" color={Settings.theme.hack} data={{ level: props.member.hack, exp: props.member.hack_exp }} />
|
<StatsRow
|
||||||
<StatsRow name="Strength" color={Settings.theme.combat} data={{ level: props.member.str, exp: props.member.str_exp }} />
|
name="Hacking"
|
||||||
<StatsRow name="Defense" color={Settings.theme.combat} data={{ level: props.member.def, exp: props.member.def_exp }} />
|
color={Settings.theme.hack}
|
||||||
<StatsRow name="Dexterity" color={Settings.theme.combat} data={{ level: props.member.dex, exp: props.member.dex_exp }} />
|
data={{ level: props.member.hack, exp: props.member.hack_exp }}
|
||||||
<StatsRow name="Agility" color={Settings.theme.combat} data={{ level: props.member.agi, exp: props.member.agi_exp }} />
|
/>
|
||||||
<StatsRow name="Charisma" color={Settings.theme.cha} data={{ level: props.member.cha, exp: props.member.cha_exp }} />
|
<StatsRow
|
||||||
|
name="Strength"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: props.member.str, exp: props.member.str_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Defense"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: props.member.def, exp: props.member.def_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Dexterity"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: props.member.dex, exp: props.member.dex_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Agility"
|
||||||
|
color={Settings.theme.combat}
|
||||||
|
data={{ level: props.member.agi, exp: props.member.agi_exp }}
|
||||||
|
/>
|
||||||
|
<StatsRow
|
||||||
|
name="Charisma"
|
||||||
|
color={Settings.theme.cha}
|
||||||
|
data={{ level: props.member.cha, exp: props.member.cha_exp }}
|
||||||
|
/>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell classes={{ root: classes.cellNone }}>
|
<TableCell classes={{ root: classes.cellNone }}>
|
||||||
<br />
|
<br />
|
||||||
|
@ -8,6 +8,7 @@ import { useGang } from "./Context";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IRecruitPopupProps {
|
interface IRecruitPopupProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -34,7 +35,7 @@ export function RecruitModal(props: IRecruitPopupProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) recruit();
|
if (event.key === KEY.ENTER) recruit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -21,6 +21,13 @@ export function TaskSelector(props: IProps): React.ReactElement {
|
|||||||
const gang = useGang();
|
const gang = useGang();
|
||||||
const [currentTask, setCurrentTask] = useState(props.member.task);
|
const [currentTask, setCurrentTask] = useState(props.member.task);
|
||||||
|
|
||||||
|
const contextMember = gang.members.find(member => member.name == props.member.name)
|
||||||
|
if (contextMember &&
|
||||||
|
contextMember.task != currentTask
|
||||||
|
) {
|
||||||
|
setCurrentTask(contextMember.task)
|
||||||
|
}
|
||||||
|
|
||||||
function onChange(event: SelectChangeEvent<string>): void {
|
function onChange(event: SelectChangeEvent<string>): void {
|
||||||
const task = event.target.value;
|
const task = event.target.value;
|
||||||
props.member.assignToTask(task);
|
props.member.assignToTask(task);
|
||||||
|
@ -10,6 +10,7 @@ import { use } from "../../ui/Context";
|
|||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import Button from "@mui/material/Button";
|
import Button from "@mui/material/Button";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@ -29,7 +30,7 @@ export function PurchaseServerModal(props: IProps): React.ReactElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
function onKeyUp(event: React.KeyboardEvent<HTMLInputElement>): void {
|
||||||
if (event.keyCode === 13) tryToPurchaseServer();
|
if (event.key === KEY.ENTER) tryToPurchaseServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
function onChange(event: React.ChangeEvent<HTMLInputElement>): void {
|
||||||
|
@ -232,6 +232,8 @@ export const RamCosts: IMap<any> = {
|
|||||||
connect: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
connect: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||||
manualHack: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
manualHack: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||||
installBackdoor: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
installBackdoor: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost),
|
||||||
|
getDarkwebProgramCost: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
||||||
|
getDarkwebPrograms: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
||||||
getStats: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
getStats: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
||||||
getCharacterInformation: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
getCharacterInformation: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
||||||
hospitalize: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
hospitalize: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost / 4),
|
||||||
|
@ -144,7 +144,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
const safeGetServer = function (hostname: string, callingFnName: string): BaseServer {
|
const safeGetServer = function (hostname: string, callingFnName: string): BaseServer {
|
||||||
const server = GetServer(hostname);
|
const server = GetServer(hostname);
|
||||||
if (server == null) {
|
if (server == null) {
|
||||||
throw makeRuntimeErrorMsg(callingFnName, `Invalid hostname or IP: ${hostname}`);
|
throw makeRuntimeErrorMsg(callingFnName, `Invalid hostname: ${hostname}`);
|
||||||
}
|
}
|
||||||
return server;
|
return server;
|
||||||
};
|
};
|
||||||
@ -539,6 +539,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
|
|||||||
|
|
||||||
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
const percentHacked = calculatePercentMoneyHacked(server, Player);
|
||||||
|
|
||||||
|
if (percentHacked === 0 || server.moneyAvailable === 0) {
|
||||||
|
return 0; // To prevent returning infinity below
|
||||||
|
}
|
||||||
|
|
||||||
return hackAmount / Math.floor(server.moneyAvailable * percentHacked);
|
return hackAmount / Math.floor(server.moneyAvailable * percentHacked);
|
||||||
},
|
},
|
||||||
hackAnalyze: function (hostname: any): any {
|
hackAnalyze: function (hostname: any): any {
|
||||||
|
@ -291,7 +291,7 @@ export function NetscriptCorporation(
|
|||||||
lastCycleExpenses: division.lastCycleExpenses,
|
lastCycleExpenses: division.lastCycleExpenses,
|
||||||
thisCycleRevenue: division.thisCycleRevenue,
|
thisCycleRevenue: division.thisCycleRevenue,
|
||||||
thisCycleExpenses: division.thisCycleExpenses,
|
thisCycleExpenses: division.thisCycleExpenses,
|
||||||
upgrades: division.upgrades,
|
upgrades: division.upgrades.slice(),
|
||||||
cities: cities,
|
cities: cities,
|
||||||
products: division.products === undefined ? [] : Object.keys(division.products),
|
products: division.products === undefined ? [] : Object.keys(division.products),
|
||||||
};
|
};
|
||||||
@ -846,5 +846,9 @@ export function NetscriptCorporation(
|
|||||||
const amountShares = helper.number("bribe", "amountShares", aamountShares);
|
const amountShares = helper.number("bribe", "amountShares", aamountShares);
|
||||||
return bribe(factionName, amountCash, amountShares);
|
return bribe(factionName, amountCash, amountShares);
|
||||||
},
|
},
|
||||||
|
getBonusTime: function (): number {
|
||||||
|
checkAccess("getBonusTime");
|
||||||
|
return Math.round(getCorporation().storedCycles / 5) * 1000;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import { IPlayer } from "../PersonObjects/IPlayer";
|
|||||||
import { Exploit } from "../Exploits/Exploit";
|
import { Exploit } from "../Exploits/Exploit";
|
||||||
import * as bcrypt from "bcryptjs";
|
import * as bcrypt from "bcryptjs";
|
||||||
import { INetscriptHelper } from "./INetscriptHelper";
|
import { INetscriptHelper } from "./INetscriptHelper";
|
||||||
import { Augmentations } from "../Augmentation/Augmentations";
|
|
||||||
|
|
||||||
export interface INetscriptExtra {
|
export interface INetscriptExtra {
|
||||||
heart: {
|
heart: {
|
||||||
|
@ -43,6 +43,7 @@ import { calculateHackingTime } from "../Hacking";
|
|||||||
import { Server } from "../Server/Server";
|
import { Server } from "../Server/Server";
|
||||||
import { netscriptCanHack } from "../Hacking/netscriptCanHack";
|
import { netscriptCanHack } from "../Hacking/netscriptCanHack";
|
||||||
import { FactionNames } from "../Faction/data/FactionNames";
|
import { FactionNames } from "../Faction/data/FactionNames";
|
||||||
|
import { FactionInfos } from "../Faction/FactionInfo";
|
||||||
|
|
||||||
export function NetscriptSingularity(
|
export function NetscriptSingularity(
|
||||||
player: IPlayer,
|
player: IPlayer,
|
||||||
@ -370,7 +371,8 @@ export function NetscriptSingularity(
|
|||||||
if (player.city != CityName.Aevum) {
|
if (player.city != CityName.Aevum) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"gymWorkout",
|
"gymWorkout",
|
||||||
() => `You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`,
|
() =>
|
||||||
|
`You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`,
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -382,7 +384,8 @@ export function NetscriptSingularity(
|
|||||||
if (player.city != CityName.Aevum) {
|
if (player.city != CityName.Aevum) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"gymWorkout",
|
"gymWorkout",
|
||||||
() => `You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`,
|
() =>
|
||||||
|
`You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`,
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -394,7 +397,8 @@ export function NetscriptSingularity(
|
|||||||
if (player.city != CityName.Sector12) {
|
if (player.city != CityName.Sector12) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"gymWorkout",
|
"gymWorkout",
|
||||||
() => `You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`,
|
() =>
|
||||||
|
`You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`,
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -406,7 +410,8 @@ export function NetscriptSingularity(
|
|||||||
if (player.city != CityName.Sector12) {
|
if (player.city != CityName.Sector12) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"gymWorkout",
|
"gymWorkout",
|
||||||
() => `You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`,
|
() =>
|
||||||
|
`You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`,
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -418,7 +423,8 @@ export function NetscriptSingularity(
|
|||||||
if (player.city != CityName.Volhaven) {
|
if (player.city != CityName.Volhaven) {
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
"gymWorkout",
|
"gymWorkout",
|
||||||
() => `You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`,
|
() =>
|
||||||
|
`You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`,
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -476,7 +482,7 @@ export function NetscriptSingularity(
|
|||||||
case CityName.Volhaven:
|
case CityName.Volhaven:
|
||||||
if (player.money < CONSTANTS.TravelCost) {
|
if (player.money < CONSTANTS.TravelCost) {
|
||||||
workerScript.log("travelToCity", () => "Not enough money to travel.");
|
workerScript.log("travelToCity", () => "Not enough money to travel.");
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
player.loseMoney(CONSTANTS.TravelCost, "other");
|
player.loseMoney(CONSTANTS.TravelCost, "other");
|
||||||
player.city = cityname;
|
player.city = cityname;
|
||||||
@ -1033,93 +1039,12 @@ export function NetscriptSingularity(
|
|||||||
|
|
||||||
const fac = Factions[name];
|
const fac = Factions[name];
|
||||||
// Arrays listing factions that allow each time of work
|
// Arrays listing factions that allow each time of work
|
||||||
const hackAvailable = [
|
|
||||||
FactionNames.Illuminati as string,
|
|
||||||
FactionNames.Daedalus as string,
|
|
||||||
FactionNames.TheCovenant as string,
|
|
||||||
FactionNames.ECorp as string,
|
|
||||||
FactionNames.MegaCorp as string,
|
|
||||||
FactionNames.BachmanAssociates as string,
|
|
||||||
FactionNames.Bladeburners as string,
|
|
||||||
FactionNames.NWO as string,
|
|
||||||
FactionNames.ClarkeIncorporated as string,
|
|
||||||
FactionNames.OmniTekIncorporated as string,
|
|
||||||
FactionNames.FourSigma as string,
|
|
||||||
FactionNames.KuaiGongInternational as string,
|
|
||||||
FactionNames.FulcrumSecretTechnologies as string,
|
|
||||||
FactionNames.BitRunners as string,
|
|
||||||
FactionNames.TheBlackHand as string,
|
|
||||||
FactionNames.NiteSec as string,
|
|
||||||
FactionNames.Chongqing as string,
|
|
||||||
FactionNames.Sector12 as string,
|
|
||||||
FactionNames.NewTokyo as string,
|
|
||||||
FactionNames.Aevum as string,
|
|
||||||
FactionNames.Ishima as string,
|
|
||||||
FactionNames.Volhaven as string,
|
|
||||||
FactionNames.SpeakersForTheDead as string,
|
|
||||||
FactionNames.TheDarkArmy as string,
|
|
||||||
FactionNames.TheSyndicate as string,
|
|
||||||
FactionNames.Silhouette as string,
|
|
||||||
FactionNames.Netburners as string,
|
|
||||||
FactionNames.TianDiHui as string,
|
|
||||||
FactionNames.CyberSec as string,
|
|
||||||
];
|
|
||||||
const fdWkAvailable = [
|
|
||||||
FactionNames.Illuminati as string,
|
|
||||||
FactionNames.Daedalus as string,
|
|
||||||
FactionNames.TheCovenant as string,
|
|
||||||
FactionNames.ECorp as string,
|
|
||||||
FactionNames.MegaCorp as string,
|
|
||||||
FactionNames.BachmanAssociates as string,
|
|
||||||
FactionNames.Bladeburners as string,
|
|
||||||
FactionNames.NWO as string,
|
|
||||||
FactionNames.ClarkeIncorporated as string,
|
|
||||||
FactionNames.OmniTekIncorporated as string,
|
|
||||||
FactionNames.FourSigma as string,
|
|
||||||
FactionNames.KuaiGongInternational as string,
|
|
||||||
FactionNames.TheBlackHand as string,
|
|
||||||
FactionNames.Chongqing as string,
|
|
||||||
FactionNames.Sector12 as string,
|
|
||||||
FactionNames.NewTokyo as string,
|
|
||||||
FactionNames.Aevum as string,
|
|
||||||
FactionNames.Ishima as string,
|
|
||||||
FactionNames.Volhaven as string,
|
|
||||||
FactionNames.SpeakersForTheDead as string,
|
|
||||||
FactionNames.TheDarkArmy as string,
|
|
||||||
FactionNames.TheSyndicate as string,
|
|
||||||
FactionNames.Silhouette as string,
|
|
||||||
FactionNames.Tetrads as string,
|
|
||||||
FactionNames.SlumSnakes as string,
|
|
||||||
];
|
|
||||||
const scWkAvailable = [
|
|
||||||
FactionNames.ECorp as string,
|
|
||||||
FactionNames.MegaCorp as string,
|
|
||||||
FactionNames.BachmanAssociates as string,
|
|
||||||
FactionNames.Bladeburners as string,
|
|
||||||
FactionNames.NWO as string,
|
|
||||||
FactionNames.ClarkeIncorporated as string,
|
|
||||||
FactionNames.OmniTekIncorporated as string,
|
|
||||||
FactionNames.FourSigma as string,
|
|
||||||
FactionNames.KuaiGongInternational as string,
|
|
||||||
FactionNames.FulcrumSecretTechnologies as string,
|
|
||||||
FactionNames.Chongqing as string,
|
|
||||||
FactionNames.Sector12 as string,
|
|
||||||
FactionNames.NewTokyo as string,
|
|
||||||
FactionNames.Aevum as string,
|
|
||||||
FactionNames.Ishima as string,
|
|
||||||
FactionNames.Volhaven as string,
|
|
||||||
FactionNames.SpeakersForTheDead as string,
|
|
||||||
FactionNames.TheSyndicate as string,
|
|
||||||
FactionNames.Tetrads as string,
|
|
||||||
FactionNames.SlumSnakes as string,
|
|
||||||
FactionNames.TianDiHui as string,
|
|
||||||
];
|
|
||||||
|
|
||||||
switch (type.toLowerCase()) {
|
switch (type.toLowerCase()) {
|
||||||
case "hacking":
|
case "hacking":
|
||||||
case "hacking contracts":
|
case "hacking contracts":
|
||||||
case "hackingcontracts":
|
case "hackingcontracts":
|
||||||
if (!hackAvailable.includes(fac.name)) {
|
if (!FactionInfos[fac.name].offerHackingWork) {
|
||||||
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with hacking contracts.`);
|
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with hacking contracts.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1136,7 +1061,7 @@ export function NetscriptSingularity(
|
|||||||
case "field":
|
case "field":
|
||||||
case "fieldwork":
|
case "fieldwork":
|
||||||
case "field work":
|
case "field work":
|
||||||
if (!fdWkAvailable.includes(fac.name)) {
|
if (!FactionInfos[fac.name].offerFieldWork) {
|
||||||
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with field missions.`);
|
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with field missions.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1153,7 +1078,7 @@ export function NetscriptSingularity(
|
|||||||
case "security":
|
case "security":
|
||||||
case "securitywork":
|
case "securitywork":
|
||||||
case "security work":
|
case "security work":
|
||||||
if (!scWkAvailable.includes(fac.name)) {
|
if (!FactionInfos[fac.name].offerSecurityWork) {
|
||||||
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with security work.`);
|
workerScript.log("workForFaction", () => `Faction '${fac.name}' do not need help with security work.`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1325,5 +1250,49 @@ export function NetscriptSingularity(
|
|||||||
|
|
||||||
return Object.assign({}, crime);
|
return Object.assign({}, crime);
|
||||||
},
|
},
|
||||||
|
getDarkwebPrograms: function (): string[] {
|
||||||
|
helper.updateDynamicRam("getDarkwebPrograms", getRamCost(player, "getDarkwebPrograms"));
|
||||||
|
helper.checkSingularityAccess("getDarkwebPrograms");
|
||||||
|
|
||||||
|
// If we don't have Tor, log it and return [] (empty list)
|
||||||
|
if (!player.hasTorRouter()) {
|
||||||
|
workerScript.log("getDarkwebPrograms", () => "You do not have the TOR router.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.values(DarkWebItems).map((p) => p.program);
|
||||||
|
},
|
||||||
|
getDarkwebProgramCost: function (programName: any): any {
|
||||||
|
helper.updateDynamicRam("getDarkwebProgramCost", getRamCost(player, "getDarkwebProgramCost"));
|
||||||
|
helper.checkSingularityAccess("getDarkwebProgramCost");
|
||||||
|
|
||||||
|
// If we don't have Tor, log it and return -1
|
||||||
|
if (!player.hasTorRouter()) {
|
||||||
|
workerScript.log("getDarkwebProgramCost", () => "You do not have the TOR router.");
|
||||||
|
// returning -1 rather than throwing an error to be consistent with purchaseProgram
|
||||||
|
// which returns false if tor has
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
programName = programName.toLowerCase();
|
||||||
|
const item = Object.values(DarkWebItems).find((i) => i.program.toLowerCase() === programName);
|
||||||
|
|
||||||
|
// If the program doesn't exist, throw an error. The reasoning here is that the 99% case is that
|
||||||
|
// the player will be using this in automation scripts, and if they're asking for a program that
|
||||||
|
// doesn't exist, it's the first time they've run the script. So throw an error to let them know
|
||||||
|
// that they need to fix it.
|
||||||
|
if (item == null) {
|
||||||
|
throw helper.makeRuntimeErrorMsg(
|
||||||
|
"getDarkwebProgramCost",
|
||||||
|
`No such exploit ('${programName}') found on the darkweb! ` +
|
||||||
|
`\nThis function is not case-sensitive. Did you perhaps forget .exe at the end?`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.hasProgram(item.program)) {
|
||||||
|
workerScript.log("getDarkwebProgramCost", () => `You already have the '${item.program}' program`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return item.price;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,20 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getSleeveStats = function (sleeveNumber: any): any {
|
||||||
|
const sl = player.sleeves[sleeveNumber];
|
||||||
|
return {
|
||||||
|
shock: 100 - sl.shock,
|
||||||
|
sync: sl.sync,
|
||||||
|
hacking: sl.hacking,
|
||||||
|
strength: sl.strength,
|
||||||
|
defense: sl.defense,
|
||||||
|
dexterity: sl.dexterity,
|
||||||
|
agility: sl.agility,
|
||||||
|
charisma: sl.charisma,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getNumSleeves: function (): number {
|
getNumSleeves: function (): number {
|
||||||
helper.updateDynamicRam("getNumSleeves", getRamCost(player, "sleeve", "getNumSleeves"));
|
helper.updateDynamicRam("getNumSleeves", getRamCost(player, "sleeve", "getNumSleeves"));
|
||||||
@ -150,18 +164,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
helper.updateDynamicRam("getSleeveStats", getRamCost(player, "sleeve", "getSleeveStats"));
|
helper.updateDynamicRam("getSleeveStats", getRamCost(player, "sleeve", "getSleeveStats"));
|
||||||
checkSleeveAPIAccess("getSleeveStats");
|
checkSleeveAPIAccess("getSleeveStats");
|
||||||
checkSleeveNumber("getSleeveStats", sleeveNumber);
|
checkSleeveNumber("getSleeveStats", sleeveNumber);
|
||||||
|
return getSleeveStats(sleeveNumber)
|
||||||
const sl = player.sleeves[sleeveNumber];
|
|
||||||
return {
|
|
||||||
shock: 100 - sl.shock,
|
|
||||||
sync: sl.sync,
|
|
||||||
hacking: sl.hacking,
|
|
||||||
strength: sl.strength,
|
|
||||||
defense: sl.defense,
|
|
||||||
dexterity: sl.dexterity,
|
|
||||||
agility: sl.agility,
|
|
||||||
charisma: sl.charisma,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
getTask: function (asleeveNumber: any = 0): {
|
getTask: function (asleeveNumber: any = 0): {
|
||||||
task: string;
|
task: string;
|
||||||
@ -289,7 +292,7 @@ export function NetscriptSleeve(player: IPlayer, workerScript: WorkerScript, hel
|
|||||||
checkSleeveAPIAccess("purchaseSleeveAug");
|
checkSleeveAPIAccess("purchaseSleeveAug");
|
||||||
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
|
checkSleeveNumber("purchaseSleeveAug", sleeveNumber);
|
||||||
|
|
||||||
if (player.sleeves[sleeveNumber].shock > 0){
|
if (getSleeveStats(sleeveNumber).shock > 0) {
|
||||||
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
throw helper.makeRuntimeErrorMsg("sleeve.purchaseSleeveAug", `Sleeve shock too high: Sleeve ${sleeveNumber}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export function NetscriptUserInterface(
|
|||||||
|
|
||||||
setTheme: function (newTheme: UserInterfaceTheme): void {
|
setTheme: function (newTheme: UserInterfaceTheme): void {
|
||||||
helper.updateDynamicRam("setTheme", getRamCost(player, "ui", "setTheme"));
|
helper.updateDynamicRam("setTheme", getRamCost(player, "ui", "setTheme"));
|
||||||
const hex = /^(#)((?:[A-Fa-f0-9]{3}){1,2})$/;
|
const hex = /^(#)((?:[A-Fa-f0-9]{2}){3,4}|(?:[A-Fa-f0-9]{3}))$/;
|
||||||
const currentTheme = {...Settings.theme}
|
const currentTheme = {...Settings.theme}
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
for (const key of Object.keys(newTheme)) {
|
for (const key of Object.keys(newTheme)) {
|
||||||
|
@ -178,7 +178,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
const entry = ns[name];
|
const entry = ns[name];
|
||||||
if (typeof entry === "function") {
|
if (typeof entry === "function") {
|
||||||
//Async functions need to be wrapped. See JS-Interpreter documentation
|
//Async functions need to be wrapped. See JS-Interpreter documentation
|
||||||
if (["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "scp", "write", "share"].includes(name)) {
|
if (["hack", "grow", "weaken", "sleep", "prompt", "manualHack", "scp", "write", "share", "wget"].includes(name)) {
|
||||||
const tempWrapper = function (...args: any[]): void {
|
const tempWrapper = function (...args: any[]): void {
|
||||||
const fnArgs = [];
|
const fnArgs = [];
|
||||||
|
|
||||||
@ -199,7 +199,21 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
|
|||||||
})
|
})
|
||||||
.catch(function (err: any) {
|
.catch(function (err: any) {
|
||||||
// workerscript is when you cancel a delay
|
// workerscript is when you cancel a delay
|
||||||
if (!(err instanceof WorkerScript)) console.error(err);
|
if (!(err instanceof WorkerScript)) {
|
||||||
|
console.error(err);
|
||||||
|
const errorTextArray = err.split("|DELIMITER|");
|
||||||
|
const hostname = errorTextArray[1];
|
||||||
|
const scriptName = errorTextArray[2];
|
||||||
|
const errorMsg = errorTextArray[3];
|
||||||
|
let msg = `${scriptName}@${hostname}<br>`;
|
||||||
|
msg += "<br>";
|
||||||
|
msg += errorMsg;
|
||||||
|
dialogBoxCreate(msg);
|
||||||
|
workerScript.env.stopFlag = true;
|
||||||
|
workerScript.running = false;
|
||||||
|
killWorkerScript(workerScript);
|
||||||
|
return Promise.resolve(workerScript);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
|
int.setProperty(scope, name, int.createAsyncFunction(tempWrapper));
|
||||||
@ -725,7 +739,7 @@ export function runScriptFromScript(
|
|||||||
`Cannot run script '${scriptname}' (t=${threads}) on '${server.hostname}' because there is not enough available RAM!`,
|
`Cannot run script '${scriptname}' (t=${threads}) on '${server.hostname}' because there is not enough available RAM!`,
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
}
|
||||||
// Able to run script
|
// Able to run script
|
||||||
workerScript.log(
|
workerScript.log(
|
||||||
caller,
|
caller,
|
||||||
@ -737,8 +751,6 @@ export function runScriptFromScript(
|
|||||||
|
|
||||||
return startWorkerScript(player, runningScriptObj, server, workerScript);
|
return startWorkerScript(player, runningScriptObj, server, workerScript);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
workerScript.log(caller, () => `Could not find script '${scriptname}' on '${server.hostname}'`);
|
workerScript.log(caller, () => `Could not find script '${scriptname}' on '${server.hostname}'`);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -25,26 +25,19 @@ export function getHackingWorkRepGain(p: IPlayer, f: Faction): number {
|
|||||||
export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number {
|
export function getFactionSecurityWorkRepGain(p: IPlayer, f: Faction): number {
|
||||||
const t =
|
const t =
|
||||||
(0.9 *
|
(0.9 *
|
||||||
(p.hacking / CONSTANTS.MaxSkillLevel +
|
(p.strength + p.defense + p.dexterity + p.agility +
|
||||||
p.strength / CONSTANTS.MaxSkillLevel +
|
(p.hacking + p.intelligence) * CalculateShareMult()
|
||||||
p.defense / CONSTANTS.MaxSkillLevel +
|
)
|
||||||
p.dexterity / CONSTANTS.MaxSkillLevel +
|
) / CONSTANTS.MaxSkillLevel / 4.5;
|
||||||
p.agility / CONSTANTS.MaxSkillLevel +
|
|
||||||
p.intelligence / CONSTANTS.MaxSkillLevel)) /
|
|
||||||
4.5;
|
|
||||||
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number {
|
export function getFactionFieldWorkRepGain(p: IPlayer, f: Faction): number {
|
||||||
const t =
|
const t =
|
||||||
(0.9 *
|
(0.9 *
|
||||||
(p.hacking / CONSTANTS.MaxSkillLevel +
|
(p.strength + p.defense + p.dexterity + p.agility + p.charisma +
|
||||||
p.strength / CONSTANTS.MaxSkillLevel +
|
(p.hacking + p.intelligence) * CalculateShareMult()
|
||||||
p.defense / CONSTANTS.MaxSkillLevel +
|
)
|
||||||
p.dexterity / CONSTANTS.MaxSkillLevel +
|
) / CONSTANTS.MaxSkillLevel / 5.5;
|
||||||
p.agility / CONSTANTS.MaxSkillLevel +
|
|
||||||
p.charisma / CONSTANTS.MaxSkillLevel +
|
|
||||||
p.intelligence / CONSTANTS.MaxSkillLevel)) /
|
|
||||||
5.5;
|
|
||||||
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
return t * p.faction_rep_mult * mult(f) * p.getIntelligenceBonus(1);
|
||||||
}
|
}
|
||||||
|
@ -427,6 +427,4 @@ export async function calculateRamUsage(
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
return { cost: RamCalculationErrorCode.SyntaxError };
|
return { cost: RamCalculationErrorCode.SyntaxError };
|
||||||
}
|
}
|
||||||
|
|
||||||
return { cost: RamCalculationErrorCode.SyntaxError };
|
|
||||||
}
|
}
|
||||||
|
71
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
71
src/ScriptEditor/NetscriptDefinitions.d.ts
vendored
@ -2289,6 +2289,67 @@ export interface Singularity {
|
|||||||
* @returns True if the focus was changed.
|
* @returns True if the focus was changed.
|
||||||
*/
|
*/
|
||||||
setFocus(focus: boolean): boolean;
|
setFocus(focus: boolean): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of programs offered on the dark web.
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 1 GB * 16/4/1
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This function allows the player to get a list of programs available for purchase
|
||||||
|
* on the dark web. Players MUST have purchased Tor to get the list of programs
|
||||||
|
* available. If Tor has not been purchased yet, this function will return an
|
||||||
|
* empty list.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // NS1
|
||||||
|
* getDarkwebProgramsAvailable();
|
||||||
|
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
||||||
|
* ```
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // NS2
|
||||||
|
* ns.getDarkwebProgramsAvailable();
|
||||||
|
* // returns ['BruteSSH.exe', 'FTPCrack.exe'...etc]
|
||||||
|
* ```
|
||||||
|
* @returns - a list of programs available for purchase on the dark web, or [] if Tor has not
|
||||||
|
* been purchased
|
||||||
|
*/
|
||||||
|
getDarkwebPrograms(): string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the price of an exploit on the dark web
|
||||||
|
* @remarks
|
||||||
|
* RAM cost: 0.5 GB * 16/4/1
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This function allows you to check the price of a darkweb exploit/program.
|
||||||
|
* You MUST have a TOR router in order to use this function. The price returned
|
||||||
|
* by this function is the same price you would see with buy -l from the terminal.
|
||||||
|
* Returns the cost of the program if it has not been purchased yet, 0 if it
|
||||||
|
* has already been purchased, or -1 if Tor has not been purchased (and thus
|
||||||
|
* the program/exploit is not available for purchase).
|
||||||
|
*
|
||||||
|
* If the program does not exist, an error is thrown.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // NS1
|
||||||
|
* getDarkwebProgramCost("brutessh.exe");
|
||||||
|
* ```
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* // NS2
|
||||||
|
* ns.getDarkwebProgramCost("brutessh.exe");
|
||||||
|
* ```
|
||||||
|
* @param programName - Name of program to check the price of
|
||||||
|
* @returns Price of the specified darkweb program
|
||||||
|
* (if not yet purchased), 0 if it has already been purchased, or -1 if Tor has not been
|
||||||
|
* purchased. Throws an error if the specified program/exploit does not exist
|
||||||
|
*/
|
||||||
|
getDarkwebProgramCost(programName: string): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -6665,6 +6726,16 @@ export interface Corporation extends WarehouseAPI, OfficeAPI {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
sellShares(amount: number): void;
|
sellShares(amount: number): void;
|
||||||
|
/**
|
||||||
|
* Get bonus time.
|
||||||
|
*
|
||||||
|
* “Bonus time” is accumulated when the game is offline or if the game is inactive in the browser.
|
||||||
|
*
|
||||||
|
* “Bonus time” makes the game progress faster.
|
||||||
|
*
|
||||||
|
* @returns Bonus time for the Corporation mechanic in milliseconds.
|
||||||
|
*/
|
||||||
|
getBonusTime(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -42,8 +42,11 @@ function GetServerByHostname(hostname: string): BaseServer | null {
|
|||||||
|
|
||||||
//Get server by IP or hostname. Returns null if invalid
|
//Get server by IP or hostname. Returns null if invalid
|
||||||
export function GetServer(s: string): BaseServer | null {
|
export function GetServer(s: string): BaseServer | null {
|
||||||
|
if (AllServers.hasOwnProperty(s)) {
|
||||||
const server = AllServers[s];
|
const server = AllServers[s];
|
||||||
if (server) return server;
|
if (server) return server;
|
||||||
|
}
|
||||||
|
|
||||||
if (!isValidIPAddress(s)) {
|
if (!isValidIPAddress(s)) {
|
||||||
return GetServerByHostname(s);
|
return GetServerByHostname(s);
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,199 @@ export function numCycleForGrowth(server: Server, growth: number, p: IPlayer, co
|
|||||||
return cycles;
|
return cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replacement function for the above function that accounts for the +$1/thread functionality of grow
|
||||||
|
* with parameters that are the same (for compatibility), but functionality is slightly different.
|
||||||
|
* This function can ONLY be used to calculate the threads needed for a given server in its current state,
|
||||||
|
* and so wouldn't be appropriate to use for formulas.exe or ns.growthAnalyze (as those are meant to
|
||||||
|
* provide theoretical scenarios, or inverse hack respectively). Players COULD use this function with a
|
||||||
|
* custom server object with the correct moneyAvailable and moneyMax amounts, combined with a multiplier
|
||||||
|
* correctly calculated to bring the server to a new moneyAvailable (ie, passing in moneyAvailable 300 and x2
|
||||||
|
* when you want the number of threads required to grow that particular server from 300 to 600), and this
|
||||||
|
* function would pass back the correct number of threads. But the key thing is that it doesn't just
|
||||||
|
* inverse/undo a hack (since the amount hacked from/to matters, not just the multiplier).
|
||||||
|
* The above is also a rather unnecessarily obtuse way of thinking about it for a formulas.exe type of
|
||||||
|
* application, so another function with different parameters is provided for that case below this one.
|
||||||
|
* Instead this function is meant to hand-off from the old numCycleForGrowth function to the new one
|
||||||
|
* where used internally for pro-rating or the like. Where you have applied a grow and want to determine
|
||||||
|
* how many threads were needed for THAT SPECIFIC grow case using a multiplier.
|
||||||
|
* Ideally, this function, and the original function above will be depreciated to use the methodology
|
||||||
|
* and inputs of the new function below this one. Even for internal cases (it's actually easier to do so).
|
||||||
|
* @param server - Server being grown
|
||||||
|
* @param growth - How much the server money is expected to be multiplied by (e.g. 1.5 for *1.5 / +50%)
|
||||||
|
* @param p - Reference to Player object
|
||||||
|
* @returns Number of "growth cycles" needed
|
||||||
|
*/
|
||||||
|
export function numCycleForGrowthTransition(server: Server, growth: number, p: IPlayer, cores = 1): number {
|
||||||
|
return numCycleForGrowthCorrected(server, server.moneyAvailable * growth, server.moneyAvailable, p, cores);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function calculates the number of threads needed to grow a server from one $amount to a higher $amount
|
||||||
|
* (ie, how many threads to grow this server from $200 to $600 for example). Used primarily for a formulas (or possibly growthAnalyze)
|
||||||
|
* type of application. It lets you "theorycraft" and easily ask what-if type questions. It's also the one that implements the
|
||||||
|
* main thread calculation algorithm, and so is the function all helper functions should call.
|
||||||
|
* It protects the inputs (so putting in INFINITY for targetMoney will use moneyMax, putting in a negative for start will use 0, etc.)
|
||||||
|
* @param server - Server being grown
|
||||||
|
* @param targetMoney - How much you want the server grown TO (not by), for instance, to grow from 200 to 600, input 600
|
||||||
|
* @param startMoney - How much you are growing the server from, for instance, to grow from 200 to 600, input 200
|
||||||
|
* @param p - Reference to Player object
|
||||||
|
* @returns Number of "growth cycles" needed
|
||||||
|
*/
|
||||||
|
export function numCycleForGrowthCorrected(server: Server, targetMoney: number, startMoney: number, p: IPlayer, cores = 1): number {
|
||||||
|
if (startMoney < 0) { startMoney = 0; } // servers "can't" have less than 0 dollars on them
|
||||||
|
if (targetMoney > server.moneyMax) { targetMoney = server.moneyMax; } // can't grow a server to more than its moneyMax
|
||||||
|
if (targetMoney <= startMoney) { return 0; } // no growth --> no threads
|
||||||
|
|
||||||
|
// exponential base adjusted by security
|
||||||
|
const adjGrowthRate = (1 + (CONSTANTS.ServerBaseGrowthRate - 1) / server.hackDifficulty);
|
||||||
|
const exponentialBase = Math.min(adjGrowthRate, CONSTANTS.ServerMaxGrowthRate); // cap growth rate
|
||||||
|
|
||||||
|
// total of all grow thread multipliers
|
||||||
|
const serverGrowthPercentage = server.serverGrowth / 100.0;
|
||||||
|
const coreMultiplier = 1 + ((cores - 1) / 16);
|
||||||
|
const threadMultiplier = serverGrowthPercentage * p.hacking_grow_mult * coreMultiplier * BitNodeMultipliers.ServerGrowthRate;
|
||||||
|
|
||||||
|
/* To understand what is done below we need to do some math. I hope the explanation is clear enough.
|
||||||
|
* First of, the names will be shortened for ease of manipulation:
|
||||||
|
* n:= targetMoney (n for new), o:= startMoney (o for old), b:= exponentialBase, t:= threadMultiplier, c:= cycles/threads
|
||||||
|
* c is what we are trying to compute.
|
||||||
|
*
|
||||||
|
* After growing, the money on a server is n = (o + c) * b^(c*t)
|
||||||
|
* c appears in an exponent and outside it, this is usually solved using the productLog/lambert's W special function
|
||||||
|
* this function will be noted W in the following
|
||||||
|
* The idea behind lambert's W function is W(x)*exp(W(x)) = x, or in other words, solving for y, y*exp(y) = x, as a function of x
|
||||||
|
* This function is provided in some advanced math library but we will compute it ourself here.
|
||||||
|
*
|
||||||
|
* Let's get back to solving the equation. It cannot be rewrote using W immediately because the base of the exponentiation is b
|
||||||
|
* b^(c*t) = exp(ln(b)*c*t) (this is how a^b is defined on reals, it matches the definition on integers)
|
||||||
|
* so n = (o + c) * exp(ln(b)*c*t) , W still cannot be used directly. We want to eliminate the other terms in 'o + c' and 'ln(b)*c*t'.
|
||||||
|
*
|
||||||
|
* A change of variable will do. The idea is to add an equation introducing a new variable (w here) in the form c = f(w) (for some f)
|
||||||
|
* With this equation we will eliminate all references to c, then solve for w and plug the result in the new equation to get c.
|
||||||
|
* The change of variable performed here should get rid of the unwanted terms mentionned above, c = w/(ln(b)*t) - o should help.
|
||||||
|
* This change of variable is allowed because whatever the value of c is, there is a value of w such that this equation holds:
|
||||||
|
* w = (c + o)*ln(b)*t (see how we used the terms we wanted to eliminate in order to build this variable change)
|
||||||
|
*
|
||||||
|
* We get n = (o + w/(ln(b)*t) - o) * exp(ln(b)*(w/(ln(b)*t) - o)*t) [ = w/(ln(b)*t) * exp(w - ln(b)*o*t) ]
|
||||||
|
* The change of variable exposed exp(w - o*ln(b)*t), we can rewrite that with exp(a - b) = exp(a)/exp(b) to isolate 'w*exp(w)'
|
||||||
|
* n = w/(ln(b)*t) * exp(w)/exp(ln(b)*o*t) [ = w*exp(w) / (ln(b) * t * b^(o*t)) ]
|
||||||
|
* Almost there, we just need to cancel the denominator on the right side of the equation:
|
||||||
|
* n * ln(b) * t * b^(o*t) = w*exp(w), Thus w = W(n * ln(b) * t * b^(o*t))
|
||||||
|
* Finally we invert the variable change: c = W(n * ln(b) * t * b^(o*t))/(ln(b)*t) - o
|
||||||
|
*
|
||||||
|
* There is still an issue left: b^(o*t) doesn't fit inside a double precision float
|
||||||
|
* because the typical amount of money on servers is arround 10^6~10^9
|
||||||
|
* We need to get an approximation of W without computing the power when o is huge
|
||||||
|
* Thankfully an approximation giving ~30% error uses log immediately so we will use
|
||||||
|
* W(n * ln(b) * t * b^(o*t)) ~= log(n * ln(b) * t * b^(o*t)) = log(n * ln(b) * t) + log(exp(ln(b) * o * t))
|
||||||
|
* = log(n * ln(b) * t) + ln(b) * o * t
|
||||||
|
* (thanks to Drak for the grow formula, f4113nb34st and Wolfram Alpha for the rewrite, dwRchyngqxs for the explanation)
|
||||||
|
*/
|
||||||
|
const x = threadMultiplier * Math.log(exponentialBase);
|
||||||
|
const y = startMoney * x + Math.log(targetMoney * x);
|
||||||
|
/* Code for the approximation of lambert's W function is adapted from
|
||||||
|
* https://git.savannah.gnu.org/cgit/gsl.git/tree/specfunc/lambert.c
|
||||||
|
* using the articles [1] https://doi.org/10.1007/BF02124750 (algorithm above)
|
||||||
|
* and [2] https://doi.org/10.1145/361952.361970 (initial approximation when x < 2.5)
|
||||||
|
*/
|
||||||
|
let w;
|
||||||
|
if (y < Math.log(2.5)) {
|
||||||
|
/* exp(y) can be safely computed without overflow.
|
||||||
|
* The relative error on the result is better when exp(y) < 2.5
|
||||||
|
* using Padé rational fraction approximation [2](5)
|
||||||
|
*/
|
||||||
|
const ey = Math.exp(y);
|
||||||
|
w = (ey + 4/3 * ey*ey) / (1 + 7/3 * ey + 5/6 * ey*ey);
|
||||||
|
} else {
|
||||||
|
/* obtain initial approximation from rough asymptotic [1](4.18)
|
||||||
|
* w = y [- log y when 0 <= y]
|
||||||
|
*/
|
||||||
|
w = y;
|
||||||
|
if (y > 0) w -= Math.log(y);
|
||||||
|
}
|
||||||
|
let cycles = w/x - startMoney;
|
||||||
|
|
||||||
|
/* Iterative refinement, the goal is to correct c until |(o + c) * b^(c*t) - n| < 1
|
||||||
|
* or the correction on the approximation is less than 1
|
||||||
|
* The Newton-Raphson method will be used, this method is a classic to find roots of functions
|
||||||
|
* (given f, find c such that f(c) = 0).
|
||||||
|
*
|
||||||
|
* The idea of this method is to take the horizontal position at which the horizontal axis
|
||||||
|
* intersects with of the tangent of the function's curve as the next approximation.
|
||||||
|
* It is equivalent to treating the curve as a line (it is called a first order approximation)
|
||||||
|
* If the current approximation is c then the new approximated value is c - f(c)/f'(c)
|
||||||
|
* (where f' is the derivative of f).
|
||||||
|
*
|
||||||
|
* In our case f(c) = (o + c) * b^(c*t) - n, f'(c) = d((o + c) * b^(c*t) - n)/dc
|
||||||
|
* = (ln(b)*t * (c + o) + 1) * b^(c*t)
|
||||||
|
* And the update step is c[new] = c[old] - ((o + c) * b^(c*t) - n)/((ln(b)*t * (o + c) + 1) * b^(c*t))
|
||||||
|
*
|
||||||
|
* The main question to ask when using this method is "does it converges?"
|
||||||
|
* (are the approximations getting better?), if it does then it does quickly.
|
||||||
|
* DOES IT CONVERGES? In the present case it does. The reason why doesn't help explaining the algorithm.
|
||||||
|
* If you are intrested then check out the wikipedia page.
|
||||||
|
*/
|
||||||
|
const bt = exponentialBase**threadMultiplier;
|
||||||
|
let corr = Infinity;
|
||||||
|
// Two sided error because we do not want to get stuck if the error stays on the wrong side
|
||||||
|
do {
|
||||||
|
// c should be above 0 so Halley's method can't be used, we have to stick to Newton-Raphson
|
||||||
|
const bct = bt**cycles;
|
||||||
|
const opc = startMoney + cycles;
|
||||||
|
const diff = opc * bct - targetMoney;
|
||||||
|
corr = diff / (opc * x + 1.0) / bct
|
||||||
|
cycles -= corr;
|
||||||
|
} while (Math.abs(corr) >= 1)
|
||||||
|
/* c is now within +/- 1 of the exact result.
|
||||||
|
* We want the ceiling of the exact result, so the floor if the approximation is above,
|
||||||
|
* the ceiling if the approximation is in the same unit as the exact result,
|
||||||
|
* and the ceiling + 1 if the approximation is below.
|
||||||
|
*/
|
||||||
|
const fca = Math.floor(cycles);
|
||||||
|
if (targetMoney <= (startMoney + fca)*Math.pow(exponentialBase, fca*threadMultiplier)) {
|
||||||
|
return fca;
|
||||||
|
}
|
||||||
|
const cca = Math.ceil(cycles);
|
||||||
|
if (targetMoney <= (startMoney + cca)*Math.pow(exponentialBase, cca*threadMultiplier)) {
|
||||||
|
return cca;
|
||||||
|
}
|
||||||
|
return cca + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function calculates the number of threads needed to grow a server based on a pre-hack money and hackAmt
|
||||||
|
* (ie, if you're hacking a server with $1e6 moneyAvail for 60%, this function will tell you how many threads to regrow it
|
||||||
|
* A good replacement for the current ns.growthAnalyze if you want players to have more control/responsibility
|
||||||
|
* @param server - Server being grown
|
||||||
|
* @param hackProp - the proportion of money hacked (total, not per thread, like 0.60 for hacking 60% of available money)
|
||||||
|
* @param prehackMoney - how much money the server had before being hacked (like 200000 for hacking a server that had $200000 on it at time of hacking)
|
||||||
|
* @param p - Reference to Player object
|
||||||
|
* @returns Number of "growth cycles" needed to reverse the described hack
|
||||||
|
*/
|
||||||
|
export function numCycleForGrowthByHackAmt(server: Server, hackProp: number, prehackMoney: number, p: IPlayer, cores = 1): number{
|
||||||
|
if (prehackMoney > server.moneyMax) prehackMoney = server.moneyMax;
|
||||||
|
const posthackMoney = Math.floor(prehackMoney * Math.min(1, Math.max(0, (1 - hackProp))));
|
||||||
|
return numCycleForGrowthCorrected(server, prehackMoney, posthackMoney, p, cores);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function calculates the number of threads needed to grow a server based on an expected growth multiplier assuming it will max out
|
||||||
|
* (ie, if you expect to grow a server by 60% to reach maxMoney, this function will tell you how many threads to grow it)
|
||||||
|
* PROBABLY the best replacement for the current ns.growthAnalyze to maintain existing scripts
|
||||||
|
* @param server - Server being grown
|
||||||
|
* @param growth - How much the server is being grown by, as a multiple in DECIMAL form (e.g. 1.5 rather than 50). Infinity is acceptable.
|
||||||
|
* @param p - Reference to Player object
|
||||||
|
* @returns Number of "growth cycles" needed
|
||||||
|
*/
|
||||||
|
export function numCycleForGrowthByMultiplier(server: Server, growth: number, p: IPlayer, cores = 1): number{
|
||||||
|
if (growth < 1.0) growth = 1.0;
|
||||||
|
const targetMoney = server.moneyMax;
|
||||||
|
const startingMoney = server.moneyMax / growth;
|
||||||
|
return numCycleForGrowthCorrected(server, targetMoney, startingMoney, p, cores);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Applied server growth for a single server. Returns the percentage growth
|
//Applied server growth for a single server. Returns the percentage growth
|
||||||
export function processSingleServerGrowth(server: Server, threads: number, p: IPlayer, cores = 1): number {
|
export function processSingleServerGrowth(server: Server, threads: number, p: IPlayer, cores = 1): number {
|
||||||
let serverGrowth = calculateServerGrowth(server, threads, p, cores);
|
let serverGrowth = calculateServerGrowth(server, threads, p, cores);
|
||||||
@ -90,8 +283,8 @@ export function processSingleServerGrowth(server: Server, threads: number, p: IP
|
|||||||
|
|
||||||
// if there was any growth at all, increase security
|
// if there was any growth at all, increase security
|
||||||
if (oldMoneyAvailable !== server.moneyAvailable) {
|
if (oldMoneyAvailable !== server.moneyAvailable) {
|
||||||
//Growing increases server security twice as much as hacking
|
let usedCycles = numCycleForGrowthCorrected(server, server.moneyAvailable, oldMoneyAvailable, p, cores);
|
||||||
let usedCycles = numCycleForGrowth(server, server.moneyAvailable / oldMoneyAvailable, p, cores);
|
// Growing increases server security twice as much as hacking
|
||||||
usedCycles = Math.min(Math.max(0, Math.ceil(usedCycles)), threads);
|
usedCycles = Math.min(Math.max(0, Math.ceil(usedCycles)), threads);
|
||||||
server.fortify(2 * CONSTANTS.ServerFortifyAmount * usedCycles);
|
server.fortify(2 * CONSTANTS.ServerFortifyAmount * usedCycles);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
import { styled, Theme, CSSObject } from "@mui/material/styles";
|
||||||
import createStyles from "@mui/styles/createStyles";
|
import createStyles from "@mui/styles/createStyles";
|
||||||
@ -53,7 +54,6 @@ import { Settings } from "../../Settings/Settings";
|
|||||||
import { redPillFlag } from "../../RedPill";
|
import { redPillFlag } from "../../RedPill";
|
||||||
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
import { AugmentationNames } from "../../Augmentation/data/AugmentationNames";
|
||||||
|
|
||||||
import { KEY } from "../../utils/helpers/keyCodes";
|
|
||||||
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
import { ProgramsSeen } from "../../Programs/ui/ProgramsRoot";
|
||||||
import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
|
import { InvitationsSeen } from "../../Faction/ui/FactionsRoot";
|
||||||
import { hash } from "../../hash/hash";
|
import { hash } from "../../hash/hash";
|
||||||
@ -276,54 +276,54 @@ export function SidebarRoot(props: IProps): React.ReactElement {
|
|||||||
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
function handleShortcuts(this: Document, event: KeyboardEvent): any {
|
||||||
if (Settings.DisableHotkeys) return;
|
if (Settings.DisableHotkeys) return;
|
||||||
if ((props.player.isWorking && props.player.focus) || redPillFlag) return;
|
if ((props.player.isWorking && props.player.focus) || redPillFlag) return;
|
||||||
if (event.keyCode == KEY.T && event.altKey) {
|
if (event.key === "t" && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTerminal();
|
clickTerminal();
|
||||||
} else if (event.keyCode === KEY.C && event.altKey) {
|
} else if (event.key === KEY.C && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickStats();
|
clickStats();
|
||||||
} else if (event.keyCode === KEY.E && event.altKey) {
|
} else if (event.key === KEY.E && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickScriptEditor();
|
clickScriptEditor();
|
||||||
} else if (event.keyCode === KEY.S && event.altKey) {
|
} else if (event.key === KEY.S && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickActiveScripts();
|
clickActiveScripts();
|
||||||
} else if (event.keyCode === KEY.H && event.altKey) {
|
} else if (event.key === KEY.H && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickHacknet();
|
clickHacknet();
|
||||||
} else if (event.keyCode === KEY.W && event.altKey) {
|
} else if (event.key === KEY.W && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickCity();
|
clickCity();
|
||||||
} else if (event.keyCode === KEY.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) {
|
} else if (event.key === KEY.J && event.altKey && !event.ctrlKey && !event.metaKey && canJob) {
|
||||||
// ctrl/cmd + alt + j is shortcut to open Chrome dev tools
|
// ctrl/cmd + alt + j is shortcut to open Chrome dev tools
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickJob();
|
clickJob();
|
||||||
} else if (event.keyCode === KEY.R && event.altKey) {
|
} else if (event.key === KEY.R && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTravel();
|
clickTravel();
|
||||||
} else if (event.keyCode === KEY.P && event.altKey) {
|
} else if (event.key === KEY.P && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickCreateProgram();
|
clickCreateProgram();
|
||||||
} else if (event.keyCode === KEY.F && event.altKey) {
|
} else if (event.key === KEY.F && event.altKey) {
|
||||||
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
|
if (props.page == Page.Terminal && Settings.EnableBashHotkeys) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickFactions();
|
clickFactions();
|
||||||
} else if (event.keyCode === KEY.A && event.altKey) {
|
} else if (event.key === KEY.A && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickAugmentations();
|
clickAugmentations();
|
||||||
} else if (event.keyCode === KEY.U && event.altKey) {
|
} else if (event.key === KEY.U && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickTutorial();
|
clickTutorial();
|
||||||
} else if (event.keyCode === KEY.B && event.altKey && props.player.bladeburner) {
|
} else if (event.key === KEY.B && event.altKey && props.player.bladeburner) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickBladeburner();
|
clickBladeburner();
|
||||||
} else if (event.keyCode === KEY.G && event.altKey && props.player.gang) {
|
} else if (event.key === KEY.G && event.altKey && props.player.gang) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
clickGang();
|
clickGang();
|
||||||
}
|
}
|
||||||
// if (event.keyCode === KEY.O && event.altKey) {
|
// if (event.key === KEY.O && event.altKey) {
|
||||||
// event.preventDefault();
|
// event.preventDefault();
|
||||||
// gameOptionsBoxOpen();
|
// gameOptionsBoxOpen();
|
||||||
// }
|
// }
|
||||||
|
@ -26,7 +26,7 @@ export const TerminalHelpText: string[] = [
|
|||||||
" hostname Displays the hostname of the machine",
|
" hostname Displays the hostname of the machine",
|
||||||
" kill [script/pid] [args...] Stops the specified script on the current server ",
|
" kill [script/pid] [args...] Stops the specified script on the current server ",
|
||||||
" killall Stops all running scripts on the current machine",
|
" killall Stops all running scripts on the current machine",
|
||||||
" ls [dir] [| grep pattern] Displays all files on the machine",
|
" ls [dir] [--grep pattern] Displays all files on the machine",
|
||||||
" lscpu Displays the number of CPU cores on the machine",
|
" lscpu Displays the number of CPU cores on the machine",
|
||||||
" mem [script] [-t n] Displays the amount of RAM required to run the script",
|
" mem [script] [-t n] Displays the amount of RAM required to run the script",
|
||||||
" mv [src] [dest] Move/rename a text or script file",
|
" mv [src] [dest] Move/rename a text or script file",
|
||||||
@ -295,28 +295,30 @@ export const HelpTexts: IMap<string[]> = {
|
|||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
ls: [
|
ls: [
|
||||||
"Usage: ls [dir] [| grep pattern]",
|
"Usage: ls [dir] [-l] [--grep pattern]",
|
||||||
" ",
|
" ",
|
||||||
"The ls command, with no arguments, prints all files and directories on the current server's directory to the Terminal screen. ",
|
"The ls command, with no arguments, prints all files and directories on the current server's directory to the Terminal screen. ",
|
||||||
"The files will be displayed in alphabetical order. ",
|
"The files will be displayed in alphabetical order. ",
|
||||||
" ",
|
" ",
|
||||||
"The 'dir' optional parameter can be used to display files/directories in another directory.",
|
"The 'dir' optional parameter can be used to display files/directories in another directory.",
|
||||||
" ",
|
" ",
|
||||||
"The '| grep pattern' optional parameter can be used to only display files whose filenames match the specified pattern.",
|
"The '-l' optional parameter allows you to force each item onto a single line.",
|
||||||
|
" ",
|
||||||
|
"The '--grep pattern' optional parameter can be used to only display files whose filenames match the specified pattern.",
|
||||||
" ",
|
" ",
|
||||||
"Examples:",
|
"Examples:",
|
||||||
" ",
|
" ",
|
||||||
"List all files with the '.script' extension in the current directory:",
|
"List all files with the '.script' extension in the current directory:",
|
||||||
" ",
|
" ",
|
||||||
" ls | grep .script",
|
" ls -l --grep .script",
|
||||||
" ",
|
" ",
|
||||||
"List all files with the '.js' extension in the root directory:",
|
"List all files with the '.js' extension in the root directory:",
|
||||||
" ",
|
" ",
|
||||||
" ls / | grep .js",
|
" ls / -l --grep .js",
|
||||||
" ",
|
" ",
|
||||||
"List all files with the word 'purchase' in the filename, in the 'scripts' directory:",
|
"List all files with the word 'purchase' in the filename, in the 'scripts' directory:",
|
||||||
" ",
|
" ",
|
||||||
" ls scripts | grep purchase",
|
" ls scripts -l --grep purchase",
|
||||||
" ",
|
" ",
|
||||||
],
|
],
|
||||||
lscpu: ["Usage: lscpu", " ", "Prints the number of CPU Cores the current server has", " "],
|
lscpu: ["Usage: lscpu", " ", "Prints the number of CPU Cores the current server has", " "],
|
||||||
|
@ -620,7 +620,6 @@ export class Terminal implements ITerminal {
|
|||||||
const n00dlesServ = GetServer("n00dles");
|
const n00dlesServ = GetServer("n00dles");
|
||||||
if (n00dlesServ == null) {
|
if (n00dlesServ == null) {
|
||||||
throw new Error("Could not get n00dles server");
|
throw new Error("Could not get n00dles server");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
switch (ITutorial.currStep) {
|
switch (ITutorial.currStep) {
|
||||||
case iTutorialSteps.TerminalHelp:
|
case iTutorialSteps.TerminalHelp:
|
||||||
|
@ -8,6 +8,7 @@ import { BaseServer } from "../../Server/BaseServer";
|
|||||||
import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../DirectoryHelpers";
|
import { evaluateDirectoryPath, getFirstParentDirectory, isValidDirectoryPath } from "../DirectoryHelpers";
|
||||||
import { IRouter } from "../../ui/Router";
|
import { IRouter } from "../../ui/Router";
|
||||||
import { ITerminal } from "../ITerminal";
|
import { ITerminal } from "../ITerminal";
|
||||||
|
import * as libarg from "arg"
|
||||||
|
|
||||||
export function ls(
|
export function ls(
|
||||||
terminal: ITerminal,
|
terminal: ITerminal,
|
||||||
@ -16,45 +17,47 @@ export function ls(
|
|||||||
server: BaseServer,
|
server: BaseServer,
|
||||||
args: (string | number | boolean)[],
|
args: (string | number | boolean)[],
|
||||||
): void {
|
): void {
|
||||||
|
let flags;
|
||||||
|
try {
|
||||||
|
flags = libarg({
|
||||||
|
'-l': Boolean,
|
||||||
|
'--grep': String,
|
||||||
|
'-g': '--grep',
|
||||||
|
},
|
||||||
|
{ argv: args }
|
||||||
|
)
|
||||||
|
} catch (e) {
|
||||||
|
// catch passing only -g / --grep with no string to use as the search
|
||||||
|
incorrectUsage()
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filter = flags['--grep']
|
||||||
|
|
||||||
const numArgs = args.length;
|
const numArgs = args.length;
|
||||||
function incorrectUsage(): void {
|
function incorrectUsage(): void {
|
||||||
terminal.error("Incorrect usage of ls command. Usage: ls [dir] [| grep pattern]");
|
terminal.error("Incorrect usage of ls command. Usage: ls [dir] [-l] [-g, --grep pattern]");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (numArgs > 4 || numArgs === 2) {
|
if (numArgs > 4) {
|
||||||
return incorrectUsage();
|
return incorrectUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grep
|
|
||||||
let filter = ""; // Grep
|
|
||||||
|
|
||||||
// Directory path
|
// Directory path
|
||||||
let prefix = terminal.cwd();
|
let prefix = terminal.cwd();
|
||||||
if (!prefix.endsWith("/")) {
|
if (!prefix.endsWith("/")) {
|
||||||
prefix += "/";
|
prefix += "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are 3+ arguments, then the last 3 must be for grep
|
// If first arg doesn't contain a - it must be the file/folder
|
||||||
if (numArgs >= 3) {
|
const dir = (args[0] && typeof args[0] == "string" && !args[0].startsWith("-")) ? args[0] : ""
|
||||||
if (args[numArgs - 2] !== "grep" || args[numArgs - 3] !== "|") {
|
const newPath = evaluateDirectoryPath(dir + "", terminal.cwd());
|
||||||
return incorrectUsage();
|
prefix = newPath || "";
|
||||||
}
|
|
||||||
filter = args[numArgs - 1] + "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the second argument is not a pipe, then it must be for listing a directory
|
|
||||||
if (numArgs >= 1 && args[0] !== "|") {
|
|
||||||
const newPath = evaluateDirectoryPath(args[0] + "", terminal.cwd());
|
|
||||||
prefix = newPath ? newPath : "";
|
|
||||||
if (prefix != null) {
|
|
||||||
if (!prefix.endsWith("/")) {
|
if (!prefix.endsWith("/")) {
|
||||||
prefix += "/";
|
prefix += "/";
|
||||||
}
|
}
|
||||||
if (!isValidDirectoryPath(prefix)) {
|
if (!isValidDirectoryPath(prefix)) {
|
||||||
return incorrectUsage();
|
return incorrectUsage();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Root directory, which is the same as no 'prefix' at all
|
// Root directory, which is the same as no 'prefix' at all
|
||||||
if (prefix === "/") {
|
if (prefix === "/") {
|
||||||
@ -139,10 +142,9 @@ export function ls(
|
|||||||
}),
|
}),
|
||||||
)();
|
)();
|
||||||
|
|
||||||
const rowSplit = row
|
const rowSplit = row.split("~");
|
||||||
.split(" ")
|
let rowSplitArray = rowSplit.map((x) => [x.trim(), x.replace(x.trim(), "")]);
|
||||||
.map((x) => x.trim())
|
rowSplitArray = rowSplitArray.filter((x) => !!x[0]);
|
||||||
.filter((x) => !!x);
|
|
||||||
|
|
||||||
function onScriptLinkClick(filename: string): void {
|
function onScriptLinkClick(filename: string): void {
|
||||||
if (player.getCurrentServer().hostname !== hostname) {
|
if (player.getCurrentServer().hostname !== hostname) {
|
||||||
@ -156,24 +158,32 @@ export function ls(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<span className={classes.scriptLinksWrap}>
|
<span className={classes.scriptLinksWrap}>
|
||||||
{rowSplit.map((rowItem) => (
|
{rowSplitArray.map((rowItem) => (
|
||||||
<span key={rowItem} className={classes.scriptLink} onClick={() => onScriptLinkClick(rowItem)}>
|
<span>
|
||||||
{rowItem}
|
<span key={rowItem[0]} className={classes.scriptLink} onClick={() => onScriptLinkClick(rowItem[0])}>
|
||||||
|
{rowItem[0]}
|
||||||
|
</span>
|
||||||
|
<span key={'s'+rowItem[0]}>
|
||||||
|
{rowItem[1]}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function postSegments(segments: string[], style?: any, linked?: boolean): void {
|
function postSegments(segments: string[], flags: any, style?: any, linked?: boolean): void {
|
||||||
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
|
const maxLength = Math.max(...segments.map((s) => s.length)) + 1;
|
||||||
const filesPerRow = Math.ceil(80 / maxLength);
|
const filesPerRow = flags["-l"] === true ? 1 : Math.ceil(80 / maxLength);
|
||||||
for (let i = 0; i < segments.length; i++) {
|
for (let i = 0; i < segments.length; i++) {
|
||||||
let row = "";
|
let row = "";
|
||||||
for (let col = 0; col < filesPerRow; col++) {
|
for (let col = 0; col < filesPerRow; col++) {
|
||||||
if (!(i < segments.length)) break;
|
if (!(i < segments.length)) break;
|
||||||
row += segments[i];
|
row += segments[i];
|
||||||
row += " ".repeat(maxLength * (col + 1) - row.length);
|
row += " ".repeat(maxLength * (col + 1) - row.length);
|
||||||
|
if(linked) {
|
||||||
|
row += "~";
|
||||||
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
i--;
|
i--;
|
||||||
@ -196,6 +206,6 @@ export function ls(
|
|||||||
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" }, linked: true },
|
{ segments: allScripts, style: { color: "yellow", fontStyle: "bold" }, linked: true },
|
||||||
].filter((g) => g.segments.length > 0);
|
].filter((g) => g.segments.length > 0);
|
||||||
for (let i = 0; i < groups.length; i++) {
|
for (let i = 0; i < groups.length; i++) {
|
||||||
postSegments(groups[i].segments, groups[i].style, groups[i].linked);
|
postSegments(groups[i].segments, flags, groups[i].style, groups[i].linked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
const terminalInput = useRef<HTMLInputElement>(null);
|
const terminalInput = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const [value, setValue] = useState(command);
|
const [value, setValue] = useState(command);
|
||||||
const [postUpdateValue, setPostUpdateValue] = useState<{postUpdate: () => void} | null>()
|
const [postUpdateValue, setPostUpdateValue] = useState<{ postUpdate: () => void } | null>();
|
||||||
const [possibilities, setPossibilities] = useState<string[]>([]);
|
const [possibilities, setPossibilities] = useState<string[]>([]);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
@ -65,14 +65,14 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
postUpdateValue.postUpdate();
|
postUpdateValue.postUpdate();
|
||||||
setPostUpdateValue(null);
|
setPostUpdateValue(null);
|
||||||
}
|
}
|
||||||
}, [postUpdateValue])
|
}, [postUpdateValue]);
|
||||||
|
|
||||||
function saveValue(newValue: string, postUpdate?: () => void): void {
|
function saveValue(newValue: string, postUpdate?: () => void): void {
|
||||||
command = newValue;
|
command = newValue;
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
|
|
||||||
if (postUpdate) {
|
if (postUpdate) {
|
||||||
setPostUpdateValue({postUpdate});
|
setPostUpdateValue({ postUpdate });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
// Move cursor to correct location
|
// Move cursor to correct location
|
||||||
// foo bar |baz bum --> foo |baz bum
|
// foo bar |baz bum --> foo |baz bum
|
||||||
const ref = terminalInput.current;
|
const ref = terminalInput.current;
|
||||||
ref?.setSelectionRange(delStart+1, delStart+1);
|
ref?.setSelectionRange(delStart + 1, delStart + 1);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
// Move cursor to correct location
|
// Move cursor to correct location
|
||||||
// foo bar |baz bum --> foo bar |bum
|
// foo bar |baz bum --> foo bar |bum
|
||||||
const ref = terminalInput.current;
|
const ref = terminalInput.current;
|
||||||
ref?.setSelectionRange(start, start)
|
ref?.setSelectionRange(start, start);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -180,13 +180,13 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function keyDown(this: Document, event: KeyboardEvent): void {
|
function keyDown(this: Document, event: KeyboardEvent): void {
|
||||||
if (terminal.contractOpen) return;
|
if (terminal.contractOpen) return;
|
||||||
if (terminal.action !== null && event.keyCode === KEY.C && event.ctrlKey) {
|
if (terminal.action !== null && event.key === KEY.C && event.ctrlKey) {
|
||||||
terminal.finishAction(router, player, true);
|
terminal.finishAction(router, player, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ref = terminalInput.current;
|
const ref = terminalInput.current;
|
||||||
if (event.ctrlKey || event.metaKey) return;
|
if (event.ctrlKey || event.metaKey) return;
|
||||||
if (event.keyCode === KEY.C && (event.ctrlKey || event.metaKey)) return; // trying to copy
|
if (event.key === KEY.C && (event.ctrlKey || event.metaKey)) return; // trying to copy
|
||||||
|
|
||||||
if (ref) ref.focus();
|
if (ref) ref.focus();
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
|
|
||||||
async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): Promise<void> {
|
async function onKeyDown(event: React.KeyboardEvent<HTMLInputElement>): Promise<void> {
|
||||||
// Run command.
|
// Run command.
|
||||||
if (event.keyCode === KEY.ENTER && value !== "") {
|
if (event.key === KEY.ENTER && value !== "") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
terminal.print(`[${player.getCurrentServer().hostname} ~${terminal.cwd()}]> ${value}`);
|
terminal.print(`[${player.getCurrentServer().hostname} ~${terminal.cwd()}]> ${value}`);
|
||||||
terminal.executeCommands(router, player, value);
|
terminal.executeCommands(router, player, value);
|
||||||
@ -205,7 +205,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Autocomplete
|
// Autocomplete
|
||||||
if (event.keyCode === KEY.TAB && value !== "") {
|
if (event.key === KEY.TAB && value !== "") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
let copy = value;
|
let copy = value;
|
||||||
@ -256,13 +256,13 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear screen.
|
// Clear screen.
|
||||||
if (event.keyCode === KEY.L && event.ctrlKey) {
|
if (event.key === KEY.L && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
terminal.clear();
|
terminal.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select previous command.
|
// Select previous command.
|
||||||
if (event.keyCode === KEY.UPARROW || (Settings.EnableBashHotkeys && event.keyCode === KEY.P && event.ctrlKey)) {
|
if (event.key === KEY.UPARROW || (Settings.EnableBashHotkeys && event.key === "p" && event.ctrlKey)) {
|
||||||
if (Settings.EnableBashHotkeys) {
|
if (Settings.EnableBashHotkeys) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@ -290,7 +290,7 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Select next command
|
// Select next command
|
||||||
if (event.keyCode === KEY.DOWNARROW || (Settings.EnableBashHotkeys && event.keyCode === KEY.M && event.ctrlKey)) {
|
if (event.key === KEY.DOWNARROW || (Settings.EnableBashHotkeys && event.key === "m" && event.ctrlKey)) {
|
||||||
if (Settings.EnableBashHotkeys) {
|
if (Settings.EnableBashHotkeys) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
@ -317,57 +317,57 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
|
|||||||
|
|
||||||
// Extra Bash Emulation Hotkeys, must be enabled through options
|
// Extra Bash Emulation Hotkeys, must be enabled through options
|
||||||
if (Settings.EnableBashHotkeys) {
|
if (Settings.EnableBashHotkeys) {
|
||||||
if (event.keyCode === KEY.A && event.ctrlKey) {
|
if (event.key === KEY.A && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("home");
|
moveTextCursor("home");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.E && event.ctrlKey) {
|
if (event.key === KEY.E && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("end");
|
moveTextCursor("end");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.B && event.ctrlKey) {
|
if (event.key === KEY.B && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("prevchar");
|
moveTextCursor("prevchar");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.B && event.altKey) {
|
if (event.key === KEY.B && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("prevword");
|
moveTextCursor("prevword");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.F && event.ctrlKey) {
|
if (event.key === KEY.F && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("nextchar");
|
moveTextCursor("nextchar");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.F && event.altKey) {
|
if (event.key === KEY.F && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
moveTextCursor("nextword");
|
moveTextCursor("nextword");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.keyCode === KEY.H || event.keyCode === KEY.D) && event.ctrlKey) {
|
if ((event.key === KEY.H || event.key === KEY.D) && event.ctrlKey) {
|
||||||
modifyInput("backspace");
|
modifyInput("backspace");
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.W && event.ctrlKey) {
|
if (event.key === KEY.W && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("deletewordbefore");
|
modifyInput("deletewordbefore");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.D && event.altKey) {
|
if (event.key === KEY.D && event.altKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("deletewordafter");
|
modifyInput("deletewordafter");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.U && event.ctrlKey) {
|
if (event.key === KEY.U && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("clearbefore");
|
modifyInput("clearbefore");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.keyCode === KEY.K && event.ctrlKey) {
|
if (event.key === KEY.K && event.ctrlKey) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
modifyInput("clearafter");
|
modifyInput("clearafter");
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ import { calculateAchievements } from "./Achievements/Achievements";
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { setupUncaughtPromiseHandler } from "./UncaughtPromiseHandler";
|
import { setupUncaughtPromiseHandler } from "./UncaughtPromiseHandler";
|
||||||
|
import { Typography } from "@mui/material";
|
||||||
|
|
||||||
const Engine: {
|
const Engine: {
|
||||||
_lastUpdate: number;
|
_lastUpdate: number;
|
||||||
@ -406,11 +407,21 @@ const Engine: {
|
|||||||
() =>
|
() =>
|
||||||
AlertEvents.emit(
|
AlertEvents.emit(
|
||||||
<>
|
<>
|
||||||
Offline for {timeOfflineString}. While you were offline:
|
<Typography>Offline for {timeOfflineString}. While you were offline:</Typography>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Your scripts generated{" "} <Money money={offlineHackingIncome} /></li>
|
<li>
|
||||||
<li>Your Hacknet Nodes generated {hacknetProdInfo}</li>
|
<Typography>
|
||||||
<li>You gained{" "} <Reputation reputation={offlineReputation} /> reputation divided amongst your factions</li>
|
Your scripts generated <Money money={offlineHackingIncome} />
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Typography>Your Hacknet Nodes generated {hacknetProdInfo}</Typography>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Typography>
|
||||||
|
You gained <Reputation reputation={offlineReputation} /> reputation divided amongst your factions
|
||||||
|
</Typography>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</>,
|
</>,
|
||||||
),
|
),
|
||||||
|
@ -3,7 +3,7 @@ import { EventEmitter } from "../../utils/EventEmitter";
|
|||||||
import { Modal } from "./Modal";
|
import { Modal } from "./Modal";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import {sha256} from "js-sha256";
|
import { sha256 } from "js-sha256";
|
||||||
|
|
||||||
export const AlertEvents = new EventEmitter<[string | JSX.Element]>();
|
export const AlertEvents = new EventEmitter<[string | JSX.Element]>();
|
||||||
|
|
||||||
@ -23,8 +23,8 @@ export function AlertManager(): React.ReactElement {
|
|||||||
i++;
|
i++;
|
||||||
setAlerts((old) => {
|
setAlerts((old) => {
|
||||||
const hash = getMessageHash(text);
|
const hash = getMessageHash(text);
|
||||||
if (old.some(a => a.hash === hash)) {
|
if (old.some((a) => a.hash === hash)) {
|
||||||
console.log('Duplicate message');
|
console.log("Duplicate message");
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
@ -51,7 +51,7 @@ export function AlertManager(): React.ReactElement {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function getMessageHash(text: string | JSX.Element): string {
|
function getMessageHash(text: string | JSX.Element): string {
|
||||||
if (typeof text === 'string') return sha256(text);
|
if (typeof text === "string") return sha256(text);
|
||||||
return sha256(JSON.stringify(text.props));
|
return sha256(JSON.stringify(text.props));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export function CodingContractModal(): React.ReactElement {
|
|||||||
// whatever ...
|
// whatever ...
|
||||||
const value = (event.target as any).value;
|
const value = (event.target as any).value;
|
||||||
|
|
||||||
if (event.keyCode === KEY.ENTER && value !== "") {
|
if (event.key === KEY.ENTER && value !== "") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
props.onAttempt(answer);
|
props.onAttempt(answer);
|
||||||
setAnswer("");
|
setAnswer("");
|
||||||
|
@ -6,6 +6,7 @@ import Button from "@mui/material/Button";
|
|||||||
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
import Select, { SelectChangeEvent } from "@mui/material/Select";
|
||||||
import TextField from "@mui/material/TextField";
|
import TextField from "@mui/material/TextField";
|
||||||
import MenuItem from "@mui/material/MenuItem";
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import { KEY } from "../../utils/helpers/keyCodes";
|
||||||
|
|
||||||
export const PromptEvent = new EventEmitter<[Prompt]>();
|
export const PromptEvent = new EventEmitter<[Prompt]>();
|
||||||
|
|
||||||
@ -93,7 +94,7 @@ function PromptMenuText({ resolve }: IContentProps): React.ReactElement {
|
|||||||
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
const onKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (event.key === "Enter") {
|
if (event.key === KEY.ENTER) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
submit();
|
submit();
|
||||||
}
|
}
|
||||||
|
@ -37,23 +37,12 @@ export function WorkInProgressRoot(): React.ReactElement {
|
|||||||
|
|
||||||
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
if (player.workType == CONSTANTS.WorkTypeFaction) {
|
||||||
const faction = Factions[player.currentWorkFactionName];
|
const faction = Factions[player.currentWorkFactionName];
|
||||||
if (!faction) {
|
|
||||||
return (
|
|
||||||
<Grid container direction="column" justifyContent="center" alignItems="center" style={{ minHeight: "100vh" }}>
|
|
||||||
<Grid item>
|
|
||||||
<Typography>
|
|
||||||
Something has gone wrong, you cannot work for {player.currentWorkFactionName || "(Faction not found)"} at
|
|
||||||
this time.
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (!faction) {
|
if (!faction) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Typography variant="h4" color="primary">
|
<Typography variant="h4" color="primary">
|
||||||
You have not joined {faction} yet!
|
You have not joined {player.currentWorkFactionName || "(Faction not found)"} yet or cannot work at this time,
|
||||||
|
please try again if you think this should have worked
|
||||||
</Typography>
|
</Typography>
|
||||||
<Button onClick={() => router.toFactions()}>Back to Factions</Button>
|
<Button onClick={() => router.toFactions()}>Back to Factions</Button>
|
||||||
</>
|
</>
|
||||||
|
@ -52,24 +52,25 @@ class NumeralFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
formatBigNumber(n: number): string {
|
formatBigNumber(n: number): string {
|
||||||
return this.format(n, "0.000a");
|
return this.format(n, "0.[000]a");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: leverage numeral.js to do it. This function also implies you can
|
// TODO: leverage numeral.js to do it. This function also implies you can
|
||||||
// use this format in some text field but you can't. ( "1t" will parse but
|
// use this format in some text field but you can't. ( "1t" will parse but
|
||||||
// "1s" will not)
|
// "1s" will not)
|
||||||
formatReallyBigNumber(n: number, decimalPlaces = 3): string {
|
formatReallyBigNumber(n: number, decimalPlaces = 3): string {
|
||||||
|
const nAbs = Math.abs(n);
|
||||||
if (n === Infinity) return "∞";
|
if (n === Infinity) return "∞";
|
||||||
for (let i = 0; i < extraFormats.length; i++) {
|
for (let i = 0; i < extraFormats.length; i++) {
|
||||||
if (extraFormats[i] < n && n <= extraFormats[i] * 1000) {
|
if (extraFormats[i] < nAbs && nAbs <= extraFormats[i] * 1000) {
|
||||||
return this.format(n / extraFormats[i], "0." + "0".repeat(decimalPlaces)) + extraNotations[i];
|
return this.format(n / extraFormats[i], "0." + "0".repeat(decimalPlaces)) + extraNotations[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Math.abs(n) < 1000) {
|
if (nAbs < 1000) {
|
||||||
return this.format(n, "0." + "0".repeat(decimalPlaces));
|
return this.format(n, "0.[" + "0".repeat(decimalPlaces) + "]");
|
||||||
}
|
}
|
||||||
const str = this.format(n, "0." + "0".repeat(decimalPlaces) + "a");
|
const str = this.format(n, "0.[" + "0".repeat(decimalPlaces) + "]a");
|
||||||
if (str === "NaNt") return this.format(n, "0." + " ".repeat(decimalPlaces) + "e+0");
|
if (str === "NaNt") return this.format(n, "0.[" + " ".repeat(decimalPlaces) + "]e+0");
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,19 +188,56 @@ class NumeralFormatter {
|
|||||||
return this.format(n, "0.00");
|
return this.format(n, "0.00");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseCustomLargeNumber(str: string): number {
|
||||||
|
const numericRegExp = new RegExp('^(\-?\\d+\\.?\\d*)([' + extraNotations.join("") + ']?)$');
|
||||||
|
const match = str.match(numericRegExp);
|
||||||
|
if (match == null) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
const [, number, notation] = match;
|
||||||
|
const notationIndex = extraNotations.indexOf(notation);
|
||||||
|
if (notationIndex === -1) {
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
return parseFloat(number) * extraFormats[notationIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
largestAbsoluteNumber(n1: number, n2 = 0, n3 = 0): number {
|
||||||
|
if(isNaN(n1)) n1=0;
|
||||||
|
if(isNaN(n2)) n2=0;
|
||||||
|
if(isNaN(n3)) n3=0;
|
||||||
|
const largestAbsolute = Math.max(Math.abs(n1), Math.abs(n2), Math.abs(n3));
|
||||||
|
switch(largestAbsolute) {
|
||||||
|
case Math.abs(n1): return n1;
|
||||||
|
case Math.abs(n2): return n2;
|
||||||
|
case Math.abs(n3): return n3;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
parseMoney(s: string): number {
|
parseMoney(s: string): number {
|
||||||
// numeral library does not handle formats like 1e10 well (returns 110),
|
// numeral library does not handle formats like 1s (returns 1) and 1e10 (returns 110) well,
|
||||||
// so if both return a valid number, return the biggest one
|
// so if more then 1 return a valid number, return the one farthest from 0
|
||||||
const numeralValue = numeral(s).value();
|
const numeralValue = numeral(s).value();
|
||||||
const parsed = parseFloat(s);
|
const parsed = parseFloat(s);
|
||||||
if (isNaN(parsed) && numeralValue === null) {
|
const selfParsed = this.parseCustomLargeNumber(s);
|
||||||
|
// Check for one or more NaN values
|
||||||
|
if (isNaN(parsed) && numeralValue === null && isNaN(selfParsed)) { // 3x NaN
|
||||||
return NaN;
|
return NaN;
|
||||||
} else if (isNaN(parsed)) {
|
} else if (isNaN(parsed) && isNaN(selfParsed)) { // 2x NaN
|
||||||
return numeralValue;
|
return numeralValue;
|
||||||
} else if (numeralValue === null) {
|
} else if (numeralValue === null && isNaN(selfParsed)) { // 2x NaN
|
||||||
return parsed;
|
return parsed;
|
||||||
} else {
|
} else if (isNaN(parsed) && numeralValue === null) { // 2x NaN
|
||||||
return Math.max(numeralValue, parsed);
|
return selfParsed;
|
||||||
|
} else if (isNaN(parsed)) { // 1x NaN
|
||||||
|
return this.largestAbsoluteNumber(numeralValue, selfParsed);
|
||||||
|
} else if (numeralValue === null) { // 1x NaN
|
||||||
|
return this.largestAbsoluteNumber(parsed, selfParsed);
|
||||||
|
} else if (isNaN(selfParsed)) { // 1x NaN
|
||||||
|
return this.largestAbsoluteNumber(numeralValue, parsed);
|
||||||
|
} else { // no NaN
|
||||||
|
return this.largestAbsoluteNumber(numeralValue, parsed, selfParsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,51 +1,53 @@
|
|||||||
import { IMap } from "../../types";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keyboard key codes
|
* Keyboard key codes
|
||||||
*/
|
*/
|
||||||
export const KEY: IMap<number> = {
|
export enum KEY {
|
||||||
CTRL: 17,
|
//SHIFT: 16, // Check by `&& event.shiftKey`
|
||||||
DOWNARROW: 40,
|
//CTRL: 17, // Check by `&& event.ctrlKey`
|
||||||
ENTER: 13,
|
//ALT: 18, // Check by `&& event.altKey`
|
||||||
ESC: 27,
|
ENTER = "Enter",
|
||||||
TAB: 9,
|
ESC = "Escape",
|
||||||
UPARROW: 38,
|
TAB = "Tab",
|
||||||
|
UPARROW = "ArrowUp",
|
||||||
|
DOWNARROW = "ArrowDown",
|
||||||
|
LEFTARROW = "ArrowLeft",
|
||||||
|
RIGHTARROW = "ArrowRight",
|
||||||
|
|
||||||
"0": 48,
|
k0 = "0",
|
||||||
"1": 49,
|
k1 = "1",
|
||||||
"2": 50,
|
k2 = "2",
|
||||||
"3": 51,
|
k3 = "3",
|
||||||
"4": 52,
|
k4 = "4",
|
||||||
"5": 53,
|
k5 = "5",
|
||||||
"6": 54,
|
k6 = "6",
|
||||||
"7": 55,
|
k7 = "7",
|
||||||
"8": 56,
|
k8 = "8",
|
||||||
"9": 57,
|
k9 = "9",
|
||||||
|
|
||||||
A: 65,
|
A = "a",
|
||||||
B: 66,
|
B = "b",
|
||||||
C: 67,
|
C = "c",
|
||||||
D: 68,
|
D = "d",
|
||||||
E: 69,
|
E = "e",
|
||||||
F: 70,
|
F = "f",
|
||||||
G: 71,
|
G = "g",
|
||||||
H: 72,
|
H = "h",
|
||||||
I: 73,
|
I = "i",
|
||||||
J: 74,
|
J = "j",
|
||||||
K: 75,
|
K = "k",
|
||||||
L: 76,
|
L = "l",
|
||||||
M: 77,
|
M = "m",
|
||||||
N: 78,
|
N = "n",
|
||||||
O: 79,
|
O = "o",
|
||||||
P: 80,
|
P = "p",
|
||||||
Q: 81,
|
Q = "q",
|
||||||
R: 82,
|
R = "r",
|
||||||
S: 83,
|
S = "s",
|
||||||
T: 84,
|
T = "t",
|
||||||
U: 85,
|
U = "u",
|
||||||
V: 86,
|
V = "v",
|
||||||
W: 87,
|
W = "w",
|
||||||
X: 88,
|
X = "x",
|
||||||
Y: 89,
|
Y = "y",
|
||||||
Z: 90,
|
Z = "z",
|
||||||
};
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
import { jest, describe, expect } from "@jest/globals";
|
||||||
import { jest, describe, expect, test } from "@jest/globals";
|
|
||||||
|
|
||||||
import { Player } from "../../../src/Player";
|
import { Player } from "../../../src/Player";
|
||||||
import { NetscriptFunctions } from "../../../src/NetscriptFunctions";
|
import { NetscriptFunctions } from "../../../src/NetscriptFunctions";
|
||||||
@ -704,6 +703,16 @@ describe("Netscript Dynamic RAM Calculation/Generation Tests", function () {
|
|||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("getDarkwebProgramCost()", async function () {
|
||||||
|
const f = ["getDarkwebProgramCost"];
|
||||||
|
await testNonzeroDynamicRamCost(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getDarkwebPrograms()", async function () {
|
||||||
|
const f = ["getDarkwebPrograms"];
|
||||||
|
await testNonzeroDynamicRamCost(f);
|
||||||
|
});
|
||||||
|
|
||||||
it("getCharacterInformation()", async function () {
|
it("getCharacterInformation()", async function () {
|
||||||
const f = ["getCharacterInformation"];
|
const f = ["getCharacterInformation"];
|
||||||
await testNonzeroDynamicRamCost(f);
|
await testNonzeroDynamicRamCost(f);
|
||||||
|
@ -656,6 +656,16 @@ describe("Netscript Static RAM Calculation/Generation Tests", function () {
|
|||||||
await expectNonZeroRamCost(f);
|
await expectNonZeroRamCost(f);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("getDarkwebPrograms()", async function () {
|
||||||
|
const f = ["getDarkwebPrograms"];
|
||||||
|
await expectNonZeroRamCost(f);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getDarkwebProgramCost()", async function () {
|
||||||
|
const f = ["getDarkwebProgramCost"];
|
||||||
|
await expectNonZeroRamCost(f);
|
||||||
|
});
|
||||||
|
|
||||||
it("upgradeHomeRam()", async function () {
|
it("upgradeHomeRam()", async function () {
|
||||||
const f = ["upgradeHomeRam"];
|
const f = ["upgradeHomeRam"];
|
||||||
await expectNonZeroRamCost(f);
|
await expectNonZeroRamCost(f);
|
||||||
|
247
test/jest/ui/nFormat.test.js
Normal file
247
test/jest/ui/nFormat.test.js
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import { describe, expect, test } from "@jest/globals";
|
||||||
|
import { numeralWrapper } from "../../../src/ui/numeralFormat";
|
||||||
|
|
||||||
|
let decimalFormat = '0.[000000]';
|
||||||
|
|
||||||
|
describe('Numeral formatting for positive numbers', () => {
|
||||||
|
test('should not format too small numbers', () => {
|
||||||
|
expect(numeralWrapper.format(0.0000000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(0.000000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(0.00000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(0.0000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(0.000001, decimalFormat)).toEqual('0.000001');
|
||||||
|
expect(numeralWrapper.format(0.00001, decimalFormat)).toEqual('0.00001');
|
||||||
|
expect(numeralWrapper.format(0.0001, decimalFormat)).toEqual('0.0001');
|
||||||
|
expect(numeralWrapper.format(0.001, decimalFormat)).toEqual('0.001');
|
||||||
|
expect(numeralWrapper.format(0.01, decimalFormat)).toEqual('0.01');
|
||||||
|
expect(numeralWrapper.format(0.1, decimalFormat)).toEqual('0.1');
|
||||||
|
expect(numeralWrapper.format(1, decimalFormat)).toEqual('1');
|
||||||
|
});
|
||||||
|
test('should format big numbers in short format', () => {
|
||||||
|
expect(numeralWrapper.formatBigNumber(987654000000000000)).toEqual('987654t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(987654300000000000)).toEqual('987654.3t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(987654320000000000)).toEqual('987654.32t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(987654321000000000)).toEqual('987654.321t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(987654321900000000)).toEqual('987654.322t');
|
||||||
|
});
|
||||||
|
test('should format really big numbers in readable format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987)).toEqual('987');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654)).toEqual('987.654k');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321)).toEqual('987.654m');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987)).toEqual('987.654b');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654)).toEqual('987.654t');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321)).toEqual('987.654q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987)).toEqual('987.654Q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987654)).toEqual('987.654s');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987654321)).toEqual('987.654S');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987654321987)).toEqual('987.654o');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987654321987654)).toEqual('987.654n');
|
||||||
|
});
|
||||||
|
test('should format even bigger really big numbers in scientific format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(987654321987654321987654321987654321)).toEqual('9.877e+35');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(9876543219876543219876543219876543219)).toEqual('9.877e+36');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(98765432198765432198765432198765432198)).toEqual('9.877e+37');
|
||||||
|
});
|
||||||
|
test('should format percentage', () => {
|
||||||
|
expect(numeralWrapper.formatPercentage(1234.56789)).toEqual('123456.79%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Numeral formatting for negative numbers', () => {
|
||||||
|
test('should not format too small numbers', () => {
|
||||||
|
expect(numeralWrapper.format(-0.0000000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(-0.000000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(-0.00000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(-0.0000001, decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format(-0.000001, decimalFormat)).toEqual('-0.000001');
|
||||||
|
expect(numeralWrapper.format(-0.00001, decimalFormat)).toEqual('-0.00001');
|
||||||
|
expect(numeralWrapper.format(-0.0001, decimalFormat)).toEqual('-0.0001');
|
||||||
|
expect(numeralWrapper.format(-0.001, decimalFormat)).toEqual('-0.001');
|
||||||
|
expect(numeralWrapper.format(-0.01, decimalFormat)).toEqual('-0.01');
|
||||||
|
expect(numeralWrapper.format(-0.1, decimalFormat)).toEqual('-0.1');
|
||||||
|
expect(numeralWrapper.format(-1, decimalFormat)).toEqual('-1');
|
||||||
|
});
|
||||||
|
test('should format big numbers in short format', () => {
|
||||||
|
expect(numeralWrapper.formatBigNumber(-987654000000000000)).toEqual('-987654t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(-987654300000000000)).toEqual('-987654.3t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(-987654320000000000)).toEqual('-987654.32t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(-987654321000000000)).toEqual('-987654.321t');
|
||||||
|
expect(numeralWrapper.formatBigNumber(-987654321900000000)).toEqual('-987654.322t');
|
||||||
|
});
|
||||||
|
test('should format really big numbers in readable format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987)).toEqual('-987');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654)).toEqual('-987.654k');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321)).toEqual('-987.654m');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987)).toEqual('-987.654b');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654)).toEqual('-987.654t');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321)).toEqual('-987.654q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987)).toEqual('-987.654Q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987654)).toEqual('-987.654s');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987654321)).toEqual('-987.654S');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987654321987)).toEqual('-987.654o');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987654321987654)).toEqual('-987.654n');
|
||||||
|
});
|
||||||
|
test('should format even bigger really big numbers in scientific format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-987654321987654321987654321987654321)).toEqual('-9.877e+35');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-9876543219876543219876543219876543219)).toEqual('-9.877e+36');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber(-98765432198765432198765432198765432198)).toEqual('-9.877e+37');
|
||||||
|
});
|
||||||
|
test('should format percentage', () => {
|
||||||
|
expect(numeralWrapper.formatPercentage(-1234.56789)).toEqual('-123456.79%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Numeral formatting of text', () => {
|
||||||
|
test('should filter non-numeric text', () => {
|
||||||
|
expect(numeralWrapper.format('abc')).toEqual('0');
|
||||||
|
expect(numeralWrapper.format('123abc')).toEqual('123');
|
||||||
|
expect(numeralWrapper.format('!3')).toEqual('3');
|
||||||
|
expect(numeralWrapper.format('3!')).toEqual('3');
|
||||||
|
expect(numeralWrapper.format('0.001', decimalFormat)).toEqual('0.001');
|
||||||
|
});
|
||||||
|
test('should not format too small numbers', () => {
|
||||||
|
expect(numeralWrapper.format('0.00000001', decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format('0.0000001', decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format('0.000001', decimalFormat)).toEqual('0.000001');
|
||||||
|
expect(numeralWrapper.format('0.00001', decimalFormat)).toEqual('0.00001');
|
||||||
|
expect(numeralWrapper.format('1', decimalFormat)).toEqual('1');
|
||||||
|
expect(numeralWrapper.format('-0.00000001', decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format('-0.0000001', decimalFormat)).toEqual('0');
|
||||||
|
expect(numeralWrapper.format('-0.000001', decimalFormat)).toEqual('-0.000001');
|
||||||
|
expect(numeralWrapper.format('-0.00001', decimalFormat)).toEqual('-0.00001');
|
||||||
|
expect(numeralWrapper.format('-1', decimalFormat)).toEqual('-1');
|
||||||
|
});
|
||||||
|
test('should format big numbers in short format', () => {
|
||||||
|
expect(numeralWrapper.formatBigNumber('987654000000000000')).toEqual('987654t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('987654300000000000')).toEqual('987654.3t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('987654320000000000')).toEqual('987654.32t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('987654321000000000')).toEqual('987654.321t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('987654321900000000')).toEqual('987654.322t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('-987654000000000000')).toEqual('-987654t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('-987654300000000000')).toEqual('-987654.3t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('-987654320000000000')).toEqual('-987654.32t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('-987654321000000000')).toEqual('-987654.321t');
|
||||||
|
expect(numeralWrapper.formatBigNumber('-987654321900000000')).toEqual('-987654.322t');
|
||||||
|
});
|
||||||
|
test('should format really big numbers in readable format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987')).toEqual('987');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654')).toEqual('987.654k');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321')).toEqual('987.654m');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987')).toEqual('987.654b');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654')).toEqual('987.654t');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321')).toEqual('987.654q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987')).toEqual('987.654Q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987654')).toEqual('987.654s');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987654321')).toEqual('987.654S');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987654321987')).toEqual('987.654o');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987654321987654')).toEqual('987.654n');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987')).toEqual('-987');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654')).toEqual('-987.654k');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321')).toEqual('-987.654m');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987')).toEqual('-987.654b');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654')).toEqual('-987.654t');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321')).toEqual('-987.654q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987')).toEqual('-987.654Q');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987654')).toEqual('-987.654s');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987654321')).toEqual('-987.654S');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987654321987')).toEqual('-987.654o');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987654321987654')).toEqual('-987.654n');
|
||||||
|
});
|
||||||
|
test('should format even bigger really big numbers in scientific format', () => {
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('987654321987654321987654321987654321')).toEqual('9.877e+35');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('9876543219876543219876543219876543219')).toEqual('9.877e+36');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('98765432198765432198765432198765432198')).toEqual('9.877e+37');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-987654321987654321987654321987654321')).toEqual('-9.877e+35');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-9876543219876543219876543219876543219')).toEqual('-9.877e+36');
|
||||||
|
expect(numeralWrapper.formatReallyBigNumber('-98765432198765432198765432198765432198')).toEqual('-9.877e+37');
|
||||||
|
});
|
||||||
|
test('should format percentage', () => {
|
||||||
|
expect(numeralWrapper.formatPercentage('1234.56789')).toEqual('123456.79%');
|
||||||
|
expect(numeralWrapper.formatPercentage('-1234.56789')).toEqual('-123456.79%');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Numeral formatting of scientific text', () => {
|
||||||
|
test('should format even bigger really big numbers in scientific format', () => {
|
||||||
|
// Accepted by numeral.js
|
||||||
|
expect(numeralWrapper.parseMoney('123')).toEqual(123);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456')).toEqual(123.456);
|
||||||
|
expect(numeralWrapper.parseMoney('123k')).toEqual(123000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456k')).toEqual(123456);
|
||||||
|
expect(numeralWrapper.parseMoney('123m')).toEqual(123000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456m')).toEqual(123456000);
|
||||||
|
expect(numeralWrapper.parseMoney('123b')).toEqual(123000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456b')).toEqual(123456000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123t')).toEqual(123000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456t')).toEqual(123456000000000);
|
||||||
|
// Custom formats, parseFloat has some rounding issues
|
||||||
|
expect(numeralWrapper.parseMoney('123q')).toBeCloseTo(123000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456q')).toBeCloseTo(123456000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123Q')).toBeCloseTo(123000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456Q')).toBeCloseTo(123456000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123s')).toBeCloseTo(123000000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456s')).toBeCloseTo(123456000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123S')).toBeCloseTo(123000000000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('123.456S')).toBeCloseTo(123456000000000000000000000);
|
||||||
|
// Larger numbers fail the test due to rounding issues
|
||||||
|
//expect(numeralWrapper.parseMoney('123o')).toBeCloseTo(123000000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('123.456o')).toBeCloseTo(123456000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('123n')).toBeCloseTo(123000000000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('123.456n')).toBeCloseTo(123456000000000000000000000000000);
|
||||||
|
});
|
||||||
|
test('should format even bigger really big negative numbers in scientific format', () => {
|
||||||
|
// Accepted by numeral.js
|
||||||
|
expect(numeralWrapper.parseMoney('-123')).toEqual(-123);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456')).toEqual(-123.456);
|
||||||
|
expect(numeralWrapper.parseMoney('-123k')).toEqual(-123000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456k')).toEqual(-123456);
|
||||||
|
expect(numeralWrapper.parseMoney('-123m')).toEqual(-123000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456m')).toEqual(-123456000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123b')).toEqual(-123000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456b')).toEqual(-123456000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123t')).toEqual(-123000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456t')).toEqual(-123456000000000);
|
||||||
|
// Custom formats, parseFloat has some rounding issues
|
||||||
|
expect(numeralWrapper.parseMoney('-123q')).toBeCloseTo(-123000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456q')).toBeCloseTo(-123456000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123Q')).toBeCloseTo(-123000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456Q')).toBeCloseTo(-123456000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123s')).toBeCloseTo(-123000000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456s')).toBeCloseTo(-123456000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123S')).toBeCloseTo(-123000000000000000000000000);
|
||||||
|
expect(numeralWrapper.parseMoney('-123.456S')).toBeCloseTo(-123456000000000000000000000);
|
||||||
|
// Larger numbers fail the test due to rounding issues
|
||||||
|
//expect(numeralWrapper.parseMoney('-123o')).toBeCloseTo(-123000000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('-123.456o')).toBeCloseTo(-123456000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('-123n')).toBeCloseTo(-123000000000000000000000000000000);
|
||||||
|
//expect(numeralWrapper.parseMoney('-123.456n')).toBeCloseTo(-123456000000000000000000000000000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Finding the number furthest away from 0', () => {
|
||||||
|
test('should work if all numbers are equal', () => {
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(0, 0, 0)).toEqual(0);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(1, 1, 1)).toEqual(1);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(123, 123, 123)).toEqual(123);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-1, -1, -1)).toEqual(-1);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-123, -123, -123)).toEqual(-123);
|
||||||
|
});
|
||||||
|
test('should work for different positive numbers, and for the largest number in each spot', () => {
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(1, 2, 3)).toEqual(3);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(456, 789, 123)).toEqual(789);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(789123, 123456, 456789)).toEqual(789123);
|
||||||
|
});
|
||||||
|
test('should work for different negative numbers, and for the smallest number in each spot', () => {
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-1, -2, -3)).toEqual(-3);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-456, -789, -123)).toEqual(-789);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-789123, -123456, -456789)).toEqual(-789123);
|
||||||
|
});
|
||||||
|
test('should work for combined positive and negative numbers', () => {
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(1, -2, 3)).toEqual(3);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(-456, 789, -123)).toEqual(789);
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber(789123, -123456, -456789)).toEqual(789123);
|
||||||
|
});
|
||||||
|
test('Should return 0 for invalid input', () => {
|
||||||
|
expect(numeralWrapper.largestAbsoluteNumber('abc', undefined, null)).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user