added ns functions

* refactored out logic to be reused by api
This commit is contained in:
phyzical 2022-03-27 15:53:13 +08:00
parent aa3b692aac
commit 7c5097ee83
10 changed files with 256 additions and 70 deletions

@ -5,7 +5,7 @@ import { Programs } from "../Programs/Programs";
import { WHRNG } from "../Casino/RNG";
import React from "react";
import { FactionNames } from "../Faction/data/FactionNames";
import { CityName } from "src/Locations/data/CityNames";
import { CityName } from "../Locations/data/CityNames";
function getRandomBonus(): any {
const bonuses = [

@ -0,0 +1,25 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import { calculateSkill } from "../../PersonObjects/formulas/skill";
function calculateRawDiff(player: IPlayer, stats: number, startingDifficulty: number): number {
const difficulty = startingDifficulty - Math.pow(stats, 0.9) / 250 - player.intelligence / 1600;
if (difficulty < 0) return 0;
if (difficulty > 3) return 3;
return difficulty;
}
export function calculateDifficulty(player: IPlayer, startingSecurityLevel: number): number {
const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma;
return calculateRawDiff(player, totalStats, startingSecurityLevel);
}
export function calculateReward(player: IPlayer, startingSecurityLevel: number): number {
const xpMult = 10 * 60 * 15;
const total =
calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) +
calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) +
calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) +
calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) +
calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult);
return calculateRawDiff(player, total, startingSecurityLevel);
}

@ -0,0 +1,49 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import { BitNodeMultipliers } from "../../BitNode/BitNodeMultipliers";
import { LocationsMetadata } from "../../Locations/data/LocationsMetadata";
export function calculateSellInformationCashReward(
player: IPlayer,
reward: number,
maxLevel: number,
difficulty: number,
): number {
const levelBonus = maxLevel * Math.pow(1.01, maxLevel);
return (
Math.pow(reward + 1, 2) *
Math.pow(difficulty, 3) *
3e3 *
levelBonus *
player.infiltration_sell_mult *
BitNodeMultipliers.InfiltrationMoney
);
}
export function calculateTradeInformationRepReward(
player: IPlayer,
reward: number,
maxLevel: number,
difficulty: number,
): number {
const levelBonus = maxLevel * Math.pow(1.01, maxLevel);
return (
Math.pow(reward + 1, 2) *
Math.pow(difficulty, 3) *
3e3 *
levelBonus *
player.infiltration_sell_mult *
BitNodeMultipliers.InfiltrationMoney
);
}
export function calculateInfiltratorsRepReward(player: IPlayer, difficulty: number): number {
const maxStartingSecurityLevel = LocationsMetadata.reduce((acc, data): number => {
const startingSecurityLevel = data.infiltrationData?.startingSecurityLevel || 0;
return acc > startingSecurityLevel ? acc : startingSecurityLevel;
}, 0);
const baseRepGain = (difficulty / maxStartingSecurityLevel) * 10;
return (baseRepGain + player.infiltration_base_rep_increase) * player.infiltration_rep_mult;
}

