Merge pull request #1418 from danielyxie/dev

grow, weaken, time compression
This commit is contained in:
hydroflame 2021-10-04 19:58:57 -04:00 committed by GitHub
commit 1d71254777
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 291 additions and 103 deletions

36
dist/vendor.bundle.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -11,10 +11,11 @@ Source-File minus 1 is extremely weak because it can be fully level up quickly.
*/
export enum Exploit {
UndocumentedFunctionCall = "UndocumentedFunctionCall",
Unclickable = "Unclickable",
PrototypeTampering = "PrototypeTampering",
Bypass = "Bypass",
PrototypeTampering = "PrototypeTampering",
Unclickable = "Unclickable",
UndocumentedFunctionCall = "UndocumentedFunctionCall",
TimeCompression = "TimeCompression",
// To the players reading this. Yes you're supposed to add EditSaveFile by
// editing your save file, yes you could add them all, no we don't care
// that's not the point.
@ -24,11 +25,12 @@ export enum Exploit {
const names: {
[key: string]: string;
} = {
UndocumentedFunctionCall: "by looking beyond the documentation.",
Bypass: "by circumventing the ram cost of document.",
EditSaveFile: "by editing your save file.",
PrototypeTampering: "by tampering with Numbers prototype.",
TimeCompression: "by compressing time",
Unclickable: "by clicking the unclickable.",
Bypass: "by circumventing the ram cost of document.",
UndocumentedFunctionCall: "by looking beyond the documentation.",
};
export function ExploitName(exploit: string): string {

35
src/Exploits/loops.ts Normal file

@ -0,0 +1,35 @@
import { Player } from "../Player";
import { Exploit } from "./Exploit";
function tampering(): void {
if (Player.exploits.includes(Exploit.PrototypeTampering)) return;
// Tampering
const a = 55;
setInterval(function () {
if (a.toExponential() !== "5.5e+1") {
Player.giveExploit(Exploit.PrototypeTampering);
}
}, 15 * 60 * 1000); // 15 minutes
}
function timeCompression(): void {
if (Player.exploits.includes(Exploit.TimeCompression)) return;
// Time compression
let last = new Date().getTime();
function minute(): void {
const now = new Date().getTime();
if (now - last < 500) {
// time has been compressed.
Player.giveExploit(Exploit.TimeCompression);
return;
}
last = now;
setTimeout(minute, 1000);
}
setTimeout(minute, 1000);
}
export function startExploits(): void {
tampering();
timeCompression();
}

@ -1,11 +0,0 @@
import { Player } from "../Player";
import { Exploit } from "./Exploit";
export function startTampering(): void {
const a = 55;
setInterval(function () {
if (a.toExponential() !== "5.5e+1") {
Player.giveExploit(Exploit.PrototypeTampering);
}
}, 15 * 60 * 1000); // 15 minutes
}

@ -1,12 +1,10 @@
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { isString } from "./utils/helpers/isString";
import { AllServers } from "./Server/AllServers";
import { WorkerScript } from "./Netscript/WorkerScript";
export function netscriptDelay(time: number, workerScript: WorkerScript): Promise<void> {
return new Promise(function (resolve) {
workerScript.delay = setTimeoutRef(() => {
workerScript.delay = window.setTimeout(() => {
workerScript.delay = null;
resolve();
}, time);

@ -142,7 +142,6 @@ import { Exploit } from "./Exploits/Exploit";
import { Router } from "./ui/GameRoot";
import { numeralWrapper } from "./ui/numeralFormat";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { is2DArray } from "./utils/helpers/is2DArray";
import { convertTimeMsToTimeElapsedString } from "./utils/StringHelperFunctions";
@ -1045,7 +1044,6 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
return Promise.reject(workerScript);
}
const moneyBefore = server.moneyAvailable <= 0 ? 1 : server.moneyAvailable;
server.moneyAvailable += 1 * threads; // It can be grown even if it has no money
processSingleServerGrowth(server, threads, Player, host.cpuCores);
const moneyAfter = server.moneyAvailable;
workerScript.scriptRef.recordGrow(server.ip, threads);
@ -1389,7 +1387,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
const spawnDelay = 10;
setTimeoutRef(() => {
setTimeout(() => {
if (isNaN(threads) || threads <= 0) {
throw makeRuntimeErrorMsg("spawn", `Invalid thread count. Must be numeric and > 0, is ${threads}`);
}
@ -4072,7 +4070,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
checkSingularityAccess("softReset", 3);
workerScript.log("softReset", "Soft resetting. This will cause this script to be killed");
setTimeoutRef(() => {
setTimeout(() => {
prestigeAugmentation();
runAfterReset(cbScript);
}, 0);
@ -4091,7 +4089,7 @@ function NetscriptFunctions(workerScript: WorkerScript): NS {
}
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
workerScript.log("installAugmentations", "Installing Augmentations. This will cause this script to be killed");
setTimeoutRef(() => {
setTimeout(() => {
installAugmentations();
runAfterReset(cbScript);
}, 0);

@ -21,7 +21,6 @@ import { Script } from "./Script/Script";
import { AllServers } from "./Server/AllServers";
import { BaseServer } from "./Server/BaseServer";
import { Settings } from "./Settings/Settings";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { generate } from "escodegen";
@ -264,7 +263,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<WorkerScript
}
if (interpreter.step()) {
setTimeoutRef(runInterpreter, Settings.CodeInstructionRunTime);
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
} else {
resolve(workerScript);
}

@ -13,7 +13,6 @@ import { loadStockMarket, StockMarket } from "./StockMarket/StockMarket";
import { GameSavedEvents } from "./ui/React/Snackbar";
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import * as ExportBonus from "./ExportBonus";
import { dialogBoxCreate } from "./ui/React/DialogBox";
@ -102,7 +101,7 @@ class BitburnerSaveObject {
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeoutRef(function () {
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);

@ -7,7 +7,6 @@
import { calculateRamUsage } from "./RamCalculations";
import { ScriptUrl } from "./ScriptUrl";
import { setTimeoutRef } from "../utils/SetTimeoutRef";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "../utils/JSONReviver";
import { roundToTwo } from "../utils/helpers/roundToTwo";
@ -70,7 +69,7 @@ export class Script {
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeoutRef(function () {
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);

@ -74,6 +74,7 @@ export function processSingleServerGrowth(server: Server, threads: number, p: IP
}
const oldMoneyAvailable = server.moneyAvailable;
server.moneyAvailable += 1 * threads; // It can be grown even if it has no money
server.moneyAvailable *= serverGrowth;
// in case of data corruption

@ -13,10 +13,11 @@ export const TerminalHelpText: string[] = [
"check [script] [args...] Print a script's logs to Terminal",
"clear Clear all text on the terminal ",
"cls See 'clear' command ",
"connect [ip/hostname] Connects to a remote server",
"connect [hostname] Connects to a remote server",
"download [script/text file] Downloads scripts or text files to your computer",
"expr [math expression] Evaluate a mathematical expression",
"free Check the machine's memory (RAM) usage",
"grow Spoof money in a servers bank account, increasing the amount available.",
"hack Hack the current machine",
"help [command] Display this help text, or the help text for a command",
"home Connect to home computer",
@ -39,6 +40,7 @@ export const TerminalHelpText: string[] = [
"tail [script] [args...] Displays dynamic logs for the specified script",
"top Displays all running scripts and their RAM usage",
"unalias [alias name] Deletes the specified alias",
"weaken [server] Reduce the security of a server",
"wget [url] [target file] Retrieves code/text from a web server",
];
@ -151,7 +153,7 @@ export const HelpTexts: IMap<string[]> = {
"and down arrow keys is still valid. Also note that this is permanent and there is no way to undo this. Synonymous with 'clear' command",
],
connect: [
"connect [hostname/ip]",
"connect [hostname]",
" ",
"Connect to a remote server. The hostname or IP address of the remote server must be given as the argument ",
"to this command. Note that only servers that are immediately adjacent to the current server in the network can be connected to. To ",
@ -190,6 +192,12 @@ export const HelpTexts: IMap<string[]> = {
"Display's the memory usage on the current machine. Print the amount of RAM that is available on the current server as well as ",
"how much of it is being used.",
],
grow: [
"grow",
"",
"Spoof transactions in the current server. Increasing the money available by hacking. Requires root access.",
"See the wiki page for hacking mechanics.",
],
hack: [
"hack",
" ",
@ -394,6 +402,12 @@ export const HelpTexts: IMap<string[]> = {
" ",
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
],
weaken: [
"weaken",
"",
"Reduces the security level of the current server. Decreasing the time it takes for all operations on this server.",
"Requires root access. See the wiki page for hacking mechanics.",
],
wget: [
"wget [url] [target file]",
" ",

@ -31,9 +31,9 @@ export class Link {
export class TTimer {
time: number;
timeLeft: number;
action: "h" | "b" | "a";
action: "h" | "b" | "a" | "g" | "w";
constructor(time: number, action: "h" | "b" | "a") {
constructor(time: number, action: "h" | "b" | "a" | "g" | "w") {
this.time = time;
this.timeLeft = time;
this.action = action;
@ -62,7 +62,11 @@ export interface ITerminal {
startAnalyze(): void;
startBackdoor(player: IPlayer): void;
startHack(player: IPlayer): void;
startGrow(player: IPlayer): void;
startWeaken(player: IPlayer): void;
finishHack(router: IRouter, player: IPlayer, cancelled?: boolean): void;
finishGrow(player: IPlayer, cancelled?: boolean): void;
finishWeaken(player: IPlayer, cancelled?: boolean): void;
finishBackdoor(router: IRouter, player: IPlayer, cancelled?: boolean): void;
finishAnalyze(player: IPlayer, cancelled?: boolean): void;
finishAction(router: IRouter, player: IPlayer, cancelled?: boolean): void;

@ -17,7 +17,7 @@ import { AllServers } from "../Server/AllServers";
import { removeLeadingSlash, isInRootDirectory, evaluateFilePath } from "./DirectoryHelpers";
import { checkIfConnectedToDarkweb } from "../DarkWeb/DarkWeb";
import { iTutorialNextStep, iTutorialSteps, ITutorial } from "../InteractiveTutorial";
import { GetServerByHostname, getServer, getServerOnNetwork } from "../Server/ServerHelpers";
import { GetServerByHostname, getServer, getServerOnNetwork, processSingleServerGrowth } from "../Server/ServerHelpers";
import { ParseCommand, ParseCommands } from "./Parser";
import { SpecialServerIps, SpecialServerNames } from "../Server/SpecialServerIps";
import { Settings } from "../Settings/Settings";
@ -27,6 +27,8 @@ import {
calculateHackingExpGain,
calculatePercentMoneyHacked,
calculateHackingTime,
calculateGrowTime,
calculateWeakenTime,
} from "../Hacking";
import { numeralWrapper } from "../ui/numeralFormat";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
@ -42,6 +44,7 @@ import { connect } from "./commands/connect";
import { download } from "./commands/download";
import { expr } from "./commands/expr";
import { free } from "./commands/free";
import { grow } from "./commands/grow";
import { hack } from "./commands/hack";
import { help } from "./commands/help";
import { home } from "./commands/home";
@ -64,6 +67,7 @@ import { sudov } from "./commands/sudov";
import { tail } from "./commands/tail";
import { top } from "./commands/top";
import { unalias } from "./commands/unalias";
import { weaken } from "./commands/weaken";
import { wget } from "./commands/wget";
export class Terminal implements ITerminal {
@ -115,6 +119,23 @@ export class Terminal implements ITerminal {
this.startAction(calculateHackingTime(server, player) / 4, "h");
}
startGrow(player: IPlayer): void {
const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
this.startAction(calculateGrowTime(server, player) / 16, "g");
}
startWeaken(player: IPlayer): void {
const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
this.startAction(calculateWeakenTime(server, player) / 16, "w");
}
startBackdoor(player: IPlayer): void {
// Backdoor should take the same amount of time as hack
const server = player.getCurrentServer();
@ -130,7 +151,7 @@ export class Terminal implements ITerminal {
this.startAction(1, "a");
}
startAction(n: number, action: "h" | "b" | "a"): void {
startAction(n: number, action: "h" | "b" | "a" | "g" | "w"): void {
this.action = new TTimer(n, action);
}
@ -183,7 +204,6 @@ export class Terminal implements ITerminal {
);
} else {
// Failure
// player only gains 25% exp for failure? TODO Can change this later to balance
player.gainHackingExp(expGainedOnFailure);
this.print(
`Failed to hack ${server.hostname}. Gained ${numeralWrapper.formatExp(expGainedOnFailure)} hacking exp`,
@ -191,6 +211,39 @@ export class Terminal implements ITerminal {
}
}
finishGrow(player: IPlayer, cancelled = false): void {
if (cancelled) return;
const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
const expGain = calculateHackingExpGain(server, player);
const growth = processSingleServerGrowth(server, 1, player, server.cpuCores) - 1;
this.print(
`Available money on '${server.hostname}' grown by ${numeralWrapper.formatPercentage(
growth,
6,
)}. Gained ${numeralWrapper.formatExp(expGain)} hacking exp.`,
);
}
finishWeaken(player: IPlayer, cancelled = false): void {
if (cancelled) return;
const server = player.getCurrentServer();
if (server instanceof HacknetServer) {
this.error("Cannot hack this kind of server");
return;
}
const expGain = calculateHackingExpGain(server, player);
server.weaken(CONSTANTS.ServerWeakenAmount);
this.print(
`'${server.hostname}' security level weakened to ${server.hackDifficulty}. Gained ${numeralWrapper.formatExp(
expGain,
)} hacking exp.`,
);
}
finishBackdoor(router: IRouter, player: IPlayer, cancelled = false): void {
if (!cancelled) {
const server = player.getCurrentServer();
@ -257,6 +310,10 @@ export class Terminal implements ITerminal {
this.print(this.getProgressText());
if (this.action.action === "h") {
this.finishHack(router, player, cancelled);
} else if (this.action.action === "g") {
this.finishGrow(player, cancelled);
} else if (this.action.action === "w") {
this.finishWeaken(player, cancelled);
} else if (this.action.action === "b") {
this.finishBackdoor(router, player, cancelled);
} else if (this.action.action === "a") {
@ -657,6 +714,7 @@ export class Terminal implements ITerminal {
args: (string | number)[],
) => void;
} = {
"scan-analyze": scananalyze,
alias: alias,
analyze: analyze,
backdoor: backdoor,
@ -664,12 +722,13 @@ export class Terminal implements ITerminal {
cat: cat,
cd: cd,
check: check,
cls: () => this.clear(),
clear: () => this.clear(),
cls: () => this.clear(),
connect: connect,
download: download,
expr: expr,
free: free,
grow: grow,
hack: hack,
help: help,
home: home,
@ -686,12 +745,12 @@ export class Terminal implements ITerminal {
rm: rm,
run: run,
scan: scan,
"scan-analyze": scananalyze,
scp: scp,
sudov: sudov,
tail: tail,
top: top,
unalias: unalias,
weaken: weaken,
wget: wget,
};

@ -0,0 +1,44 @@
import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { Server } from "../../Server/Server";
export function grow(
terminal: ITerminal,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],
): void {
if (args.length !== 0) {
terminal.error("Incorrect usage of grow command. Usage: grow");
return;
}
if (!(server instanceof Server)) {
terminal.error(
"Cannot grow your own machines! You are currently connected to your home PC or one of your purchased servers",
);
}
const normalServer = server as Server;
// Hack the current PC (usually for money)
// You can't grow your home pc or servers you purchased
if (normalServer.purchasedByPlayer) {
terminal.error(
"Cannot grow your own machines! You are currently connected to your home PC or one of your purchased servers",
);
return;
}
if (!normalServer.hasAdminRights) {
terminal.error("You do not have admin rights for this machine! Cannot grow");
return;
}
if (normalServer.requiredHackingSkill > player.hacking_skill) {
terminal.error(
"Your hacking skill is not high enough to attempt hacking this machine. Try analyzing the machine to determine the required hacking skill",
);
return;
}
terminal.startGrow(player);
}

@ -0,0 +1,44 @@
import { ITerminal } from "../ITerminal";
import { IRouter } from "../../ui/Router";
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BaseServer } from "../../Server/BaseServer";
import { Server } from "../../Server/Server";
export function weaken(
terminal: ITerminal,
router: IRouter,
player: IPlayer,
server: BaseServer,
args: (string | number)[],
): void {
if (args.length !== 0) {
terminal.error("Incorrect usage of weaken command. Usage: weaken");
return;
}
if (!(server instanceof Server)) {
terminal.error(
"Cannot weaken your own machines! You are currently connected to your home PC or one of your purchased servers",
);
}
const normalServer = server as Server;
// Hack the current PC (usually for money)
// You can't weaken your home pc or servers you purchased
if (normalServer.purchasedByPlayer) {
terminal.error(
"Cannot weaken your own machines! You are currently connected to your home PC or one of your purchased servers",
);
return;
}
if (!normalServer.hasAdminRights) {
terminal.error("You do not have admin rights for this machine! Cannot weaken");
return;
}
if (normalServer.requiredHackingSkill > player.hacking_skill) {
terminal.error(
"Your hacking skill is not high enough to attempt hacking this machine. Try analyzing the machine to determine the required hacking skill",
);
return;
}
terminal.startWeaken(player);
}

@ -21,6 +21,7 @@ const commands = [
"download",
"expr",
"free",
"grow",
"hack",
"help",
"home",
@ -36,13 +37,14 @@ const commands = [
"ps",
"rm",
"run",
"scan",
"scan-analyze",
"scan",
"scp",
"sudov",
"tail",
"theme",
"top",
"weaken",
];
export function determineAllPossibilitiesForTabCompletion(

@ -4,7 +4,7 @@ import { Theme } from "@mui/material/styles";
import makeStyles from "@mui/styles/makeStyles";
import createStyles from "@mui/styles/createStyles";
import TextField from "@mui/material/TextField";
import Paper from "@mui/material/Paper";
import Tooltip from "@mui/material/Tooltip";
import { KEY } from "../../utils/helpers/keyCodes";
import { ITerminal } from "../ITerminal";
@ -18,7 +18,6 @@ const useStyles = makeStyles((theme: Theme) =>
createStyles({
textfield: {
margin: theme.spacing(0),
width: "100%",
},
input: {
backgroundColor: "#000",
@ -330,40 +329,46 @@ export function TerminalInput({ terminal, router, player }: IProps): React.React
return (
<>
{possibilities.length > 0 && (
<Paper square>
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
Possible autocomplete candidate:
</Typography>
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
{possibilities.join(" ")}
</Typography>
</Paper>
)}
<TextField
color={terminal.action === null ? "primary" : "secondary"}
autoFocus
disabled={terminal.action !== null}
autoComplete="off"
classes={{ root: classes.textfield }}
value={value}
onChange={handleValueChange}
inputRef={terminalInput}
InputProps={{
// for players to hook in
id: "terminal-input",
className: classes.input,
startAdornment: (
<Tooltip
title={
possibilities.length > 0 ? (
<>
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
Possible autocomplete candidate:
</Typography>
<Typography classes={{ root: classes.preformatted }} color={"primary"} paragraph={false}>
{possibilities.join(" ")}
</Typography>
</>
) : (
""
)
}
>
<TextField
fullWidth
color={terminal.action === null ? "primary" : "secondary"}
autoFocus
disabled={terminal.action !== null}
autoComplete="off"
value={value}
classes={{ root: classes.textfield }}
onChange={handleValueChange}
inputRef={terminalInput}
InputProps={{
// for players to hook in
id: "terminal-input",
className: classes.input,
startAdornment: (
<Typography color={terminal.action === null ? "primary" : "secondary"} flexShrink={0}>
[{player.getCurrentServer().hostname}&nbsp;~{terminal.cwd()}]&gt;&nbsp;
</Typography>
</>
),
spellCheck: false,
onKeyDown: onKeyDown,
}}
></TextField>
),
spellCheck: false,
onKeyDown: onKeyDown,
}}
></TextField>
</Tooltip>
</>
);
}

@ -1,4 +1,3 @@
import { setTimeoutRef } from "./utils/SetTimeoutRef";
import { dialogBoxCreate } from "./ui/React/DialogBox";
import { BaseServer } from "./Server/BaseServer";
import { Generic_fromJSON, Generic_toJSON, Reviver } from "./utils/JSONReviver";
@ -47,7 +46,7 @@ export class TextFile {
a.download = this.fn;
document.body.appendChild(a);
a.click();
setTimeoutRef(() => {
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);

@ -44,7 +44,7 @@ import { Reputation } from "./ui/React/Reputation";
import { AlertEvents } from "./ui/React/AlertManager";
import { exceptionAlert } from "./utils/helpers/exceptionAlert";
import { startTampering } from "./Exploits/tampering";
import { startExploits } from "./Exploits/loops";
import React from "react";
@ -262,7 +262,7 @@ const Engine: {
},
load: function (saveString) {
startTampering();
startExploits();
// Load game from save or create new game
if (loadGame(saveString)) {
ThemeEvents.emit();

@ -285,7 +285,9 @@ export function InteractiveTutorialRoot(): React.ReactElement {
<br />
<br />
The amount of money on a server is not limitless. So, if you constantly hack a server and deplete its money,
then you will encounter diminishing returns in your hacking.
then you will encounter diminishing returns in your hacking. You will need to use{" "}
<Typography classes={{ root: classes.textfield }}>{"[n00dles ~/]> grow"}</Typography>
and <Typography classes={{ root: classes.textfield }}>{"[n00dles ~/]> weaken"}</Typography>
</Typography>
),
canNext: true,

@ -1,5 +0,0 @@
// This is a reference to the native setTimeout() function
// setTimeout() is used in various places around the game's source code.
// This reference is used to make sure that if players alter window.setTimeout()
// through NetscriptJS, then the game will still function properly
export const setTimeoutRef = window.setTimeout.bind(window);