mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-18 05:33:54 +01:00
Added hacknet node api functions for spending hashes. Fixed several bugs with v0.46.0. Rebalanced hash upgrades. continued working on terminal directory implementation
This commit is contained in:
parent
fb857642e8
commit
3241945452
@ -1,5 +1,5 @@
|
||||
getScriptRam() Netscript Function
|
||||
===========================
|
||||
=================================
|
||||
|
||||
.. js:function:: getScriptRam(scriptname[, hostname/ip])
|
||||
|
||||
|
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
23
doc/source/netscript/hacknetnodeapi/hashCost.rst
Normal file
@ -0,0 +1,23 @@
|
||||
hashCost() Netscript Function
|
||||
=============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: hashCost(upgName, upgTarget)
|
||||
|
||||
:param string upgName: Name of upgrade to get the cost of. Must be an exact match
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes required for the specified upgrade. The name of the
|
||||
upgrade must be an exact match.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
var upgradeName = "Sell for Corporation Funds";
|
||||
if (hacknet.numHashes() > hacknet.hashCost(upgradeName)) {
|
||||
hacknet.spendHashes(upgName);
|
||||
}
|
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
11
doc/source/netscript/hacknetnodeapi/numHashes.rst
Normal file
@ -0,0 +1,11 @@
|
||||
numHashes() Netscript Function
|
||||
==============================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: numHashes()
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Returns the number of hashes you have
|
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
26
doc/source/netscript/hacknetnodeapi/spendHashes.rst
Normal file
@ -0,0 +1,26 @@
|
||||
spendHashes() Netscript Function
|
||||
================================
|
||||
|
||||
.. warning:: This page contains spoilers for the game
|
||||
|
||||
.. js:function:: spendHashes(upgName, upgTarget)
|
||||
|
||||
:param string upgName: Name of upgrade to spend hashes on. Must be an exact match
|
||||
:param string upgTarget: Object to which upgrade applies. Required for certain upgrades
|
||||
|
||||
.. note:: This function is only applicable for Hacknet Servers (the upgraded version
|
||||
of a Hacknet Node).
|
||||
|
||||
Spend the hashes generated by your Hacknet Servers on an upgrade. Returns a boolean value -
|
||||
true if the upgrade is successfully purchased, and false otherwise.
|
||||
|
||||
The name of the upgrade must be an exact match. The :code:`upgTarget` argument is used
|
||||
for upgrades such as :code:`Reduce Minimum Security`, which applies to a specific server.
|
||||
In this case, the :code:`upgTarget` argument must be the hostname of the server.
|
||||
|
||||
Example:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
hacknet.spendHashes("Sell for Corporation Funds");
|
||||
hacknet.spendHashes("Increase Maximum Money", "foodnstuff");
|
@ -3,6 +3,10 @@
|
||||
Netscript Hacknet Node API
|
||||
==========================
|
||||
|
||||
.. warning:: Not all functions in the Hacknet Node API are immediately available.
|
||||
For this reason, the documentation for this API may contains spoilers
|
||||
for the game.
|
||||
|
||||
Netscript provides the following API for accessing and upgrading your Hacknet Nodes
|
||||
through scripts.
|
||||
|
||||
@ -36,6 +40,9 @@ In :ref:`netscriptjs`::
|
||||
getRamUpgradeCost() <hacknetnodeapi/getRamUpgradeCost>
|
||||
getCoreUpgradeCost() <hacknetnodeapi/getCoreUpgradeCost>
|
||||
getCacheUpgradeCost() <hacknetnodeapi/getCacheUpgradeCost>
|
||||
numHashes() <hacknetnodeapi/numHashes>
|
||||
hashCost() <hacknetnodeapi/hashCost>
|
||||
spendHashes() <hacknetnodeapi/spendHashes>
|
||||
|
||||
.. _netscript_hacknetnodeapi_referencingahacknetnode:
|
||||
|
||||
|
@ -41,6 +41,9 @@ In :ref:`netscriptjs`::
|
||||
setToUniversityCourse() <sleeveapi/setToUniversityCourse>
|
||||
setToGymWorkout() <sleeveapi/setToGymWorkout>
|
||||
travel() <sleeveapi/travel>
|
||||
getSleeveAugmentations() <sleeveapi/getSleeveAugmentations>
|
||||
getSleevePurchasableAugs() <sleeveapi/getSleevePurchasableAugs>
|
||||
purchaseSleeveAug() <sleeveapi/purchaseSleeveAug>
|
||||
|
||||
.. _netscript_sleeveapi_referencingaduplicatesleeve:
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
getSleevePurchasableAugs() Netscript Function
|
||||
=======================================
|
||||
=============================================
|
||||
|
||||
.. js:function:: getSleevePurchasableAugs(sleeveNumber)
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
import {post} from "./ui/postToTerminal";
|
||||
import { IMap } from "./types";
|
||||
import { post } from "./ui/postToTerminal";
|
||||
|
||||
let Aliases = {};
|
||||
let GlobalAliases = {};
|
||||
export let Aliases: IMap<string> = {};
|
||||
export let GlobalAliases: IMap<string> = {};
|
||||
|
||||
function loadAliases(saveString) {
|
||||
export function loadAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
Aliases = {};
|
||||
} else {
|
||||
@ -11,7 +12,7 @@ function loadAliases(saveString) {
|
||||
}
|
||||
}
|
||||
|
||||
function loadGlobalAliases(saveString) {
|
||||
export function loadGlobalAliases(saveString: string): void {
|
||||
if (saveString === "") {
|
||||
GlobalAliases = {};
|
||||
} else {
|
||||
@ -20,7 +21,7 @@ function loadGlobalAliases(saveString) {
|
||||
}
|
||||
|
||||
//Print all aliases to terminal
|
||||
function printAliases() {
|
||||
export function printAliases(): void {
|
||||
for (var name in Aliases) {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
post("alias " + name + "=" + Aliases[name]);
|
||||
@ -34,7 +35,7 @@ function printAliases() {
|
||||
}
|
||||
|
||||
//True if successful, false otherwise
|
||||
function parseAliasDeclaration(dec,global=false) {
|
||||
export function parseAliasDeclaration(dec: string, global: boolean=false) {
|
||||
var re = /^([_|\w|!|%|,|@]+)="(.+)"$/;
|
||||
var matches = dec.match(re);
|
||||
if (matches == null || matches.length != 3) {return false;}
|
||||
@ -46,50 +47,53 @@ function parseAliasDeclaration(dec,global=false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function addAlias(name, value) {
|
||||
if (name in GlobalAliases){
|
||||
function addAlias(name: string, value: string): void {
|
||||
if (name in GlobalAliases) {
|
||||
delete GlobalAliases[name];
|
||||
}
|
||||
Aliases[name] = value;
|
||||
}
|
||||
|
||||
function addGlobalAlias(name, value) {
|
||||
function addGlobalAlias(name: string, value: string): void {
|
||||
if (name in Aliases){
|
||||
delete Aliases[name];
|
||||
}
|
||||
GlobalAliases[name] = value;
|
||||
}
|
||||
|
||||
function getAlias(name) {
|
||||
function getAlias(name: string): string | null {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
return Aliases[name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGlobalAlias(name) {
|
||||
function getGlobalAlias(name: string): string | null {
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
return GlobalAliases[name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function removeAlias(name) {
|
||||
export function removeAlias(name: string): boolean {
|
||||
if (Aliases.hasOwnProperty(name)) {
|
||||
delete Aliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GlobalAliases.hasOwnProperty(name)) {
|
||||
delete GlobalAliases[name];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//Returns the original string with any aliases substituted in
|
||||
//Aliases only applied to "whole words", one level deep
|
||||
function substituteAliases(origCommand) {
|
||||
var commandArray = origCommand.split(" ");
|
||||
export function substituteAliases(origCommand: string): string {
|
||||
const commandArray = origCommand.split(" ");
|
||||
if (commandArray.length > 0){
|
||||
// For the unalias command, dont substite
|
||||
if (commandArray[0] === "unalias") { return commandArray.join(" "); }
|
||||
@ -112,6 +116,3 @@ function substituteAliases(origCommand) {
|
||||
}
|
||||
return commandArray.join(" ");
|
||||
}
|
||||
|
||||
export {Aliases, GlobalAliases, printAliases, parseAliasDeclaration,
|
||||
removeAlias, substituteAliases, loadAliases, loadGlobalAliases};
|
@ -273,21 +273,13 @@ export let CONSTANTS: IMap<any> = {
|
||||
|
||||
LatestUpdate:
|
||||
`
|
||||
v0.46.0
|
||||
* Added BitNode-9: Hacktocracy
|
||||
* Changed BitNode-11's multipliers to make it slightly harder overall
|
||||
* Source-File 11 is now slightly stronger
|
||||
* Added several functions to Netscript Sleeve API for buying Sleeve augmentations (by hydroflame)
|
||||
* Added a new stat for Duplicate Sleeves: Memory
|
||||
* Increase baseline experience earned from Infiltration, but it now gives diminishing returns (on exp) as you get to higher difficulties/levels
|
||||
* In Bladeburner, stamina gained from Hyperbolic Regeneration Chamber is now a percentage of your max stamina
|
||||
|
||||
* Corporation Changes:
|
||||
** 'Demand' value of products decreases more slowly
|
||||
** Bug Fix: Fixed a Corporation issue that broke the Market-TA2 Research
|
||||
** Bug Fix: Issuing New Shares now works properly
|
||||
|
||||
* Bug Fix: Money Statistics tracker was incorrectly recording profits when selling stocks manually
|
||||
* Bug Fix: Fixed an issue with the job requirement tooltip for security jobs
|
||||
v0.46.1
|
||||
* Added numHashes(), hashCost(), and spendHashes() functions to the Netscript Hacknet Node API
|
||||
* 'Generate Coding Contract' hash upgrade is now more expensive
|
||||
* 'Generate Coding Contract' hash upgrade now generates the contract randomly on the server, rather than on home computer
|
||||
* The cost of selling hashes for money no longer increases each time
|
||||
* Selling hashes for money now costs 4 hashes (in exchange for $1m)
|
||||
* Bug Fix: Hacknet Node earnings should work properly when game is inactive/offline
|
||||
* Bug Fix: Duplicate Sleeve augmentations are now properly reset when switching to a new BitNode
|
||||
`
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export class MainPanel extends BaseReactComponent {
|
||||
|
||||
// We can pass this setter to child components
|
||||
changeCityState(newCity) {
|
||||
if (Object.values(Cities).includes(newCity)) {
|
||||
if (Object.values(CityName).includes(newCity)) {
|
||||
this.state.city = newCity;
|
||||
} else {
|
||||
console.error(`Tried to change MainPanel's city state to an invalid city: ${newCity}`);
|
||||
@ -45,7 +45,7 @@ export class MainPanel extends BaseReactComponent {
|
||||
const currentDivision = this.routing().current();
|
||||
if (currentDivision !== this.state.division) {
|
||||
this.state.division = currentDivision;
|
||||
this.state.city = Cities.Sector12;
|
||||
this.state.city = CityName.Sector12;
|
||||
}
|
||||
|
||||
return this.renderDivisionPage();
|
||||
|
@ -54,7 +54,7 @@ export class Overview extends BaseReactComponent {
|
||||
`Dividends per share: ${numeralWrapper.format(dividendsPerShare, "$0.000a")} / s<br>` +
|
||||
`Your earnings as a shareholder (Pre-Tax): ${numeralWrapper.format(playerEarnings, "$0.000a")} / s<br>` +
|
||||
`Dividend Tax Rate: ${this.corp().dividendTaxPercentage}%<br>` +
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br>`;
|
||||
`Your earnings as a shareholder (Post-Tax): ${numeralWrapper.format(playerEarnings * (1 - (this.corp().dividendTaxPercentage / 100)), "$0.000a")} / s<br><br>`;
|
||||
}
|
||||
|
||||
let txt = "Total Funds: " + numeralWrapper.format(this.corp().funds.toNumber(), '$0.000a') + "<br>" +
|
||||
|
@ -15,7 +15,7 @@ import { HacknetServer,
|
||||
import { HashManager } from "./HashManager";
|
||||
import { HashUpgrades } from "./HashUpgrades";
|
||||
|
||||
import { generateRandomContractOnHome } from "../CodingContractGenerator";
|
||||
import { generateRandomContract } from "../CodingContractGenerator";
|
||||
import { iTutorialSteps, iTutorialNextStep,
|
||||
ITutorial} from "../InteractiveTutorial";
|
||||
import { Player } from "../Player";
|
||||
@ -45,25 +45,6 @@ export function hasHacknetServers() {
|
||||
return (Player.bitNodeN === 9 || SourceFileFlags[9] > 0);
|
||||
}
|
||||
|
||||
export function createHacknetServer() {
|
||||
const numOwned = Player.hacknetNodes.length;
|
||||
const name = `hacknet-node-${numOwned}`;
|
||||
const server = new HacknetServer({
|
||||
adminRights: true,
|
||||
hostname: name,
|
||||
player: Player,
|
||||
});
|
||||
Player.hacknetNodes.push(server.ip);
|
||||
|
||||
// Configure the HacknetServer to actually act as a Server
|
||||
AddToAllServers(server);
|
||||
const homeComputer = Player.getHomeComputer();
|
||||
homeComputer.serversOnNetwork.push(server.ip);
|
||||
server.serversOnNetwork.push(homeComputer.ip);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
export function purchaseHacknet() {
|
||||
/* INTERACTIVE TUTORIAL */
|
||||
if (ITutorial.isRunning) {
|
||||
@ -84,7 +65,7 @@ export function purchaseHacknet() {
|
||||
|
||||
if (!Player.canAfford(cost)) { return -1; }
|
||||
Player.loseMoney(cost);
|
||||
const server = createHacknetServer();
|
||||
const server = Player.createHacknetServer();
|
||||
Player.hashManager.updateCapacity(Player);
|
||||
|
||||
return numOwned;
|
||||
@ -281,9 +262,9 @@ export function processHacknetEarnings(numCycles) {
|
||||
// call the appropriate function
|
||||
if (Player.hacknetNodes.length === 0) { return 0; }
|
||||
if (hasHacknetServers()) {
|
||||
return processAllHacknetServerEarnings();
|
||||
return processAllHacknetServerEarnings(numCycles);
|
||||
} else if (Player.hacknetNodes[0] instanceof HacknetNode) {
|
||||
return processAllHacknetNodeEarnings();
|
||||
return processAllHacknetNodeEarnings(numCycles);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@ -421,7 +402,7 @@ export function purchaseHashUpgrade(upgName, upgTarget) {
|
||||
break;
|
||||
}
|
||||
case "Generate Coding Contract": {
|
||||
generateRandomContractOnHome();
|
||||
generateRandomContract();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -3,13 +3,13 @@
|
||||
*/
|
||||
import { CONSTANTS } from "../Constants";
|
||||
|
||||
import { IHacknetNode } from "./IHacknetNode";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
import { IHacknetNode } from "../Hacknet/IHacknetNode";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer";
|
||||
import { BaseServer } from "../Server/BaseServer";
|
||||
import { RunningScript } from "../Script/RunningScript";
|
||||
|
||||
import { dialogBoxCreate } from "../../utils/DialogBox";
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
|
||||
import { Generic_fromJSON,
|
||||
|
@ -151,13 +151,12 @@ export class HashManager {
|
||||
*/
|
||||
upgrade(upgName: string): boolean {
|
||||
const upg = HashUpgrades[upgName];
|
||||
const currLevel = this.upgrades[upgName];
|
||||
if (upg == null || currLevel == null) {
|
||||
if (upg == null) {
|
||||
console.error(`Invalid Upgrade Name given to HashManager.upgrade(): ${upgName}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const cost = upg.getCost(currLevel);
|
||||
const cost = this.getUpgradeCost(upgName);
|
||||
|
||||
if (this.hashes < cost) {
|
||||
return false;
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Object representing an upgrade that can be purchased with hashes
|
||||
*/
|
||||
export interface IConstructorParams {
|
||||
cost?: number;
|
||||
costPerLevel: number;
|
||||
desc: string;
|
||||
hasTargetServer?: boolean;
|
||||
@ -10,6 +11,14 @@ export interface IConstructorParams {
|
||||
}
|
||||
|
||||
export class HashUpgrade {
|
||||
/**
|
||||
* If the upgrade has a flat cost (never increases), it goes here
|
||||
* Otherwise, this property should be undefined
|
||||
*
|
||||
* This property overrides the 'costPerLevel' property
|
||||
*/
|
||||
cost?: number;
|
||||
|
||||
/**
|
||||
* Base cost for this upgrade. Every time the upgrade is purchased,
|
||||
* its cost increases by this same amount (so its 1x, 2x, 3x, 4x, etc.)
|
||||
@ -35,6 +44,8 @@ export class HashUpgrade {
|
||||
value: number = 0;
|
||||
|
||||
constructor(p: IConstructorParams) {
|
||||
if (p.cost != null) { this.cost = p.cost; }
|
||||
|
||||
this.costPerLevel = p.costPerLevel;
|
||||
this.desc = p.desc;
|
||||
this.hasTargetServer = p.hasTargetServer ? p.hasTargetServer : false;
|
||||
@ -43,6 +54,8 @@ export class HashUpgrade {
|
||||
}
|
||||
|
||||
getCost(levels: number): number {
|
||||
if (typeof this.cost === "number") { return this.cost; }
|
||||
|
||||
return Math.round((levels + 1) * this.costPerLevel);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,8 @@ import { IConstructorParams } from "../HashUpgrade";
|
||||
|
||||
export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||
{
|
||||
costPerLevel: 1,
|
||||
cost: 4,
|
||||
costPerLevel: 4,
|
||||
desc: "Sell hashes for $1m",
|
||||
name: "Sell for Money",
|
||||
value: 1e6,
|
||||
@ -62,8 +63,8 @@ export const HashUpgradesMetadata: IConstructorParams[] = [
|
||||
value: 10,
|
||||
},
|
||||
{
|
||||
costPerLevel: 150,
|
||||
desc: "Generate a random Coding Contract on your home computer",
|
||||
costPerLevel: 200,
|
||||
desc: "Generate a random Coding Contract somewhere on the network",
|
||||
name: "Generate Coding Contract",
|
||||
value: 1,
|
||||
},
|
||||
|
@ -78,7 +78,6 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
this.applyForSoftwareConsultantJob = this.applyForSoftwareConsultantJob.bind(this);
|
||||
this.applyForSoftwareJob = this.applyForSoftwareJob.bind(this);
|
||||
this.applyForWaiterJob = this.applyForWaiterJob.bind(this);
|
||||
this.checkIfEmployedHere = this.checkIfEmployedHere.bind(this);
|
||||
this.startInfiltration = this.startInfiltration.bind(this);
|
||||
this.work = this.work.bind(this);
|
||||
|
||||
@ -102,70 +101,70 @@ export class CompanyLocation extends React.Component<IProps, IState> {
|
||||
applyForAgentJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForAgentJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForBusinessConsultantJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForBusinessConsultantJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForBusinessJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForBusinessJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForEmployeeJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForEmployeeJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForItJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForItJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForPartTimeEmployeeJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForPartTimeEmployeeJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForPartTimeWaiterJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForPartTimeWaiterJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSecurityJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSecurityJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSoftwareConsultantJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSoftwareConsultantJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForSoftwareJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForSoftwareJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
applyForWaiterJob(e: React.MouseEvent<HTMLElement>) {
|
||||
if (!e.isTrusted) { return false; }
|
||||
this.props.p.applyForWaiterJob();
|
||||
this.checkIfEmployedHere();
|
||||
this.checkIfEmployedHere(true);
|
||||
}
|
||||
|
||||
checkIfEmployedHere(updateState=true) {
|
||||
checkIfEmployedHere(updateState=false) {
|
||||
this.jobTitle = this.props.p.jobs[this.props.locName];
|
||||
if (this.jobTitle != null) {
|
||||
this.companyPosition = CompanyPositions[this.jobTitle];
|
||||
|
@ -36,12 +36,12 @@ import { netscriptCanGrow,
|
||||
import { getCostOfNextHacknetNode,
|
||||
getCostOfNextHacknetServer,
|
||||
purchaseHacknet,
|
||||
hasHacknetServers } from "./Hacknet/HacknetHelpers";
|
||||
hasHacknetServers,
|
||||
purchaseHashUpgrade } from "./Hacknet/HacknetHelpers";
|
||||
import { CityName } from "./Locations/data/CityNames";
|
||||
import { LocationName } from "./Locations/data/LocationNames";
|
||||
|
||||
import { HacknetServer } from "./Hacknet/HacknetServer";
|
||||
import {Locations} from "./Locations";
|
||||
import { Message } from "./Message/Message";
|
||||
import { Messages } from "./Message/MessageHelpers";
|
||||
import {inMission} from "./Missions";
|
||||
@ -363,6 +363,19 @@ function NetscriptFunctions(workerScript) {
|
||||
if (!hasHacknetServers()) { return Infinity; }
|
||||
const node = getHacknetNode(i);
|
||||
return node.calculateCacheUpgradeCost(n);
|
||||
},
|
||||
numHashes : function() {
|
||||
if (!hasHacknetServers()) { return 0; }
|
||||
return Player.hashManager.hashes;
|
||||
},
|
||||
hashCost : function(upgName) {
|
||||
if (!hasHacknetServers()) { return Infinity; }
|
||||
|
||||
return Player.hashManager.getUpgradeCost(upgName);
|
||||
},
|
||||
spendHashes : function(upgName, upgTarget) {
|
||||
if (!hasHacknetServers()) { return false; }
|
||||
return purchaseHashUpgrade(upgName, upgTarget);
|
||||
}
|
||||
},
|
||||
sprintf : sprintf,
|
||||
@ -2852,10 +2865,6 @@ function NetscriptFunctions(workerScript) {
|
||||
AddToAllServers(darkweb);
|
||||
SpecialServerIps.addIp("Darkweb Server", darkweb.ip);
|
||||
|
||||
const purchaseTor = document.getElementById("location-purchase-tor");
|
||||
purchaseTor.setAttribute("class", "a-link-button-bought");
|
||||
purchaseTor.innerHTML = "TOR Router - Purchased";
|
||||
|
||||
Player.getHomeComputer().serversOnNetwork.push(darkweb.ip);
|
||||
darkweb.serversOnNetwork.push(Player.getHomeComputer().ip);
|
||||
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain);
|
||||
|
@ -123,6 +123,7 @@ export interface IPlayer {
|
||||
gainAgilityExp(exp: number): void;
|
||||
gainCharismaExp(exp: number): void;
|
||||
gainMoney(money: number): void;
|
||||
getCurrentServer(): Server;
|
||||
getHomeComputer(): Server;
|
||||
getNextCompanyPosition(company: Company, entryPosType: CompanyPosition): CompanyPosition;
|
||||
getUpgradeHomeRamCost(): number;
|
||||
|
@ -1631,9 +1631,8 @@ export function applyForJob(entryPosType, sing=false) {
|
||||
document.getElementById("world-menu-header").click();
|
||||
|
||||
if (sing) { return true; }
|
||||
dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!");
|
||||
|
||||
Engine.loadLocationContent(false);
|
||||
dialogBoxCreate("Congratulations! You were offered a new job at " + this.companyName + " as a " + pos.name + "!");
|
||||
}
|
||||
|
||||
//Returns your next position at a company given the field (software, business, etc.)
|
||||
@ -1734,7 +1733,6 @@ export function applyForEmployeeJob(sing=false) {
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
dialogBoxCreate("Congratulations, you are now employed at " + this.companyName);
|
||||
Engine.loadLocationContent(false);
|
||||
} else {
|
||||
if (sing) {return false;}
|
||||
dialogBoxCreate("Unforunately, you do not qualify for this position");
|
||||
@ -1750,7 +1748,6 @@ export function applyForPartTimeEmployeeJob(sing=false) {
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
dialogBoxCreate("Congratulations, you are now employed part-time at " + this.companyName);
|
||||
Engine.loadLocationContent(false);
|
||||
} else {
|
||||
if (sing) {return false;}
|
||||
dialogBoxCreate("Unforunately, you do not qualify for this position");
|
||||
@ -1766,7 +1763,6 @@ export function applyForWaiterJob(sing=false) {
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
dialogBoxCreate("Congratulations, you are now employed as a waiter at " + this.companyName);
|
||||
Engine.loadLocationContent(false);
|
||||
} else {
|
||||
if (sing) {return false;}
|
||||
dialogBoxCreate("Unforunately, you do not qualify for this position");
|
||||
@ -1782,7 +1778,6 @@ export function applyForPartTimeWaiterJob(sing=false) {
|
||||
document.getElementById("world-menu-header").click();
|
||||
if (sing) {return true;}
|
||||
dialogBoxCreate("Congratulations, you are now employed as a part-time waiter at " + this.companyName);
|
||||
Engine.loadLocationContent(false);
|
||||
} else {
|
||||
if (sing) {return false;}
|
||||
dialogBoxCreate("Unforunately, you do not qualify for this position");
|
||||
|
@ -1,8 +1,14 @@
|
||||
/**
|
||||
* Server and HacknetServer-related methods for the Player class (PlayerObject)
|
||||
*/
|
||||
import { IPlayer } from "../IPlayer";
|
||||
|
||||
import { CONSTANTS } from "../../Constants";
|
||||
import { AllServers } from "../../Server/AllServers";
|
||||
|
||||
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
|
||||
import { HacknetServer } from "../../Hacknet/HacknetServer";
|
||||
import { AddToAllServers,
|
||||
AllServers } from "../../Server/AllServers";
|
||||
import { SpecialServerIps } from "../../Server/SpecialServerIps";
|
||||
|
||||
export function hasTorRouter(this: IPlayer) {
|
||||
@ -28,3 +34,22 @@ export function getUpgradeHomeRamCost(this: IPlayer) {
|
||||
var cost = currentRam * CONSTANTS.BaseCostFor1GBOfRamHome * mult * BitNodeMultipliers.HomeComputerRamCost;
|
||||
return cost;
|
||||
}
|
||||
|
||||
export function createHacknetServer(this: IPlayer): HacknetServer {
|
||||
const numOwned = this.hacknetNodes.length;
|
||||
const name = `hacknet-node-${numOwned}`;
|
||||
const server = new HacknetServer({
|
||||
adminRights: true,
|
||||
hostname: name,
|
||||
player: this,
|
||||
});
|
||||
this.hacknetNodes.push(server.ip);
|
||||
|
||||
// Configure the HacknetServer to actually act as a Server
|
||||
AddToAllServers(server);
|
||||
const homeComputer = this.getHomeComputer();
|
||||
homeComputer.serversOnNetwork.push(server.ip);
|
||||
server.serversOnNetwork.push(homeComputer.ip);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
@ -435,20 +435,30 @@ export class Sleeve extends Person {
|
||||
* Called on every sleeve for a Source File prestige
|
||||
*/
|
||||
prestige(p: IPlayer) {
|
||||
// Reset exp
|
||||
this.hacking_exp = 0;
|
||||
this.strength_exp = 0;
|
||||
this.defense_exp = 0;
|
||||
this.dexterity_exp = 0;
|
||||
this.agility_exp = 0;
|
||||
this.charisma_exp = 0;
|
||||
|
||||
// Reset task-related stuff
|
||||
this.resetTaskStatus();
|
||||
this.earningsForSleeves = createTaskTracker();
|
||||
this.earningsForPlayer = createTaskTracker();
|
||||
this.logs = [];
|
||||
this.shockRecovery(p);
|
||||
|
||||
// Reset augs and multipliers
|
||||
this.augmentations = [];
|
||||
this.resetMultipliers();
|
||||
|
||||
// Reset sleeve-related stats
|
||||
this.shock = 1;
|
||||
this.storedCycles = 0;
|
||||
this.sync = Math.max(this.memory, 1);
|
||||
this.shockRecovery(p);
|
||||
|
||||
this.logs = [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,7 +14,6 @@ import { Faction } from "./Faction/Faction";
|
||||
import { Factions,
|
||||
initFactions } from "./Faction/Factions";
|
||||
import { joinFaction } from "./Faction/FactionHelpers";
|
||||
import { createHacknetServer } from "./Hacknet/HacknetHelpers";
|
||||
import {deleteGangDisplayContent} from "./Gang";
|
||||
import { Message } from "./Message/Message";
|
||||
import { initMessages,
|
||||
@ -97,9 +96,6 @@ function prestigeAugmentation() {
|
||||
//Re-create foreign servers
|
||||
initForeignServers(Player.getHomeComputer());
|
||||
|
||||
//Darkweb is purchase-able
|
||||
document.getElementById("location-purchase-tor").setAttribute("class", "a-link-button");
|
||||
|
||||
//Gain favor for Companies
|
||||
for (var member in Companies) {
|
||||
if (Companies.hasOwnProperty(member)) {
|
||||
@ -340,7 +336,8 @@ function prestigeSourceFile() {
|
||||
|
||||
// Source-File 9 (level 3) effect
|
||||
if (SourceFileFlags[9] >= 3) {
|
||||
const hserver = createHacknetServer();
|
||||
const hserver = Player.createHacknetServer();
|
||||
|
||||
hserver.level = 100;
|
||||
hserver.cores = 10;
|
||||
hserver.cache = 5;
|
||||
|
@ -17,6 +17,7 @@ import { AllServers } from "../Server/AllServers";
|
||||
import { processSingleServerGrowth } from "../Server/ServerHelpers";
|
||||
import { Settings } from "../Settings/Settings";
|
||||
import { EditorSetting } from "../Settings/SettingEnums";
|
||||
import { isValidFilename } from "../Terminal/DirectoryHelpers";
|
||||
import {TextFile} from "../TextFile";
|
||||
|
||||
import {Page, routing} from "../ui/navigationTracking";
|
||||
@ -247,7 +248,7 @@ function saveAndCloseScriptEditor() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkValidFilename(filename) == false) {
|
||||
if (filename !== ".fconf" && !isValidFilename(filename)) {
|
||||
dialogBoxCreate("Script filename can contain only alphanumerics, hyphens, and underscores");
|
||||
return;
|
||||
}
|
||||
@ -292,17 +293,6 @@ function saveAndCloseScriptEditor() {
|
||||
Engine.loadTerminalContent();
|
||||
}
|
||||
|
||||
//Checks that the string contains only valid characters for a filename, which are alphanumeric,
|
||||
// underscores, hyphens, and dots
|
||||
function checkValidFilename(filename) {
|
||||
var regex = /^[.a-zA-Z0-9\/_-]+$/;
|
||||
|
||||
if (filename.match(regex)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//Called when the game is loaded. Loads all running scripts (from all servers)
|
||||
//into worker scripts so that they will start running
|
||||
export function loadAllRunningScripts() {
|
||||
|
@ -16,7 +16,7 @@ import { Reviver } from "../../utils/JSONReviver";
|
||||
export let AllServers: IMap<Server | HacknetServer> = {};
|
||||
|
||||
// Saftely add a Server to the AllServers map
|
||||
export function AddToAllServers(server: Server): void {
|
||||
export function AddToAllServers(server: Server | HacknetServer): void {
|
||||
var serverIp = server.ip;
|
||||
if (ipExists(serverIp)) {
|
||||
console.log("IP of server that's being added: " + serverIp);
|
||||
|
@ -6,7 +6,7 @@ import { GetServerByHostname } from "./ServerHelpers";
|
||||
|
||||
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
|
||||
|
||||
import { createRandomString } from "../utils/createRandomString";
|
||||
import { createRandomString } from "../utils/helpers/createRandomString";
|
||||
import { createRandomIp } from "../../utils/IPAddress";
|
||||
import { Generic_fromJSON,
|
||||
Generic_toJSON,
|
||||
|
275
src/Terminal.js
275
src/Terminal.js
@ -1,7 +1,16 @@
|
||||
import {substituteAliases, printAliases,
|
||||
parseAliasDeclaration,
|
||||
removeAlias, GlobalAliases,
|
||||
Aliases} from "./Alias";
|
||||
import {
|
||||
evaluateDirectoryPath,
|
||||
isValidDirectoryPath,
|
||||
isValidFilename
|
||||
} from "./Terminal/DirectoryHelpers";
|
||||
import { determineAllPossibilitiesForTabCompletion } from "./Terminal/determineAllPossibilitiesForTabCompletion";
|
||||
|
||||
import { Aliases,
|
||||
GlobalAliases,
|
||||
parseAliasDeclaration,
|
||||
printAliases,
|
||||
removeAlias,
|
||||
substituteAliases } from "./Alias";
|
||||
import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers";
|
||||
import {CodingContract, CodingContractResult,
|
||||
CodingContractRewardType} from "./CodingContracts";
|
||||
@ -85,12 +94,13 @@ $(document).keydown(function(event) {
|
||||
|
||||
if (event.keyCode === KEY.ENTER) {
|
||||
event.preventDefault(); //Prevent newline from being entered in Script Editor
|
||||
var command = terminalInput.value;
|
||||
const command = terminalInput.value;
|
||||
const dir = Terminal.currDir + "/";
|
||||
post(
|
||||
"<span class='prompt'>[" +
|
||||
(FconfSettings.ENABLE_TIMESTAMPS ? getTimestamp() + " " : "") +
|
||||
Player.getCurrentServer().hostname +
|
||||
" ~]></span> " + command
|
||||
` ~${dir}]></span> ${command}`
|
||||
);
|
||||
|
||||
if (command.length > 0) {
|
||||
@ -183,7 +193,7 @@ $(document).keydown(function(event) {
|
||||
var commandArray = input.split(" ");
|
||||
var index = commandArray.length - 2;
|
||||
if (index < -1) {index = 0;}
|
||||
var allPos = determineAllPossibilitiesForTabCompletion(input, index);
|
||||
var allPos = determineAllPossibilitiesForTabCompletion(Player, input, index);
|
||||
if (allPos.length == 0) {return;}
|
||||
|
||||
var arg = "";
|
||||
@ -393,178 +403,8 @@ function tabCompletion(command, arg, allPossibilities, index=0) {
|
||||
}
|
||||
}
|
||||
|
||||
function determineAllPossibilitiesForTabCompletion(input, index=0) {
|
||||
var allPos = [];
|
||||
allPos = allPos.concat(Object.keys(GlobalAliases));
|
||||
var currServ = Player.getCurrentServer();
|
||||
input = input.toLowerCase();
|
||||
|
||||
//If the command starts with './' and the index == -1, then the user
|
||||
//has input ./partialexecutablename so autocomplete the script or program
|
||||
//Put './' in front of each script/executable
|
||||
if (input.startsWith("./") && index == -1) {
|
||||
//All programs and scripts
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push("./" + currServ.scripts[i].filename);
|
||||
}
|
||||
|
||||
//Programs are on home computer
|
||||
var homeComputer = Player.getHomeComputer();
|
||||
for(var i = 0; i < homeComputer.programs.length; ++i) {
|
||||
allPos.push("./" + homeComputer.programs[i]);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
//Autocomplete the command
|
||||
if (index == -1) {
|
||||
return ["alias", "analyze", "cat", "check", "clear", "cls", "connect", "download", "expr",
|
||||
"free", "hack", "help", "home", "hostname", "ifconfig", "kill", "killall",
|
||||
"ls", "lscpu", "mem", "nano", "ps", "rm", "run", "scan", "scan-analyze",
|
||||
"scp", "sudov", "tail", "theme", "top"].concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
|
||||
}
|
||||
|
||||
if (input.startsWith ("buy ")) {
|
||||
let options = [];
|
||||
for(const i in DarkWebItems) {
|
||||
const item = DarkWebItems[i]
|
||||
options.push(item.program);
|
||||
}
|
||||
return options.concat(Object.keys(GlobalAliases));
|
||||
}
|
||||
|
||||
if (input.startsWith("scp ") && index == 1) {
|
||||
for (var iphostname in AllServers) {
|
||||
if (AllServers.hasOwnProperty(iphostname)) {
|
||||
allPos.push(AllServers[iphostname].ip);
|
||||
allPos.push(AllServers[iphostname].hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("scp ") && index == 0) {
|
||||
//All Scripts and lit files
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (var i = 0; i < currServ.messages.length; ++i) {
|
||||
if (!(currServ.messages[i] instanceof Message)) {
|
||||
allPos.push(currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("connect ") || input.startsWith("telnet ")) {
|
||||
//All network connections
|
||||
for (var i = 0; i < currServ.serversOnNetwork.length; ++i) {
|
||||
var serv = AllServers[currServ.serversOnNetwork[i]];
|
||||
if (serv == null) {continue;}
|
||||
allPos.push(serv.ip); //IP
|
||||
allPos.push(serv.hostname); //Hostname
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("kill ") || input.startsWith("tail ") ||
|
||||
input.startsWith("mem ") || input.startsWith("check ")) {
|
||||
//All Scripts
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("nano ")) {
|
||||
//Scripts and text files and .fconf
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
allPos.push(".fconf");
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("rm ")) {
|
||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (let i = 0; i < currServ.programs.length; ++i) {
|
||||
allPos.push(currServ.programs[i]);
|
||||
}
|
||||
for (let i = 0; i < currServ.messages.length; ++i) {
|
||||
if (!(currServ.messages[i] instanceof Message) && isString(currServ.messages[i]) &&
|
||||
currServ.messages[i].endsWith(".lit")) {
|
||||
allPos.push(currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (let i = 0; i < currServ.contracts.length; ++i) {
|
||||
allPos.push(currServ.contracts[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("run ")) {
|
||||
//All programs, scripts, and contracts
|
||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
|
||||
//Programs are on home computer
|
||||
var homeComputer = Player.getHomeComputer();
|
||||
for (let i = 0; i < homeComputer.programs.length; ++i) {
|
||||
allPos.push(homeComputer.programs[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < currServ.contracts.length; ++i) {
|
||||
allPos.push(currServ.contracts[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("cat ")) {
|
||||
for (var i = 0; i < currServ.messages.length; ++i) {
|
||||
if (currServ.messages[i] instanceof Message) {
|
||||
allPos.push(currServ.messages[i].filename);
|
||||
} else {
|
||||
allPos.push(currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("download ")) {
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("ls ")) {
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
let Terminal = {
|
||||
//Flags to determine whether the player is currently running a hack or an analyze
|
||||
// Flags to determine whether the player is currently running a hack or an analyze
|
||||
hackFlag: false,
|
||||
analyzeFlag: false,
|
||||
actionStarted: false,
|
||||
@ -573,7 +413,12 @@ let Terminal = {
|
||||
commandHistory: [],
|
||||
commandHistoryIndex: 0,
|
||||
|
||||
contractOpen: false, //True if a Coding Contract prompt is opened
|
||||
// True if a Coding Contract prompt is opened
|
||||
contractOpen: false,
|
||||
|
||||
// Full Path of current directory
|
||||
// Excludes the trailing forward slash
|
||||
currDir: "",
|
||||
|
||||
resetTerminalInput: function() {
|
||||
if (FconfSettings.WRAP_INPUT) {
|
||||
@ -1104,7 +949,7 @@ let Terminal = {
|
||||
postError("You need to be able to connect to the Dark Web to use the buy command. (Maybe there's a TOR router you can buy somewhere)");
|
||||
}
|
||||
break;
|
||||
case "cat":
|
||||
case "cat": {
|
||||
if (commandArray.length !== 2) {
|
||||
postError("Incorrect usage of cat command. Usage: cat [file]");
|
||||
return;
|
||||
@ -1131,6 +976,34 @@ let Terminal = {
|
||||
}
|
||||
postError(`No such file ${filename}`);
|
||||
break;
|
||||
}
|
||||
case "cd": {
|
||||
if (commandArray.length !== 2) {
|
||||
postError("Incorrect number of arguments. Usage: cd [dir]");
|
||||
} else {
|
||||
let dir = commandArray[1];
|
||||
|
||||
// Ignore trailing slashes
|
||||
if (dir.endsWith("/")) {
|
||||
dir = dir.slice(0, -1);
|
||||
}
|
||||
|
||||
// If the path begins with a slash, then its an absolute path. Otherwise its relative
|
||||
if (dir.startsWith("/")) {
|
||||
// TODO
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
|
||||
evaledDir = evaluateDirectoryPath(dir);
|
||||
if (evaledDir == null) {
|
||||
postError("Invalid path");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "check":
|
||||
if (commandArray.length < 2) {
|
||||
postError("Incorrect number of arguments. Usage: check [script] [arg1] [arg2]...");
|
||||
@ -1687,28 +1560,35 @@ let Terminal = {
|
||||
},
|
||||
|
||||
executeListCommand: function(commandArray) {
|
||||
if (commandArray.length !== 1 && commandArray.length !== 2 && commandArray.length !== 4) {
|
||||
postError("Incorrect usage of ls command. Usage: ls [| grep pattern]");
|
||||
return;
|
||||
const numArgs = commandArray.length;
|
||||
function incorrectUsage() {
|
||||
postError("Incorrect usage of ls command. Usage: ls [dir] [| grep pattern]");
|
||||
}
|
||||
|
||||
// grep
|
||||
let filter = null;
|
||||
let prefix = null;
|
||||
if (commandArray.length === 4) {
|
||||
if (commandArray[1] === "|" && commandArray[2] === "grep") {
|
||||
if (commandArray[3] !== " ") {
|
||||
filter = commandArray[3];
|
||||
}
|
||||
} else {
|
||||
postError("Incorrect usage of ls command. Usage: ls [| grep pattern]");
|
||||
return;
|
||||
if (numArgs <= 0 || numArgs > 5 || numArgs === 3) {
|
||||
return incorrectUsage();
|
||||
}
|
||||
|
||||
let filter = null; // Grep
|
||||
let prefix = null; // Directory path
|
||||
|
||||
// If there are 4+ arguments, then the last 3 must be for grep
|
||||
if (numArgs >= 4) {
|
||||
if (commandArray[numArgs - 2] !== "grep" || commandArray[numArgs - 3] !== "|") {
|
||||
return incorrectUsage();
|
||||
}
|
||||
} else if (commandArray.length === 2) { // want to ls a folder
|
||||
filter = commandArray[numArgs - 1];
|
||||
}
|
||||
|
||||
// If the second argument is not a pipe, then it must be for listing a directory
|
||||
if (numArgs >= 2 && commandArray[1] !== "|") {
|
||||
prefix = commandArray[1];
|
||||
if (!prefix.endsWith("/")) {
|
||||
prefix += "/";
|
||||
}
|
||||
if (!isValidDirectoryPath(prefix)) {
|
||||
return incorrectUsage();
|
||||
}
|
||||
}
|
||||
|
||||
//Display all programs and scripts
|
||||
@ -1728,12 +1608,15 @@ let Terminal = {
|
||||
}
|
||||
}
|
||||
|
||||
// If the fn includes a forward slash, it must be in a subdirectory.
|
||||
// Therefore, we only list the "first" directory in its path
|
||||
if (fn.includes("/")) {
|
||||
fn = fn.split("/")[0]+"/";
|
||||
fn = fn.split("/")[0] + "/";
|
||||
if (folders.includes(fn)) {
|
||||
return;
|
||||
}
|
||||
folders.push(fn);
|
||||
return;
|
||||
}
|
||||
|
||||
allFiles.push(fn);
|
||||
|
121
src/Terminal/DirectoryHelpers.ts
Normal file
121
src/Terminal/DirectoryHelpers.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Helper functions that implement "directory" functionality in the Terminal.
|
||||
* These aren't real directories, they're more of a pseudo-directory implementation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether a string is a valid filename. Only used for the filename itself,
|
||||
* not the entire filepath
|
||||
*/
|
||||
export function isValidFilename(filename: string): boolean {
|
||||
// Allows alphanumerics, hyphens, underscores.
|
||||
// Must have a file exntesion
|
||||
const regex = /^[.a-zA-Z0-9_-]+[.][.a-zA-Z0-9_-]+$/;
|
||||
|
||||
// match() returns null if no match is found
|
||||
return filename.match(regex) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string is a valid directory name. Only used for the directory itself,
|
||||
* not an entire path
|
||||
*/
|
||||
export function isValidDirectoryName(name: string): boolean {
|
||||
// Allows alphanumerics, hyphens and underscores.
|
||||
// Name can begin with a single period, but otherwise cannot have any
|
||||
const regex = /^.?[a-zA-Z0-9_-]+$/;
|
||||
|
||||
// match() returns null if no match is found
|
||||
return name.match(regex) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string is a valid directory path.
|
||||
* This only checks if it has the proper formatting. It does NOT check
|
||||
* if the directories actually exist on Terminal
|
||||
*/
|
||||
export function isValidDirectoryPath(path: string): boolean {
|
||||
let t_path: string = path;
|
||||
|
||||
if (t_path.length === 0) { return false; }
|
||||
if (t_path.length === 1) {
|
||||
return isValidDirectoryName(t_path);
|
||||
}
|
||||
|
||||
// Leading/Trailing slashes dont matter for this
|
||||
if (t_path.startsWith("/")) { t_path = t_path.slice(1); }
|
||||
if (t_path.endsWith("/")) { t_path = t_path.slice(0, -1); }
|
||||
|
||||
// Check that every section of the path is a valid directory name
|
||||
const dirs = t_path.split("/");
|
||||
for (const dir of dirs) {
|
||||
// Special case, "." and ".." are valid for path
|
||||
if (dir === "." || dir === "..") { continue; }
|
||||
if (!isValidDirectoryName(dir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string is a valid file path. This only checks if it has the
|
||||
* proper formatting. It dose NOT check if the file actually exists on Terminal
|
||||
*/
|
||||
export function isValidFilePath(path: string): boolean {
|
||||
let t_path = path;
|
||||
|
||||
// Impossible for filename to have less than length of 3
|
||||
if (t_path.length < 3) { return false; }
|
||||
|
||||
// Filename can't end with trailing slash. Leading slash can be ignored
|
||||
if (t_path.endsWith("")) { return false; }
|
||||
if (t_path.startsWith("/")) { t_path = t_path.slice(1); }
|
||||
|
||||
// Everything after the last forward slash is the filename. Everything before
|
||||
// it is the file path
|
||||
const fnSeparator = t_path.lastIndexOf("/");
|
||||
if (fnSeparator === -1) {
|
||||
return isValidFilename(t_path);
|
||||
}
|
||||
|
||||
const fn = t_path.slice(fnSeparator + 1);
|
||||
const dirPath = t_path.slice(0, fnSeparator);
|
||||
|
||||
return (isValidDirectoryPath(dirPath) && isValidFilename(fn));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a directory path, including the processing of linux dots.
|
||||
* Returns the full, proper path, or null if an invalid path is passed in
|
||||
*/
|
||||
export function evaluateDirectoryPath(path: string): string | null {
|
||||
if (!isValidDirectoryPath(path)) { return null; }
|
||||
|
||||
let t_path = path;
|
||||
|
||||
// Trim leading/trailing slashes
|
||||
if (t_path.startsWith("/")) { t_path = t_path.slice(1); }
|
||||
if (t_path.endsWith("/")) { t_path = t_path.slice(0, -1); }
|
||||
|
||||
const dirs = t_path.split("/");
|
||||
const reconstructedPath: string[] = [];
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (dir === ".") {
|
||||
// Current directory, do nothing
|
||||
continue;
|
||||
} else if (dir === "..") {
|
||||
// Parent directory
|
||||
const res = reconstructedPath.pop();
|
||||
if (res == null) {
|
||||
return null; // Array was empty, invalid path
|
||||
}
|
||||
} else {
|
||||
reconstructedPath.push(dir);
|
||||
}
|
||||
}
|
||||
|
||||
return reconstructedPath.join("/");
|
||||
}
|
175
src/Terminal/determineAllPossibilitiesForTabCompletion.ts
Normal file
175
src/Terminal/determineAllPossibilitiesForTabCompletion.ts
Normal file
@ -0,0 +1,175 @@
|
||||
import { Aliases,
|
||||
GlobalAliases } from "../Alias";
|
||||
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
|
||||
import { Message } from "../Message/Message";
|
||||
import { IPlayer } from "../PersonObjects/IPlayer"
|
||||
import { AllServers } from "../Server/AllServers";
|
||||
|
||||
export function determineAllPossibilitiesForTabCompletion(p: IPlayer, input: string, index: number=0): string[] {
|
||||
let allPos: string[] = [];
|
||||
allPos = allPos.concat(Object.keys(GlobalAliases));
|
||||
const currServ = p.getCurrentServer();
|
||||
const homeComputer = p.getHomeComputer();
|
||||
input = input.toLowerCase();
|
||||
|
||||
//If the command starts with './' and the index == -1, then the user
|
||||
//has input ./partialexecutablename so autocomplete the script or program
|
||||
//Put './' in front of each script/executable
|
||||
if (input.startsWith("./") && index == -1) {
|
||||
//All programs and scripts
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push("./" + currServ.scripts[i].filename);
|
||||
}
|
||||
|
||||
//Programs are on home computer
|
||||
for(var i = 0; i < homeComputer.programs.length; ++i) {
|
||||
allPos.push("./" + homeComputer.programs[i]);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
//Autocomplete the command
|
||||
if (index == -1) {
|
||||
return ["alias", "analyze", "cat", "check", "clear", "cls", "connect", "download", "expr",
|
||||
"free", "hack", "help", "home", "hostname", "ifconfig", "kill", "killall",
|
||||
"ls", "lscpu", "mem", "nano", "ps", "rm", "run", "scan", "scan-analyze",
|
||||
"scp", "sudov", "tail", "theme", "top"].concat(Object.keys(Aliases)).concat(Object.keys(GlobalAliases));
|
||||
}
|
||||
|
||||
if (input.startsWith("buy ")) {
|
||||
let options = [];
|
||||
for (const i in DarkWebItems) {
|
||||
const item = DarkWebItems[i]
|
||||
options.push(item.program);
|
||||
}
|
||||
return options.concat(Object.keys(GlobalAliases));
|
||||
}
|
||||
|
||||
if (input.startsWith("scp ") && index == 1) {
|
||||
for (var iphostname in AllServers) {
|
||||
if (AllServers.hasOwnProperty(iphostname)) {
|
||||
allPos.push(AllServers[iphostname].ip);
|
||||
allPos.push(AllServers[iphostname].hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("scp ") && index == 0) {
|
||||
//All Scripts and lit files
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (var i = 0; i < currServ.messages.length; ++i) {
|
||||
if (!(currServ.messages[i] instanceof Message)) {
|
||||
allPos.push(<string>currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("connect ") || input.startsWith("telnet ")) {
|
||||
//All network connections
|
||||
for (var i = 0; i < currServ.serversOnNetwork.length; ++i) {
|
||||
var serv = AllServers[currServ.serversOnNetwork[i]];
|
||||
if (serv == null) {continue;}
|
||||
allPos.push(serv.ip); //IP
|
||||
allPos.push(serv.hostname); //Hostname
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("kill ") || input.startsWith("tail ") ||
|
||||
input.startsWith("mem ") || input.startsWith("check ")) {
|
||||
//All Scripts
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("nano ")) {
|
||||
//Scripts and text files and .fconf
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
allPos.push(".fconf");
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("rm ")) {
|
||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
for (let i = 0; i < currServ.programs.length; ++i) {
|
||||
allPos.push(currServ.programs[i]);
|
||||
}
|
||||
for (let i = 0; i < currServ.messages.length; ++i) {
|
||||
if (!(currServ.messages[i] instanceof Message)) {
|
||||
allPos.push(<string>currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (let i = 0; i < currServ.contracts.length; ++i) {
|
||||
allPos.push(currServ.contracts[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("run ")) {
|
||||
//All programs, scripts, and contracts
|
||||
for (let i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
|
||||
//Programs are on home computer
|
||||
for (let i = 0; i < homeComputer.programs.length; ++i) {
|
||||
allPos.push(homeComputer.programs[i]);
|
||||
}
|
||||
|
||||
for (let i = 0; i < currServ.contracts.length; ++i) {
|
||||
allPos.push(currServ.contracts[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("cat ")) {
|
||||
for (var i = 0; i < currServ.messages.length; ++i) {
|
||||
if (currServ.messages[i] instanceof Message) {
|
||||
const msg: Message = <Message>currServ.messages[i];
|
||||
allPos.push(msg.filename);
|
||||
} else {
|
||||
allPos.push(<string>currServ.messages[i]);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
return allPos;
|
||||
}
|
||||
|
||||
if (input.startsWith("download ")) {
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.startsWith("ls ")) {
|
||||
for (var i = 0; i < currServ.textFiles.length; ++i) {
|
||||
allPos.push(currServ.textFiles[i].fn);
|
||||
}
|
||||
for (var i = 0; i < currServ.scripts.length; ++i) {
|
||||
allPos.push(currServ.scripts[i].filename);
|
||||
}
|
||||
}
|
||||
return allPos;
|
||||
}
|
Loading…
Reference in New Issue
Block a user