@ -1,47 +1,22 @@
import { IPlayer } from "../../PersonObjects/IPlayer";
import React, { useState } from "react";
import { Intro } from "./Intro";
import { Game } from "./Game";
import { Location } from "../../Locations/Location";
import { use } from "../../ui/Context";
import { calculateSkill } from "../../PersonObjects/formulas/skill";
import { calculateDifficulty, calculateReward } from "../formulas/game";
interface IProps {
location: Location;
}
function calcRawDiff(player: IPlayer, stats: number, startingDifficulty: number): number {
const difficulty = startingDifficulty - Math.pow(stats, 0.9) / 250 - player.intelligence / 1600;
if (difficulty < 0) return 0;
if (difficulty > 3) return 3;
return difficulty;
}
function calcDifficulty(player: IPlayer, startingDifficulty: number): number {
const totalStats = player.strength + player.defense + player.dexterity + player.agility + player.charisma;
return calcRawDiff(player, totalStats, startingDifficulty);
}
function calcReward(player: IPlayer, startingDifficulty: number): number {
const xpMult = 10 * 60 * 15;
const total =
calculateSkill(player.strength_exp_mult * xpMult, player.strength_mult) +
calculateSkill(player.defense_exp_mult * xpMult, player.defense_mult) +
calculateSkill(player.agility_exp_mult * xpMult, player.agility_mult) +
calculateSkill(player.dexterity_exp_mult * xpMult, player.dexterity_mult) +
calculateSkill(player.charisma_exp_mult * xpMult, player.charisma_mult);
return calcRawDiff(player, total, startingDifficulty);
}
export function InfiltrationRoot(props: IProps): React.ReactElement {
const player = use.Player();
const router = use.Router();
const [start, setStart] = useState(false);
if (props.location.infiltrationData === undefined) throw new Error("Trying to do infiltration on invalid location.");
const startingDifficulty = props.location.infiltrationData.startingSecurityLevel;
const difficulty = calcDifficulty(player, startingDifficulty);
const reward = calcReward(player, startingDifficulty);
const startingSecurityLevel = props.location.infiltrationData.startingSecurityLevel;
const difficulty = calculateDifficulty(player, startingSecurityLevel);
const reward = calculateReward(player, startingSecurityLevel);
function cancel(): void {
router.toCity();
@ -61,7 +36,7 @@ export function InfiltrationRoot(props: IProps): React.ReactElement {
return (
<Game
StartingDifficulty={startingDifficulty}
StartingDifficulty={startingSecurityLevel}
Difficulty={difficulty}
Reward={reward}
MaxLevel={props.location.infiltrationData.maxClearanceLevel}

@ -10,8 +10,12 @@ import Button from "@mui/material/Button";
import MenuItem from "@mui/material/MenuItem";
import Select, { SelectChangeEvent } from "@mui/material/Select";
import { FactionNames } from "../../Faction/data/FactionNames";
import { LocationsMetadata } from "../../Locations/data/LocationsMetadata";
import { formatNumber } from "../../utils/StringHelperFunctions";
import {
calculateInfiltratorsRepReward,
calculateSellInformationCashReward,
calculateTradeInformationRepReward,
} from "../formulas/victory";
interface IProps {
StartingDifficulty: number;
@ -30,37 +34,13 @@ export function Victory(props: IProps): React.ReactElement {
router.toCity();
}
const levelBonus = props.MaxLevel * Math.pow(1.01, props.MaxLevel);
const repGain =
Math.pow(props.Reward + 1, 1.1) *
Math.pow(props.StartingDifficulty, 1.2) *
30 *
levelBonus *
player.infiltration_trade_mult *
BitNodeMultipliers.InfiltrationRep;
const repGain = calculateTradeInformationRepReward(player, props.Reward, props.MaxLevel, props.StartingDifficulty);
const moneyGain = calculateSellInformationCashReward(player, props.Reward, props.MaxLevel, props.StartingDifficulty);
const infiltrationRepGain = calculateInfiltratorsRepReward(player, props.StartingDifficulty);
const infiltratorFaction = Factions[FactionNames.Infiltrators];
const isMemberOfInfiltrators = infiltratorFaction && infiltratorFaction.isMember;
const moneyGain =
Math.pow(props.Reward + 1, 2) *
Math.pow(props.StartingDifficulty, 3) *
3e3 *
levelBonus *
player.infiltration_sell_mult *
BitNodeMultipliers.InfiltrationMoney;
function calculateInfiltratorsRepReward(): number {
const maxStartingSecurityLevel = LocationsMetadata.reduce((acc, data): number => {
const startingSecurityLevel = data.infiltrationData?.startingSecurityLevel || 0;
return acc > startingSecurityLevel ? acc : startingSecurityLevel;
}, 0);
const baseRepGain = (props.StartingDifficulty / maxStartingSecurityLevel) * 10;
return (baseRepGain + player.infiltration_base_rep_increase) * player.infiltration_rep_mult;
}
function sell(): void {
handleInfiltrators();
player.gainMoney(moneyGain, "infiltration");
@ -81,7 +61,7 @@ export function Victory(props: IProps): React.ReactElement {
function handleInfiltrators(): void {
player.hasCompletedAnInfiltration = true;
if (isMemberOfInfiltrators) {
infiltratorFaction.playerReputation += calculateInfiltratorsRepReward();
infiltratorFaction.playerReputation += infiltrationRepGain;
}
}
@ -96,7 +76,7 @@ export function Victory(props: IProps): React.ReactElement {
You{" "}
{isMemberOfInfiltrators ? (
<>
have gained {formatNumber(calculateInfiltratorsRepReward(), 2)} rep for {FactionNames.Infiltrators} and{" "}
have gained {formatNumber(infiltrationRepGain, 2)} rep for {FactionNames.Infiltrators} and{" "}
</>
) : (
<></>

@ -69,6 +69,11 @@ export const RamCostConstants: IMap<number> = {
ScriptStanekPlace: 5,
ScriptStanekFragmentAt: 2,
ScriptStanekDeleteAt: 0.15,
ScriptInfiltrationCalculateDifficulty: 2.5,
ScriptInfiltrationCalculateRewards: 2.5,
ScriptInfiltrationGetLocations: 5,
ScriptInfiltrationGetInfiltrations: 15,
};
function SF4Cost(cost: number): (player: IPlayer) => number {
@ -376,6 +381,13 @@ export const RamCosts: IMap<any> = {
remove: RamCostConstants.ScriptStanekDeleteAt,
},
infiltration: {
calculateDifficulty: RamCostConstants.ScriptInfiltrationCalculateDifficulty,
calculateRewards: RamCostConstants.ScriptInfiltrationCalculateRewards,
calculateGetLocations: RamCostConstants.ScriptInfiltrationGetLocations,
calculateGetInfiltrations: RamCostConstants.ScriptInfiltrationGetInfiltrations,
},
ui: {
getTheme: 0,
setTheme: 0,

@ -64,6 +64,7 @@ import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
import { NetscriptExtra } from "./NetscriptFunctions/Extra";
import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
import { NetscriptStanek } from "./NetscriptFunctions/Stanek";
import { NetscriptInfiltration } from "./NetscriptFunctions/Infiltration";
import { NetscriptUserInterface } from "./NetscriptFunctions/UserInterface";
import { NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner";
import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract";
@ -78,6 +79,7 @@ import {
Gang as IGang,
Bladeburner as IBladeburner,
Stanek as IStanek,
Infiltration as IInfiltration,
SourceFileLvl,
} from "./ScriptEditor/NetscriptDefinitions";
import { NetscriptSingularity } from "./NetscriptFunctions/Singularity";
@ -96,6 +98,7 @@ interface NS extends INS {
gang: IGang;
bladeburner: IBladeburner;
stanek: IStanek;
infiltration: IInfiltration;
}
export function NetscriptFunctions(workerScript: WorkerScript): NS {
@ -473,6 +476,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
const extra = NetscriptExtra(Player, workerScript, helper);
const hacknet = NetscriptHacknet(Player, workerScript, helper);
const stanek = NetscriptStanek(Player, workerScript, helper);
const infiltration = NetscriptInfiltration(Player, workerScript, helper);
const bladeburner = NetscriptBladeburner(Player, workerScript, helper);
const codingcontract = NetscriptCodingContract(Player, workerScript, helper);
const corporation = NetscriptCorporation(Player, workerScript, helper);
@ -490,6 +494,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
sleeve: sleeve,
corporation: corporation,
stanek: stanek,
infiltration: infiltration,
ui: ui,
formulas: formulas,
stock: stockmarket,

@ -0,0 +1,75 @@
import { INetscriptHelper } from "./INetscriptHelper";
import { IPlayer } from "../PersonObjects/IPlayer";
import { WorkerScript } from "../Netscript/WorkerScript";
import { getRamCost } from "../Netscript/RamCostGenerator";
import {
Infiltration as IInfiltration,
InfiltrationLocation,
InfiltrationReward,
} from "../ScriptEditor/NetscriptDefinitions";
import { Location } from "../Locations/Location";
import { Locations } from "../Locations/Locations";
import { calculateDifficulty, calculateReward } from "../Infiltration/formulas/game";
import {
calculateInfiltratorsRepReward,
calculateSellInformationCashReward,
calculateTradeInformationRepReward,
} from "../Infiltration/formulas/victory";
export function NetscriptInfiltration(
player: IPlayer,
workerScript: WorkerScript,
helper: INetscriptHelper,
): IInfiltration {
const getLocationsWithInfiltrations = Object.values(Locations).filter(
(location: Location) => location.infiltrationData,
);
const calculateInfiltrationData = (location: Location | undefined): InfiltrationLocation => {
if (location === undefined)
throw helper.makeRuntimeErrorMsg(
`infiltration.calculateReward`,
"The provided location does not exist or does not provide infiltrations",
);
if (location.infiltrationData === undefined)
throw helper.makeRuntimeErrorMsg(
`infiltration.calculateReward`,
"The provided location does not exist or does not provide infiltrations",
);
const startingSecurityLevel = location.infiltrationData.startingSecurityLevel;
const difficulty = calculateDifficulty(player, startingSecurityLevel);
const reward = calculateReward(player, startingSecurityLevel);
const maxLevel = location.infiltrationData.maxClearanceLevel;
return {
location: location,
reward: {
tradeRep: calculateTradeInformationRepReward(player, reward, maxLevel, difficulty),
sellCash: calculateSellInformationCashReward(player, reward, maxLevel, difficulty),
infiltratorRep: calculateInfiltratorsRepReward(player, difficulty),
},
difficulty: difficulty,
};
};
return {
calculateDifficulty: function (locationName: string): number {
const location = getLocationsWithInfiltrations.find((infilLocation) => infilLocation.name === locationName);
helper.updateDynamicRam("calculateDifficulty", getRamCost(player, "infiltration", "calculateDifficulty"));
return calculateInfiltrationData(location).difficulty;
},
calculateRewards: function (locationName: string): InfiltrationReward {
const location = getLocationsWithInfiltrations.find((infilLocation) => infilLocation.name === locationName);
helper.updateDynamicRam("calculateReward", getRamCost(player, "infiltration", "calculateReward"));
return calculateInfiltrationData(location).reward;
},
getLocations: function (): Location[] {
helper.updateDynamicRam("getLocations", getRamCost(player, "infiltration", "getLocations"));
return getLocationsWithInfiltrations;
},
getInfiltrations: function (): InfiltrationLocation[] {
helper.updateDynamicRam("getInfiltrations", getRamCost(player, "infiltration", "getInfiltrations"));
return getLocationsWithInfiltrations.map(calculateInfiltrationData);
},
};
}

@ -17,7 +17,7 @@ import { areImportsEquals } from "../Terminal/DirectoryHelpers";
import { IPlayer } from "../PersonObjects/IPlayer";
export interface RamUsageEntry {
type: 'ns' | 'dom' | 'fn' | 'misc';
type: "ns" | "dom" | "fn" | "misc";
name: string;
cost: number;
}
@ -139,7 +139,9 @@ async function parseOnlyRamCalculate(
// Finally, walk the reference map and generate a ram cost. The initial set of keys to scan
// are those that start with __SPECIAL_INITIAL_MODULE__.
let ram = RamCostConstants.ScriptBaseRamCost;
const detailedCosts: RamUsageEntry[] = [{ type: 'misc', name: 'baseCost', cost: RamCostConstants.ScriptBaseRamCost}];
const detailedCosts: RamUsageEntry[] = [
{ type: "misc", name: "baseCost", cost: RamCostConstants.ScriptBaseRamCost },
];
const unresolvedRefs = Object.keys(dependencyMap).filter((s) => s.startsWith(initialModule));
const resolvedRefs = new Set();
while (unresolvedRefs.length > 0) {
@ -149,19 +151,19 @@ async function parseOnlyRamCalculate(
// Check if this is one of the special keys, and add the appropriate ram cost if so.
if (ref === "hacknet" && !resolvedRefs.has("hacknet")) {
ram += RamCostConstants.ScriptHacknetNodesRamCost;
detailedCosts.push({ type: 'ns', name: 'hacknet', cost: RamCostConstants.ScriptHacknetNodesRamCost});
detailedCosts.push({ type: "ns", name: "hacknet", cost: RamCostConstants.ScriptHacknetNodesRamCost });
}
if (ref === "document" && !resolvedRefs.has("document")) {
ram += RamCostConstants.ScriptDomRamCost;
detailedCosts.push({ type: 'dom', name: 'document', cost: RamCostConstants.ScriptDomRamCost});
detailedCosts.push({ type: "dom", name: "document", cost: RamCostConstants.ScriptDomRamCost });
}
if (ref === "window" && !resolvedRefs.has("window")) {
ram += RamCostConstants.ScriptDomRamCost;
detailedCosts.push({ type: 'dom', name: 'window', cost: RamCostConstants.ScriptDomRamCost});
detailedCosts.push({ type: "dom", name: "window", cost: RamCostConstants.ScriptDomRamCost });
}
if (ref === "corporation" && !resolvedRefs.has("corporation")) {
ram += RamCostConstants.ScriptCorporationRamCost;
detailedCosts.push({ type: 'ns', name: 'corporation', cost: RamCostConstants.ScriptCorporationRamCost});
detailedCosts.push({ type: "ns", name: "corporation", cost: RamCostConstants.ScriptCorporationRamCost });
}
resolvedRefs.add(ref);
@ -203,7 +205,7 @@ async function parseOnlyRamCalculate(
// This accounts for namespaces (Bladeburner, CodingCpntract, etc.)
let func;
let refDetail = 'n/a';
let refDetail = "n/a";
if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref];
refDetail = `bladeburner.${ref}`;
@ -213,6 +215,9 @@ async function parseOnlyRamCalculate(
} else if (ref in workerScript.env.vars.stanek) {
func = workerScript.env.vars.stanek[ref];
refDetail = `stanek.${ref}`;
} else if (ref in workerScript.env.vars.infiltration) {
func = workerScript.env.vars.infiltration[ref];
refDetail = `infiltration.${ref}`;
} else if (ref in workerScript.env.vars.gang) {
func = workerScript.env.vars.gang[ref];
refDetail = `gang.${ref}`;
@ -231,12 +236,12 @@ async function parseOnlyRamCalculate(
}
const fnRam = applyFuncRam(func);
ram += fnRam;
detailedCosts.push({ type: 'fn', name: refDetail, cost: fnRam});
detailedCosts.push({ type: "fn", name: refDetail, cost: fnRam });
} catch (error) {
continue;
}
}
return { cost: ram, entries: detailedCosts.filter(e => e.cost > 0) };
return { cost: ram, entries: detailedCosts.filter((e) => e.cost > 0) };
} catch (error) {
// console.info("parse or eval error: ", error);
// This is not unexpected. The user may be editing a script, and it may be in

@ -1,3 +1,5 @@
import { Location } from "src/Locations/Location";
/**
* @public
*/
@ -4119,6 +4121,59 @@ interface Stanek {
remove(rootX: number, rootY: number): boolean;
}
export interface InfiltrationReward {
tradeRep: number;
sellCash: number;
infiltratorRep: number;
}
export interface InfiltrationLocation {
location: Location;
reward: InfiltrationReward;
difficulty: number;
}
/**
* Infiltration API.
* @public
*/
interface Infiltration {
/**
* Calculate the difficulty of performing an infiltration.
* @remarks
* RAM cost: 2.5 GB
*
* @param locationName - name of the location to check.
* @returns the difficulty.
*/
calculateDifficulty(locationName: string): number;
/**
* Calculate the rewards for trading and selling information for completing an infiltration.
* @remarks
* RAM cost: 2.5 GB
*
* @param locationName - name of the location to check.
* @returns the trade reputation, sell cash and infiltrators rep reward.
*/
calculateRewards(locationName: string): InfiltrationReward;
/**
* Get all locations that can be infiltrated.
* @remarks
* RAM cost: 5 GB
*
* @returns all locations that can be infiltrated.
*/
getLocations(): Location[];
/**
* Get all infiltrations with difficulty, location and rewards.
* @remarks
* RAM cost: 15 GB
*
* @returns all infiltrations with difficulty, location and rewards.
*/
getInfiltrations(): InfiltrationLocation[];
}
/**
* User Interface API.
* @public
@ -4268,6 +4323,11 @@ export interface NS extends Singularity {
* RAM cost: 0 GB
*/
readonly stanek: Stanek;
/**
* Namespace for infiltration functions.
* RAM cost: 0 GB
*/
readonly infiltration: Infiltration;
/**
* Namespace for corporation functions.
* RAM cost: 0 GB