bitburner-src/src/NetscriptFunctions/Singularity.ts

1270 lines
49 KiB
TypeScript
Raw Normal View History

import { Player } from "@player";
import { purchaseAugmentation, joinFaction, getFactionAugmentationsFiltered } from "../Faction/FactionHelpers";
import { startWorkerScript } from "../NetscriptWorker";
import { Augmentation } from "../Augmentation/Augmentation";
import { StaticAugmentations } from "../Augmentation/StaticAugmentations";
import { augmentationExists, installAugmentations } from "../Augmentation/AugmentationHelpers";
import { AugmentationNames } from "../Augmentation/data/AugmentationNames";
import { CONSTANTS } from "../Constants";
import { isString } from "../utils/helpers/isString";
import { RunningScript } from "../Script/RunningScript";
2022-07-22 19:16:48 +02:00
import { calculateAchievements } from "../Achievements/Achievements";
import { Singularity as ISingularity } from "@nsdefs";
import { findCrime } from "../Crime/CrimeHelpers";
import { CompanyPositions } from "../Company/CompanyPositions";
import { DarkWebItems } from "../DarkWeb/DarkWebItems";
import { CityName, LocationName, JobName } from "../Enums";
import { Router } from "../ui/GameRoot";
import { SpecialServers } from "../Server/data/SpecialServers";
import { Page } from "../ui/Router";
import { Locations } from "../Locations/Locations";
2022-05-21 00:20:04 +02:00
import { GetServer } from "../Server/AllServers";
import { Programs } from "../Programs/Programs";
import { formatMoney, formatRam, formatReputation } from "../ui/formatNumber";
import { BitNodeMultipliers } from "../BitNode/BitNodeMultipliers";
import { Company } from "../Company/Company";
import { Companies } from "../Company/Companies";
import { companiesMetadata } from "../Company/data/CompaniesMetadata";
import { Factions, factionExists } from "../Faction/Factions";
import { Faction } from "../Faction/Faction";
2022-08-08 19:43:41 +02:00
import { helpers } from "../Netscript/NetscriptHelpers";
import { convertTimeMsToTimeElapsedString } from "../utils/StringHelperFunctions";
2022-05-21 00:20:04 +02:00
import { getServerOnNetwork } from "../Server/ServerHelpers";
import { Terminal } from "../Terminal";
import { calculateHackingTime } from "../Hacking";
import { Server } from "../Server/Server";
import { netscriptCanHack } from "../Hacking/netscriptCanHack";
2022-03-21 04:27:53 +01:00
import { FactionInfos } from "../Faction/FactionInfo";
2023-01-02 19:18:02 +01:00
import { InternalAPI, NetscriptContext, removedFunction } from "../Netscript/APIWrapper";
2022-04-13 23:34:02 +02:00
import { BlackOperationNames } from "../Bladeburner/data/BlackOperationNames";
import { enterBitNode } from "../RedPill";
import { FactionNames } from "../Faction/data/FactionNames";
import { ClassWork } from "../Work/ClassWork";
2022-07-10 07:37:36 +02:00
import { CreateProgramWork, isCreateProgramWork } from "../Work/CreateProgramWork";
import { FactionWork } from "../Work/FactionWork";
import { FactionWorkType, GymType, UniversityClassType } from "../Enums";
import { CompanyWork } from "../Work/CompanyWork";
import { canGetBonus, onExport } from "../ExportBonus";
import { saveObject } from "../SaveObject";
import { calculateCrimeWorkStats } from "../Work/Formulas";
import { findEnumMember } from "../utils/helpers/enum";
import { Engine } from "../engine";
import { checkEnum } from "../utils/helpers/enum";
2022-08-09 21:41:47 +02:00
export function NetscriptSingularity(): InternalAPI<ISingularity> {
2022-08-08 19:43:41 +02:00
const getAugmentation = function (ctx: NetscriptContext, name: string): Augmentation {
if (!augmentationExists(name)) {
2022-08-08 19:43:41 +02:00
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid augmentation: '${name}'`);
}
return StaticAugmentations[name];
};
2022-08-08 19:43:41 +02:00
const getFaction = function (ctx: NetscriptContext, name: string): Faction {
if (!factionExists(name)) {
2022-08-08 19:43:41 +02:00
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid faction name: '${name}`);
}
return Factions[name];
};
2022-08-08 19:43:41 +02:00
const getCompany = function (ctx: NetscriptContext, name: string): Company {
const company = Companies[name];
if (!company) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company name: '${name}'`);
return company;
};
const runAfterReset = function (cbScript: string | null = null) {
//Run a script after reset
2022-03-30 01:49:37 +02:00
if (!cbScript) return;
2022-09-06 15:07:12 +02:00
const home = Player.getHomeComputer();
const script = home.scripts.get(cbScript);
if (!script) return;
const ramUsage = script.getRamUsage(home.scripts);
if (!ramUsage) {
return Terminal.error(`Attempted to launch ${cbScript} after reset but could not calculate ram usage.`);
}
const ramAvailable = home.maxRam - home.ramUsed;
if (ramUsage > ramAvailable + 0.001) return;
// Start script with no args and 1 thread (default).
const runningScriptObj = new RunningScript(script, ramUsage, []);
startWorkerScript(runningScriptObj, home);
};
2023-01-02 19:18:02 +01:00
const singularityAPI: InternalAPI<ISingularity> = {
2022-10-12 15:54:13 +02:00
getOwnedAugmentations: (ctx) => (_purchased) => {
helpers.checkSingularityAccess(ctx);
const purchased = !!_purchased;
const res: string[] = [];
for (let i = 0; i < Player.augmentations.length; ++i) {
res.push(Player.augmentations[i].name);
}
if (purchased) {
for (let i = 0; i < Player.queuedAugmentations.length; ++i) {
res.push(Player.queuedAugmentations[i].name);
}
2022-10-12 15:54:13 +02:00
}
return res;
},
getOwnedSourceFiles: () => () => {
return [...Player.sourceFiles].map(([n, lvl]) => ({ n, lvl }));
2022-07-21 08:13:47 +02:00
},
2022-10-12 15:54:13 +02:00
getAugmentationsFromFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
2022-10-12 15:54:13 +02:00
return getFactionAugmentationsFiltered(faction);
},
getAugmentationPrereq: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug = getAugmentation(ctx, augName);
return aug.prereqs.slice();
},
getAugmentationBasePrice: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug = getAugmentation(ctx, augName);
return aug.baseCost * BitNodeMultipliers.AugmentationMoneyCost;
},
getAugmentationPrice: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug = getAugmentation(ctx, augName);
return aug.getCost().moneyCost;
},
getAugmentationRepReq: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug = getAugmentation(ctx, augName);
return aug.getCost().repCost;
},
getAugmentationStats: (ctx) => (_augName) => {
helpers.checkSingularityAccess(ctx);
const augName = helpers.string(ctx, "augName", _augName);
const aug = getAugmentation(ctx, augName);
return Object.assign({}, aug.mults);
},
purchaseAugmentation: (ctx) => (_facName, _augName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const augName = helpers.string(ctx, "augName", _augName);
const fac = getFaction(ctx, facName);
const aug = getAugmentation(ctx, augName);
2022-10-12 15:54:13 +02:00
const augs = getFactionAugmentationsFiltered(fac);
2022-10-12 15:54:13 +02:00
if (!Player.factions.includes(fac.name)) {
helpers.log(ctx, () => `You can't purchase augmentations from '${facName}' because you aren't a member`);
return false;
}
2022-10-12 15:54:13 +02:00
if (!augs.includes(augName)) {
helpers.log(ctx, () => `Faction '${facName}' does not have the '${augName}' augmentation.`);
return false;
}
2022-10-12 15:54:13 +02:00
const isNeuroflux = aug.name === AugmentationNames.NeuroFluxGovernor;
if (!isNeuroflux) {
for (let j = 0; j < Player.queuedAugmentations.length; ++j) {
if (Player.queuedAugmentations[j].name === aug.name) {
helpers.log(ctx, () => `You already have the '${augName}' augmentation.`);
return false;
}
}
2022-10-12 15:54:13 +02:00
for (let j = 0; j < Player.augmentations.length; ++j) {
if (Player.augmentations[j].name === aug.name) {
helpers.log(ctx, () => `You already have the '${augName}' augmentation.`);
return false;
}
}
2022-10-12 15:54:13 +02:00
}
2022-10-12 15:54:13 +02:00
if (fac.playerReputation < aug.getCost().repCost) {
helpers.log(ctx, () => `You do not have enough reputation with '${fac.name}'.`);
return false;
}
2022-10-12 15:54:13 +02:00
const res = purchaseAugmentation(aug, fac, true);
helpers.log(ctx, () => res);
if (isString(res) && res.startsWith("You purchased")) {
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10);
return true;
} else {
return false;
}
},
softReset: (ctx) => (_cbScript) => {
helpers.checkSingularityAccess(ctx);
const cbScript = _cbScript ? helpers.string(ctx, "cbScript", _cbScript) : "";
2022-10-12 15:54:13 +02:00
helpers.log(ctx, () => "Soft resetting. This will cause this script to be killed");
installAugmentations(true);
if (cbScript) setTimeout(() => runAfterReset(cbScript), 500);
2022-10-12 15:54:13 +02:00
},
installAugmentations: (ctx) => (_cbScript) => {
helpers.checkSingularityAccess(ctx);
const cbScript = _cbScript ? helpers.string(ctx, "cbScript", _cbScript) : "";
2022-10-12 15:54:13 +02:00
if (Player.queuedAugmentations.length === 0) {
helpers.log(ctx, () => "You do not have any Augmentations to be installed.");
return false;
}
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 10);
helpers.log(ctx, () => "Installing Augmentations. This will cause this script to be killed");
installAugmentations();
if (cbScript) setTimeout(() => runAfterReset(cbScript), 500);
2022-10-12 15:54:13 +02:00
},
2022-10-12 15:54:13 +02:00
goToLocation: (ctx) => (_locationName) => {
helpers.checkSingularityAccess(ctx);
const locationName = helpers.string(ctx, "locationName", _locationName);
const location = Object.values(Locations).find((l) => l.name === locationName);
if (!location) {
helpers.log(ctx, () => `No location named ${locationName}`);
return false;
}
if (location.city && Player.city !== location.city) {
helpers.log(ctx, () => `No location named ${locationName} in ${Player.city}`);
return false;
}
if (location.name === LocationName.TravelAgency) {
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Travel);
2022-10-12 15:54:13 +02:00
} else if (location.name === LocationName.WorldStockExchange) {
2022-12-04 09:14:06 +01:00
Router.toPage(Page.StockMarket);
2022-10-12 15:54:13 +02:00
} else {
Router.toLocation(location);
}
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000);
return true;
},
universityCourse:
(ctx) =>
(_universityName, _className, _focus = true) => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
const universityName = helpers.string(ctx, "universityName", _universityName);
const classType = findEnumMember(UniversityClassType, helpers.string(ctx, "className", _className));
if (!classType) {
helpers.log(ctx, () => `Invalid class name: ${_className}.`);
return false;
}
2022-08-08 19:43:41 +02:00
const focus = !!_focus;
2022-09-06 15:07:12 +02:00
const wasFocusing = Player.focus;
switch (universityName.toLowerCase()) {
case LocationName.AevumSummitUniversity.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Aevum) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() => `You cannot study at 'Summit University' because you are not in '${CityName.Aevum}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.gotoLocation(LocationName.AevumSummitUniversity);
break;
case LocationName.Sector12RothmanUniversity.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Sector12) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() => `You cannot study at 'Rothman University' because you are not in '${CityName.Sector12}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.Sector12RothmanUniversity;
break;
case LocationName.VolhavenZBInstituteOfTechnology.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Volhaven) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() => `You cannot study at 'ZB Institute of Technology' because you are not in '${CityName.Volhaven}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.VolhavenZBInstituteOfTechnology;
break;
default:
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Invalid university name: '${universityName}'.`);
return false;
}
2022-09-06 15:07:12 +02:00
Player.startWork(
new ClassWork({
classType,
2022-09-06 15:07:12 +02:00
location: Player.location,
singularity: true,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
helpers.log(ctx, () => `Started ${classType} at ${universityName}`);
return true;
},
2022-10-12 15:54:13 +02:00
gymWorkout:
(ctx) =>
(_gymName, _stat, _focus = true) => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
const gymName = helpers.string(ctx, "gymName", _gymName);
const classType = findEnumMember(GymType, helpers.string(ctx, "stat", _stat));
if (!classType) {
helpers.log(ctx, () => `Invalid stat: ${_stat}.`);
return false;
}
2022-08-08 19:43:41 +02:00
const focus = !!_focus;
2022-09-06 15:07:12 +02:00
const wasFocusing = Player.focus;
switch (gymName.toLowerCase()) {
case LocationName.AevumCrushFitnessGym.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Aevum) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
`You cannot workout at '${LocationName.AevumCrushFitnessGym}' because you are not in '${CityName.Aevum}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.AevumCrushFitnessGym;
break;
case LocationName.AevumSnapFitnessGym.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Aevum) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
`You cannot workout at '${LocationName.AevumSnapFitnessGym}' because you are not in '${CityName.Aevum}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.AevumSnapFitnessGym;
break;
case LocationName.Sector12IronGym.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Sector12) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
`You cannot workout at '${LocationName.Sector12IronGym}' because you are not in '${CityName.Sector12}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.Sector12IronGym;
break;
case LocationName.Sector12PowerhouseGym.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Sector12) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
`You cannot workout at '${LocationName.Sector12PowerhouseGym}' because you are not in '${CityName.Sector12}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.Sector12PowerhouseGym;
break;
case LocationName.VolhavenMilleniumFitnessGym.toLowerCase():
2022-09-06 15:07:12 +02:00
if (Player.city != CityName.Volhaven) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
`You cannot workout at '${LocationName.VolhavenMilleniumFitnessGym}' because you are not in '${CityName.Volhaven}'.`,
);
return false;
}
2022-09-06 15:07:12 +02:00
Player.location = LocationName.VolhavenMilleniumFitnessGym;
break;
default:
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Invalid gym name: ${gymName}. gymWorkout() failed`);
return false;
}
Player.startWork(new ClassWork({ classType, location: Player.location, singularity: true }));
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
helpers.log(ctx, () => `Started training ${classType} at ${gymName}`);
return true;
},
2022-10-12 15:54:13 +02:00
travelToCity: (ctx) => (_cityName) => {
helpers.checkSingularityAccess(ctx);
const cityName = helpers.city(ctx, "cityName", _cityName);
switch (cityName) {
case CityName.Aevum:
case CityName.Chongqing:
case CityName.Sector12:
case CityName.NewTokyo:
case CityName.Ishima:
case CityName.Volhaven:
if (Player.money < CONSTANTS.TravelCost) {
helpers.log(ctx, () => "Not enough money to travel.");
return false;
}
Player.loseMoney(CONSTANTS.TravelCost, "other");
Player.city = cityName;
helpers.log(ctx, () => `Traveled to ${cityName}`);
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 50000);
return true;
2022-10-12 15:54:13 +02:00
default:
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid city name: '${cityName}'.`);
}
},
2022-10-12 15:54:13 +02:00
purchaseTor: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
if (Player.hasTorRouter()) {
helpers.log(ctx, () => "You already have a TOR router!");
return true;
2022-10-12 15:54:13 +02:00
}
2022-10-12 15:54:13 +02:00
if (Player.money < CONSTANTS.TorRouterCost) {
helpers.log(ctx, () => "You cannot afford to purchase a Tor router.");
return false;
}
Player.loseMoney(CONSTANTS.TorRouterCost, "other");
2022-10-12 15:54:13 +02:00
const darkweb = GetServer(SpecialServers.DarkWeb);
if (!darkweb) throw helpers.makeRuntimeErrorMsg(ctx, "DarkWeb was not a server but should have been");
2022-10-12 15:54:13 +02:00
Player.getHomeComputer().serversOnNetwork.push(darkweb.hostname);
darkweb.serversOnNetwork.push(Player.getHomeComputer().hostname);
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 500);
helpers.log(ctx, () => "You have purchased a Tor router!");
return true;
},
purchaseProgram: (ctx) => (_programName) => {
helpers.checkSingularityAccess(ctx);
const programName = helpers.string(ctx, "programName", _programName).toLowerCase();
2022-10-12 15:54:13 +02:00
if (!Player.hasTorRouter()) {
helpers.log(ctx, () => "You do not have the TOR router.");
return false;
}
2022-10-12 15:54:13 +02:00
const item = Object.values(DarkWebItems).find((i) => i.program.toLowerCase() === programName);
if (item == null) {
helpers.log(ctx, () => `Invalid program name: '${programName}.`);
return false;
}
2022-10-12 15:54:13 +02:00
if (Player.money < item.price) {
helpers.log(ctx, () => `Not enough money to purchase '${item.program}'. Need ${formatMoney(item.price)}`);
2022-10-12 15:54:13 +02:00
return false;
}
if (Player.hasProgram(item.program)) {
helpers.log(ctx, () => `You already have the '${item.program}' program`);
return true;
2022-10-12 15:54:13 +02:00
}
2022-10-12 15:54:13 +02:00
Player.getHomeComputer().pushProgram(item.program);
// Cancel if the program is in progress of writing
if (isCreateProgramWork(Player.currentWork) && Player.currentWork.programName === item.program) {
Player.finishWork(true);
}
2022-10-12 15:54:13 +02:00
Player.loseMoney(item.price, "other");
helpers.log(
ctx,
() => `You have purchased the '${item.program}' program. The new program can be found on your home computer.`,
);
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain / 5000);
return true;
},
getCurrentServer: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return Player.getCurrentServer().hostname;
},
connect: (ctx) => (_hostname) => {
helpers.checkSingularityAccess(ctx);
const hostname = helpers.string(ctx, "hostname", _hostname);
if (!hostname) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid hostname: '${hostname}'`);
}
2022-10-12 15:54:13 +02:00
const target = GetServer(hostname);
if (target == null) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid hostname: '${hostname}'`);
}
//Home case
if (hostname === "home") {
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = Player.getHomeComputer().hostname;
Player.getCurrentServer().isConnectedTo = true;
Terminal.setcwd("/");
return true;
}
2022-10-12 15:54:13 +02:00
//Adjacent server case
const server = Player.getCurrentServer();
for (let i = 0; i < server.serversOnNetwork.length; i++) {
const other = getServerOnNetwork(server, i);
if (other === null) continue;
if (other.hostname == hostname) {
2022-09-06 15:07:12 +02:00
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = target.hostname;
Player.getCurrentServer().isConnectedTo = true;
Terminal.setcwd("/");
return true;
}
2022-10-12 15:54:13 +02:00
}
2022-10-12 15:54:13 +02:00
//Backdoor case
const other = GetServer(hostname);
if (other !== null && other instanceof Server && other.backdoorInstalled) {
Player.getCurrentServer().isConnectedTo = false;
Player.currentServer = target.hostname;
Player.getCurrentServer().isConnectedTo = true;
Terminal.setcwd("/");
return true;
}
//Failure case
return false;
},
manualHack: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
const server = Player.getCurrentServer();
return helpers.hack(ctx, server.hostname, true);
},
installBackdoor: (ctx) => async (): Promise<void> => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
2022-09-06 15:07:12 +02:00
const baseserver = Player.getCurrentServer();
2022-05-26 02:19:42 +02:00
if (!(baseserver instanceof Server)) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => "cannot backdoor this kind of server");
2022-05-26 02:19:42 +02:00
return Promise.resolve();
}
const server = baseserver;
2022-09-06 15:07:12 +02:00
const installTime = (calculateHackingTime(server, Player) / 4) * 1000;
2022-05-26 02:19:42 +02:00
// No root access or skill level too low
2022-09-06 15:07:12 +02:00
const canHack = netscriptCanHack(server);
2022-05-26 02:19:42 +02:00
if (!canHack.res) {
2022-08-08 19:43:41 +02:00
throw helpers.makeRuntimeErrorMsg(ctx, canHack.msg || "");
2022-05-26 02:19:42 +02:00
}
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
2022-05-26 02:19:42 +02:00
() => `Installing backdoor on '${server.hostname}' in ${convertTimeMsToTimeElapsedString(installTime, true)}`,
);
2022-08-08 19:43:41 +02:00
return helpers.netscriptDelay(ctx, installTime).then(function () {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Successfully installed backdoor on '${server.hostname}'`);
2022-05-26 02:19:42 +02:00
server.backdoorInstalled = true;
2022-05-26 02:19:42 +02:00
if (SpecialServers.WorldDaemon === server.hostname) {
return Router.toBitVerse(false, false);
2022-05-26 02:19:42 +02:00
}
// Manunally check for faction invites
Engine.Counters.checkFactionInvitations = 0;
Engine.checkCounters();
2022-05-26 02:19:42 +02:00
});
},
2022-10-12 15:54:13 +02:00
isFocused: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return Player.focus;
},
setFocus: (ctx) => (_focus) => {
helpers.checkSingularityAccess(ctx);
const focus = !!_focus;
if (Player.currentWork === null) {
throw helpers.makeRuntimeErrorMsg(ctx, "Not currently working");
}
2022-10-12 15:54:13 +02:00
if (!Player.focus && focus) {
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
2022-10-12 15:54:13 +02:00
return true;
} else if (Player.focus && !focus) {
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
2022-10-12 15:54:13 +02:00
return true;
}
return false;
},
hospitalize: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
if (Player.currentWork || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse) {
helpers.log(ctx, () => "Cannot go to the hospital because the player is busy.");
return;
}
Player.hospitalize();
},
isBusy: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return Player.currentWork !== null || Router.page() === Page.Infiltration || Router.page() === Page.BitVerse;
},
stopAction: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
const wasWorking = Player.currentWork !== null;
Player.finishWork(true);
return wasWorking;
},
upgradeHomeCores: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
// Check if we're at max cores
const homeComputer = Player.getHomeComputer();
if (homeComputer.cpuCores >= 8) {
helpers.log(ctx, () => `Your home computer is at max cores.`);
return false;
}
2022-10-12 15:54:13 +02:00
const cost = Player.getUpgradeHomeCoresCost();
if (Player.money < cost) {
helpers.log(ctx, () => `You don't have enough money. Need ${formatMoney(cost)}`);
2022-10-12 15:54:13 +02:00
return false;
}
2022-10-12 15:54:13 +02:00
homeComputer.cpuCores += 1;
Player.loseMoney(cost, "servers");
2021-12-16 18:02:46 +01:00
2022-10-12 15:54:13 +02:00
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 2);
helpers.log(
ctx,
() => `Purchased an additional core for home computer! It now has ${homeComputer.cpuCores} cores.`,
);
return true;
},
getUpgradeHomeCoresCost: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
return Player.getUpgradeHomeCoresCost();
},
upgradeHomeRam: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
// Check if we're at max RAM
const homeComputer = Player.getHomeComputer();
if (homeComputer.maxRam >= CONSTANTS.HomeComputerMaxRam) {
helpers.log(ctx, () => `Your home computer is at max RAM.`);
return false;
}
2022-10-12 15:54:13 +02:00
const cost = Player.getUpgradeHomeRamCost();
if (Player.money < cost) {
helpers.log(ctx, () => `You don't have enough money. Need ${formatMoney(cost)}`);
2022-10-12 15:54:13 +02:00
return false;
}
2022-10-12 15:54:13 +02:00
homeComputer.maxRam *= 2;
Player.loseMoney(cost, "servers");
2022-10-12 15:54:13 +02:00
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 2);
helpers.log(
ctx,
() => `Purchased additional RAM for home computer! It now has ${formatRam(homeComputer.maxRam)} of RAM.`,
2022-10-12 15:54:13 +02:00
);
return true;
},
getUpgradeHomeRamCost: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
return Player.getUpgradeHomeRamCost();
},
getCompanyPositions: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
// Make sure its a valid company
if (companyName == null || companyName === "" || !Companies[companyName]) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`);
}
return Object.entries(CompanyPositions)
.filter((_position) => Companies[companyName].hasPosition(_position[0]))
.map((_position) => _position[1]["name"]);
},
getCompanyPositionInfo: (ctx) => (_companyName, _positionName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const positionName = helpers.string(ctx, "positionName", _positionName);
// Make sure its a valid company
if (!(companyName in Companies)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`);
}
// Make sure its a valid position
if (!checkEnum(JobName, positionName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid position: '${positionName}'`);
}
if (!Companies[companyName].hasPosition(positionName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `Company '${companyName}' does not have position '${positionName}'`);
}
const c = CompanyPositions[positionName];
const n = companiesMetadata.filter((company) => company.name === companyName)[0];
const res = {
name: CompanyPositions[positionName].name,
nextPosition: CompanyPositions[positionName].nextPosition,
salary: CompanyPositions[positionName].baseSalary * n.salaryMultiplier,
requiredReputation: CompanyPositions[positionName].requiredReputation,
requiredSkills: {
hacking: c.requiredHacking > 0 ? c.requiredHacking + n.jobStatReqOffset : 0,
strength: c.requiredStrength > 0 ? c.requiredStrength + n.jobStatReqOffset : 0,
defense: c.requiredDefense > 0 ? c.requiredDefense + n.jobStatReqOffset : 0,
dexterity: c.requiredDexterity > 0 ? c.requiredDexterity + n.jobStatReqOffset : 0,
agility: c.requiredAgility > 0 ? c.requiredAgility + n.jobStatReqOffset : 0,
charisma: c.requiredCharisma > 0 ? c.requiredCharisma + n.jobStatReqOffset : 0,
intelligence: 0,
},
};
return res;
},
2022-10-12 15:54:13 +02:00
workForCompany:
(ctx) =>
(_companyName, _focus = true) => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const focus = !!_focus;
// Make sure its a valid company
if (companyName == null || companyName === "" || !Companies[companyName]) {
throw helpers.makeRuntimeErrorMsg(ctx, `Invalid company: '${companyName}'`);
}
2022-10-09 07:25:31 +02:00
// Make sure player is actually employed at the company
2022-09-06 15:07:12 +02:00
if (!Object.keys(Player.jobs).includes(companyName)) {
throw helpers.makeRuntimeErrorMsg(ctx, `You do not have a job at: '${companyName}'`);
}
// Check to make sure company position data is valid
2022-09-06 15:07:12 +02:00
const companyPositionName = Player.jobs[companyName];
const companyPosition = CompanyPositions[companyPositionName];
if (companyPositionName === "" || !companyPosition) {
throw helpers.makeRuntimeErrorMsg(ctx, `You do not have a job`);
}
2022-09-06 15:07:12 +02:00
const wasFocused = Player.focus;
2022-09-06 15:07:12 +02:00
Player.startWork(
new CompanyWork({
singularity: true,
companyName: companyName,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocused) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
2022-10-09 07:25:31 +02:00
helpers.log(ctx, () => `Began working at '${companyName}' with position '${companyPositionName}'`);
return true;
},
2022-10-12 15:54:13 +02:00
applyToCompany: (ctx) => (_companyName, _field) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const field = helpers.string(ctx, "field", _field);
getCompany(ctx, companyName);
Player.location = companyName as LocationName;
let res;
switch (field.toLowerCase()) {
case "software":
res = Player.applyForSoftwareJob(true);
break;
case "software consultant":
res = Player.applyForSoftwareConsultantJob(true);
break;
case "it":
res = Player.applyForItJob(true);
break;
case "security engineer":
res = Player.applyForSecurityEngineerJob(true);
break;
case "network engineer":
res = Player.applyForNetworkEngineerJob(true);
break;
case "business":
res = Player.applyForBusinessJob(true);
break;
case "business consultant":
res = Player.applyForBusinessConsultantJob(true);
break;
case "security":
res = Player.applyForSecurityJob(true);
break;
case "agent":
res = Player.applyForAgentJob(true);
break;
case "employee":
res = Player.applyForEmployeeJob(true);
break;
case "part-time employee":
res = Player.applyForPartTimeEmployeeJob(true);
break;
case "waiter":
res = Player.applyForWaiterJob(true);
break;
case "part-time waiter":
res = Player.applyForPartTimeWaiterJob(true);
break;
default:
helpers.log(ctx, () => `Invalid job: '${field}'.`);
return false;
2022-10-12 15:54:13 +02:00
}
// TODO https://github.com/danielyxie/bitburner/issues/1378
// The player object's applyForJob function can return string with special error messages
// if (isString(res)) {
// helpers.log(ctx,"applyToCompany",()=> res);
// return false;
// }
if (res) {
helpers.log(
ctx,
() => `You were offered a new job at '${companyName}' with position '${Player.jobs[companyName]}'`,
);
} else {
helpers.log(ctx, () => `You failed to get a new job/promotion at '${companyName}' in the '${field}' field.`);
}
return res;
},
quitJob: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
Player.quitJob(companyName);
},
getCompanyRep: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const company = getCompany(ctx, companyName);
return company.playerReputation;
},
getCompanyFavor: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const company = getCompany(ctx, companyName);
return company.favor;
},
getCompanyFavorGain: (ctx) => (_companyName) => {
helpers.checkSingularityAccess(ctx);
const companyName = helpers.string(ctx, "companyName", _companyName);
const company = getCompany(ctx, companyName);
return company.getFavorGain();
},
checkFactionInvitations: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
// Manually trigger a check for faction invites
Engine.Counters.checkFactionInvitations = 0;
Engine.checkCounters();
2022-10-12 15:54:13 +02:00
// Make a copy of player.factionInvitations
return Player.factionInvitations.slice();
},
joinFaction: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
getFaction(ctx, facName);
if (!Player.factionInvitations.includes(facName)) {
helpers.log(ctx, () => `You have not been invited by faction '${facName}'`);
return false;
}
const fac = Factions[facName];
joinFaction(fac);
// Update Faction Invitation list to account for joined + banned factions
for (let i = 0; i < Player.factionInvitations.length; ++i) {
if (Player.factionInvitations[i] == facName || Factions[Player.factionInvitations[i]].isBanned) {
Player.factionInvitations.splice(i, 1);
i--;
}
2022-10-12 15:54:13 +02:00
}
Player.gainIntelligenceExp(CONSTANTS.IntelligenceSingFnBaseExpGain * 5);
helpers.log(ctx, () => `Joined the '${facName}' faction.`);
return true;
},
workForFaction:
(ctx) =>
(_facName, _type, _focus = true) => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const type = helpers.string(ctx, "type", _type);
const focus = !!_focus;
const faction = getFaction(ctx, facName);
// if the player is in a gang and the target faction is any of the gang faction, fail
if (Player.gang && faction.name === Player.getGangFaction().name) {
2022-09-06 15:07:12 +02:00
helpers.log(ctx, () => `You can't work for '${facName}' because youre managing a gang for it`);
return false;
}
2022-09-06 15:07:12 +02:00
if (!Player.factions.includes(facName)) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `You are not a member of '${facName}'`);
return false;
}
2022-09-06 15:07:12 +02:00
const wasFocusing = Player.focus;
switch (type.toLowerCase()) {
case "hacking":
case "hacking contracts":
case "hackingcontracts":
if (!FactionInfos[faction.name].offerHackingWork) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with hacking contracts.`);
return false;
}
2022-09-06 15:07:12 +02:00
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.hacking,
faction: faction.name,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Started carrying out hacking contracts for '${faction.name}'`);
return true;
case "field":
case "fieldwork":
case "field work":
if (!FactionInfos[faction.name].offerFieldWork) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with field missions.`);
return false;
}
2022-09-06 15:07:12 +02:00
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.field,
faction: faction.name,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Started carrying out field missions for '${faction.name}'`);
return true;
case "security":
case "securitywork":
case "security work":
if (!FactionInfos[faction.name].offerSecurityWork) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Faction '${faction.name}' do not need help with security work.`);
return false;
}
2022-09-06 15:07:12 +02:00
Player.startWork(
new FactionWork({
singularity: true,
factionWorkType: FactionWorkType.security,
faction: faction.name,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Started carrying out security work for '${faction.name}'`);
return true;
default:
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Invalid work type: '${type}`);
return false;
}
},
2022-10-12 15:54:13 +02:00
getFactionRep: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
return faction.playerReputation;
},
getFactionFavor: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
return faction.favor;
},
getFactionFavorGain: (ctx) => (_facName) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const faction = getFaction(ctx, facName);
return faction.getFavorGain();
},
donateToFaction: (ctx) => (_facName, _amt) => {
helpers.checkSingularityAccess(ctx);
const facName = helpers.string(ctx, "facName", _facName);
const amt = helpers.number(ctx, "amt", _amt);
const faction = getFaction(ctx, facName);
if (!Player.factions.includes(faction.name)) {
helpers.log(ctx, () => `You can't donate to '${facName}' because you aren't a member`);
return false;
}
if (Player.gang && faction.name === Player.getGangFaction().name) {
helpers.log(ctx, () => `You can't donate to '${facName}' because youre managing a gang for it`);
return false;
}
if (faction.name === FactionNames.ChurchOfTheMachineGod || faction.name === FactionNames.Bladeburners) {
helpers.log(ctx, () => `You can't donate to '${facName}' because they do not accept donations`);
return false;
}
if (typeof amt !== "number" || amt <= 0 || isNaN(amt)) {
helpers.log(ctx, () => `Invalid donation amount: '${amt}'.`);
return false;
}
if (Player.money < amt) {
helpers.log(ctx, () => `You do not have enough money to donate ${formatMoney(amt)} to '${facName}'`);
2022-10-12 15:54:13 +02:00
return false;
}
const repNeededToDonate = Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction);
if (faction.favor < repNeededToDonate) {
2022-08-08 21:51:50 +02:00
helpers.log(
ctx,
() =>
2022-10-12 15:54:13 +02:00
`You do not have enough favor to donate to this faction. Have ${faction.favor}, need ${repNeededToDonate}`,
);
2022-10-12 15:54:13 +02:00
return false;
}
const repGain = (amt / CONSTANTS.DonateMoneyToRepDivisor) * Player.mults.faction_rep;
faction.playerReputation += repGain;
Player.loseMoney(amt, "other");
helpers.log(ctx, () => `${formatMoney(amt)} donated to '${facName}' for ${formatReputation(repGain)} reputation`);
2022-10-12 15:54:13 +02:00
return true;
},
createProgram:
(ctx) =>
(_programName, _focus = true) => {
2022-08-08 19:43:41 +02:00
helpers.checkSingularityAccess(ctx);
const programName = helpers.string(ctx, "programName", _programName).toLowerCase();
const focus = !!_focus;
2022-09-06 15:07:12 +02:00
const wasFocusing = Player.focus;
const p = Object.values(Programs).find((p) => p.name.toLowerCase() === programName);
if (p == null) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `The specified program does not exist: '${programName}`);
return false;
}
2022-09-06 15:07:12 +02:00
if (Player.hasProgram(p.name)) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `You already have the '${p.name}' program`);
return false;
}
const create = p.create;
if (create === null) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `You cannot create the '${p.name}' program`);
return false;
}
2022-09-06 15:07:12 +02:00
if (!create.req()) {
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Hacking level is too low to create '${p.name}' (level ${create.level} req)`);
return false;
}
2022-09-06 15:07:12 +02:00
Player.startWork(
2022-07-10 07:37:36 +02:00
new CreateProgramWork({
programName: p.name,
singularity: true,
}),
);
if (focus) {
2022-09-06 15:07:12 +02:00
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
} else if (wasFocusing) {
2022-09-06 15:07:12 +02:00
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
}
2022-08-08 21:51:50 +02:00
helpers.log(ctx, () => `Began creating program: '${programName}'`);
return true;
},
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
commitCrime: (ctx) => (_crimeType, _focus) => {
helpers.checkSingularityAccess(ctx);
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
const focus = _focus === undefined ? true : !!_focus;
const wasFocusing = Player.focus;
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
if (Player.currentWork !== null) Player.finishWork(true);
Player.gotoLocation(LocationName.Slums);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
helpers.log(ctx, () => `Attempting to commit ${crime.type}...`);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
const crimeTime = crime.commit(1, ctx.workerScript);
if (focus) {
Player.startFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Work);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
} else if (wasFocusing) {
Player.stopFocusing();
2022-12-04 09:14:06 +01:00
Router.toPage(Page.Terminal);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
}
return crimeTime;
},
getCrimeChance: (ctx) => (_crimeType) => {
2022-10-12 15:54:13 +02:00
helpers.checkSingularityAccess(ctx);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
2022-10-12 15:54:13 +02:00
return crime.successRate(Player);
},
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
getCrimeStats: (ctx) => (_crimeType) => {
2022-10-12 15:54:13 +02:00
helpers.checkSingularityAccess(ctx);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
const crimeType = helpers.string(ctx, "crimeType", _crimeType);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
// If input isn't a crimeType, use search using roughname.
const crime = findCrime(crimeType);
WIP: Crimes streamlining. (#138) * streamline crimes * Crimes object is now indexed by CrimeType enum instead of an entirely new set of keys that aren't used for anything else. This eliminated a lot of instances of iterating to find the right crime for a given CrimeType. * Removed unused `None` CrimeType which allowed typing Crimes as a Record<CrimeType, Crime>. * Added slums tooltip text as a crime property, to allow streamlining slums. * Refactor slums location - removed repetitive code, rerenders 1/sec to update chances * Fix bugged descriptive text when sleeve is committing a crime (was "is attempting to DRUGS", now uses correct text e.g. "to deal drugs"). * Remove unused and now unneeded NewCrimeType enum. Values were identical to existing CrimeType values after removing unused None. * Add CrimeType enum in NetscriptDefinition.d.ts * Also update broken ToastVariant type. Better support for enums in player scripts. * Still todo is modifying some NS functions to expect CrimeType as input (rough crime names will continue to work to avoid breaking scripts) * Expect enum use for crime functions Affected functions: * ns.singularity.commitCrime * ns.singularity.getCrimeChance * ns.singularity.getCrimeStats * ns.sleeve.setToCommitCrime * formulas.work.crimeGains (param type only) - Affected functions still will fall back to rough names, except formulas.work.crimeGains which already only accepted the enum members. - Some documentation changes: * examples updated to use uppercase expected form. * Note on sleeve.setToCommitCrime that it only accepts exact matches removed. It already, and still does, accept any rough crime name (but the enum is expected input). * note about needing to use isBusy to schedule crimes remove - crimes autoloop now. * Since expected string inputs are documented directly on the type, removed list of crimes from sleeve.setToCommitCrimes
2022-10-21 17:57:37 +02:00
if (crime == null) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid crime: '${crimeType}'`);
2022-03-21 03:49:46 +01:00
const crimeStatsWithMultipliers = calculateCrimeWorkStats(Player, crime);
2022-10-12 15:54:13 +02:00
return Object.assign({}, crime, {
money: crimeStatsWithMultipliers.money,
reputation: crimeStatsWithMultipliers.reputation,
hacking_exp: crimeStatsWithMultipliers.hackExp,
strength_exp: crimeStatsWithMultipliers.strExp,
defense_exp: crimeStatsWithMultipliers.defExp,
dexterity_exp: crimeStatsWithMultipliers.dexExp,
agility_exp: crimeStatsWithMultipliers.agiExp,
charisma_exp: crimeStatsWithMultipliers.chaExp,
intelligence_exp: crimeStatsWithMultipliers.intExp,
});
},
getDarkwebPrograms: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-10-12 15:54:13 +02:00
// If we don't have Tor, log it and return [] (empty list)
if (!Player.hasTorRouter()) {
helpers.log(ctx, () => "You do not have the TOR router.");
return [];
}
return Object.values(DarkWebItems).map((p) => p.program);
},
getDarkwebProgramCost: (ctx) => (_programName) => {
helpers.checkSingularityAccess(ctx);
const programName = helpers.string(ctx, "programName", _programName).toLowerCase();
// If we don't have Tor, log it and return -1
if (!Player.hasTorRouter()) {
helpers.log(ctx, () => "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;
}
2022-03-21 03:49:46 +01:00
2022-10-12 15:54:13 +02:00
const item = Object.values(DarkWebItems).find((i) => i.program.toLowerCase() === programName);
2022-10-12 15:54:13 +02:00
// 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 helpers.makeRuntimeErrorMsg(
ctx,
`No such exploit ('${programName}') found on the darkweb! ` +
`\nThis function is not case-sensitive. Did you perhaps forget .exe at the end?`,
2022-10-12 15:54:13 +02:00
);
}
2022-03-21 03:49:46 +01:00
2022-10-12 15:54:13 +02:00
if (Player.hasProgram(item.program)) {
helpers.log(ctx, () => `You already have the '${item.program}' program`);
return 0;
}
return item.price;
},
2022-04-13 23:34:02 +02:00
b1tflum3:
(ctx) =>
2022-10-12 15:54:13 +02:00
(_nextBN, _callbackScript = "") => {
helpers.checkSingularityAccess(ctx);
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
const callbackScript = helpers.string(ctx, "callbackScript", _callbackScript);
helpers.checkSingularityAccess(ctx);
enterBitNode(true, Player.bitNodeN, nextBN);
if (callbackScript)
setTimeout(() => {
runAfterReset(callbackScript);
}, 0);
},
2022-04-13 23:34:02 +02:00
destroyW0r1dD43m0n:
(ctx) =>
2022-10-12 15:54:13 +02:00
(_nextBN, _callbackScript = "") => {
helpers.checkSingularityAccess(ctx);
const nextBN = helpers.number(ctx, "nextBN", _nextBN);
if (nextBN > 13 || nextBN < 1 || !Number.isInteger(nextBN)) {
throw new Error(`Invalid bitnode specified: ${_nextBN}`);
}
2022-10-12 15:54:13 +02:00
const callbackScript = helpers.string(ctx, "callbackScript", _callbackScript);
const wd = GetServer(SpecialServers.WorldDaemon);
if (!(wd instanceof Server)) throw new Error("WorldDaemon was not a normal server. This is a bug contact dev.");
const hackingRequirements = () => {
if (Player.skills.hacking < wd.requiredHackingSkill) return false;
if (!wd.hasAdminRights) return false;
return true;
};
const bladeburnerRequirements = () => {
if (!Player.bladeburner) return false;
return Player.bladeburner.blackops[BlackOperationNames.OperationDaedalus];
};
if (!hackingRequirements() && !bladeburnerRequirements()) {
helpers.log(ctx, () => "Requirements not met to destroy the world daemon");
return;
}
2022-07-22 19:28:51 +02:00
2022-10-12 15:54:13 +02:00
wd.backdoorInstalled = true;
calculateAchievements();
enterBitNode(false, Player.bitNodeN, nextBN);
if (callbackScript)
setTimeout(() => {
runAfterReset(callbackScript);
}, 0);
},
getCurrentWork: () => () => {
2022-09-06 15:07:12 +02:00
if (!Player.currentWork) return null;
return Player.currentWork.APICopy();
2022-07-26 21:30:12 +02:00
},
exportGame: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
2022-09-23 14:27:16 +02:00
onExport();
return saveObject.exportGame();
},
exportGameBonus: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return canGetBonus();
},
getLastAugReset: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return Player.lastAugReset;
},
getLastNodeReset: (ctx) => () => {
helpers.checkSingularityAccess(ctx);
return Player.lastNodeReset;
},
};
2023-01-02 19:18:02 +01:00
Object.assign(singularityAPI, {
getAugmentationCost: removedFunction(
"v2.2.0",
"singularity.getAugmentationPrice and singularity.getAugmentationRepReq",
),
});
return singularityAPI;
}