Fixed documentation typos in v0.47.0

This commit is contained in:
danielyxie 2019-05-17 15:51:28 -07:00
parent 95c928afc9
commit a2551f98c2
8 changed files with 117 additions and 79 deletions

@ -16,8 +16,6 @@ You can use the Singularity Functions in other BitNodes if and only if you have
Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to
level 3, then you will be able to access all of the Singularity Functions. level 3, then you will be able to access all of the Singularity Functions.
Note that Singularity Functions require twice as much RAM outside of BitNode-4
.. toctree:: .. toctree::
:caption: Functions: :caption: Functions:

@ -4,7 +4,7 @@ getStockSaleGain() Netscript Function
.. js:function:: getStockSaleGain(sym, shares, posType) .. js:function:: getStockSaleGain(sym, shares, posType)
:param string sym: Stock symbol :param string sym: Stock symbol
:param number shares: Number of shares to purchase :param number shares: Number of shares to sell
:param string posType: Specifies whether the order is a "Long" or "Short" position. :param string posType: Specifies whether the order is a "Long" or "Short" position.
The values "L" or "S" can also be used. The values "L" or "S" can also be used.
:RAM cost: 2 GB :RAM cost: 2 GB

@ -1,3 +1,13 @@
/**
* Generic helper/utility functions for the Hacknet mechanic:
* - Purchase nodes/upgrades
* - Calculating maximum number of upgrades
* - Processing Hacknet earnings
* - Updating Hash Manager capacity
* - Purchasing hash upgrades
*
* TODO Should probably split the different types of functions into their own modules
*/
import { import {
HacknetNode, HacknetNode,
BaseCostForHacknetNode, BaseCostForHacknetNode,
@ -26,7 +36,7 @@ import {
ITutorial ITutorial
} from "../InteractiveTutorial"; } from "../InteractiveTutorial";
import { Player } from "../Player"; import { Player } from "../Player";
import { AddToAllServers, AllServers } from "../Server/AllServers"; import { AllServers } from "../Server/AllServers";
import { GetServerByHostname } from "../Server/ServerHelpers"; import { GetServerByHostname } from "../Server/ServerHelpers";
import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; import { SourceFileFlags } from "../SourceFile/SourceFileFlags";
import { Page, routing } from "../ui/navigationTracking"; import { Page, routing } from "../ui/navigationTracking";
@ -115,7 +125,7 @@ export function getCostOfNextHacknetServer() {
return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult; return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult;
} }
//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node // Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level
export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`);
@ -149,6 +159,7 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM
export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`);
@ -177,6 +188,7 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores
export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`);
@ -193,7 +205,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
return levelsToMax; return levelsToMax;
} }
//Use a binary search to find the max possible number of upgrades // Use a binary search to find the max possible number of upgrades
while (min <= max) { while (min <= max) {
let curr = (min + max) / 2 | 0; let curr = (min + max) / 2 | 0;
if (curr != maxLevel && if (curr != maxLevel &&
@ -212,6 +224,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) {
return 0; return 0;
} }
// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache
export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) {
if (maxLevel == null) { if (maxLevel == null) {
throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`); throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`);

@ -176,33 +176,28 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
return totalCost; return totalCost;
} }
// Process this Hacknet Server in the game loop. // Process this Hacknet Server in the game loop. Returns the number of hashes generated
// Returns the number of hashes generated
process(numCycles: number=1): number { process(numCycles: number=1): number {
const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000; const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000;
return this.hashRate * seconds; return this.hashRate * seconds;
} }
// Returns a boolean indicating whether the cache was successfully upgraded
upgradeCache(levels: number): void { upgradeCache(levels: number): void {
this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels)); this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels));
this.updateHashCapacity(); this.updateHashCapacity();
} }
// Returns a boolean indicating whether the number of cores was successfully upgraded
upgradeCore(levels: number, prodMult: number): void { upgradeCore(levels: number, prodMult: number): void {
this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels)); this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels));
this.updateHashRate(prodMult); this.updateHashRate(prodMult);
} }
// Returns a boolean indicating whether the level was successfully upgraded
upgradeLevel(levels: number, prodMult: number): void { upgradeLevel(levels: number, prodMult: number): void {
this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels)); this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels));
this.updateHashRate(prodMult); this.updateHashRate(prodMult);
} }
// Returns a boolean indicating whether the RAM was successfully upgraded
upgradeRam(levels: number, prodMult: number): boolean { upgradeRam(levels: number, prodMult: number): boolean {
for (let i = 0; i < levels; ++i) { for (let i = 0; i < levels; ++i) {
this.maxRam *= 2; this.maxRam *= 2;
@ -213,9 +208,7 @@ export class HacknetServer extends BaseServer implements IHacknetNode {
return true; return true;
} }
/** // Whenever a script is run, we must update this server's hash rate
* Whenever a script is run, we must update this server's hash rate
*/
runScript(script: RunningScript, prodMult?: number): void { runScript(script: RunningScript, prodMult?: number): void {
super.runScript(script); super.runScript(script);
if (prodMult != null && typeof prodMult === "number") { if (prodMult != null && typeof prodMult === "number") {

@ -40,13 +40,15 @@ import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseBu
import { removeElementById } from "../../utils/uiHelpers/removeElementById"; import { removeElementById } from "../../utils/uiHelpers/removeElementById";
/** /**
* Create a pop-up box that lets the player confirm traveling to a different city * Create a pop-up box that lets the player confirm traveling to a different city.
* If settings are configured to suppress this popup, just instantly travel * If settings are configured to suppress this popup, just instantly travel.
* The actual "Travel" implementation is implemented in the UI, and is passed in * The actual "Travel" implementation is implemented in the UI, and is passed in
* as an argument * as an argument.
* @param {CityName} destination - City that the player is traveling to
* @param {Function} travelFn - Function that changes the player's state for traveling
*/ */
type TravelFunction = (to: CityName) => void; type TravelFunction = (to: CityName) => void;
export function createTravelPopup(destination: CityName, travelFn: TravelFunction) { export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void {
const cost = CONSTANTS.TravelCost; const cost = CONSTANTS.TravelCost;
if (Settings.SuppressTravelConfirmation) { if (Settings.SuppressTravelConfirmation) {
@ -80,10 +82,10 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio
/** /**
* Create a pop-up box that lets the player purchase a server. * Create a pop-up box that lets the player purchase a server.
* @param ram - Amount of RAM (GB) on server * @param {number} ram - Amount of RAM (GB) on server
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createPurchaseServerPopup(ram: number, p: IPlayer) { export function createPurchaseServerPopup(ram: number, p: IPlayer): void {
const cost = getPurchaseServerCost(ram); const cost = getPurchaseServerCost(ram);
if (cost === Infinity) { if (cost === Infinity) {
dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer"); dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer");
@ -111,6 +113,7 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) {
/** /**
* Create a popup that lets the player start a Corporation * Create a popup that lets the player start a Corporation
* @param {IPlayer} p - Player object
*/ */
export function createStartCorporationPopup(p: IPlayer) { export function createStartCorporationPopup(p: IPlayer) {
if (!p.canAccessCorporation() || p.hasCorporation()) { return; } if (!p.canAccessCorporation() || p.hasCorporation()) { return; }
@ -172,8 +175,10 @@ export function createStartCorporationPopup(p: IPlayer) {
if (worldHeader instanceof HTMLElement) { if (worldHeader instanceof HTMLElement) {
worldHeader.click(); worldHeader.click(); worldHeader.click(); worldHeader.click();
} }
dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " + dialogBoxCreate(
"You can visit and manage your company in the City"); "Congratulations! You just started your own corporation with government seed money. " +
"You can visit and manage your company in the City"
);
removeElementById(popupId); removeElementById(popupId);
return false; return false;
} }
@ -187,21 +192,23 @@ export function createStartCorporationPopup(p: IPlayer) {
/** /**
* Create a popup that lets the player upgrade the cores on his/her home computer * Create a popup that lets the player upgrade the cores on his/her home computer
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createUpgradeHomeCoresPopup(p: IPlayer) { export function createUpgradeHomeCoresPopup(p: IPlayer) {
const currentCores = p.getHomeComputer().cpuCores; const currentCores = p.getHomeComputer().cpuCores;
if (currentCores >= 8) { return; } // Max of 8 cores if (currentCores >= 8) { return; } // Max of 8 cores
//Cost of purchasing another cost is found by indexing this array with number of current cores // Cost of purchasing another cost is found by indexing this array with number of current cores
const allCosts = [0, const allCosts = [
10e9, // 1->2 Cores - 10 bn 0,
250e9, // 2->3 Cores - 250 bn 10e9,
5e12, // 3->4 Cores - 5 trillion 250e9,
100e12, // 4->5 Cores - 100 trillion 5e12,
1e15, // 5->6 Cores - 1 quadrillion 100e12,
20e15, // 6->7 Cores - 20 quadrillion 1e15,
200e15]; // 7->8 Cores - 200 quadrillion 20e15,
200e15
];
const cost: number = allCosts[currentCores]; const cost: number = allCosts[currentCores];
const yesBtn = yesNoBoxGetYesButton(); const yesBtn = yesNoBoxGetYesButton();
@ -215,8 +222,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
} else { } else {
p.loseMoney(cost); p.loseMoney(cost);
p.getHomeComputer().cpuCores++; p.getHomeComputer().cpuCores++;
dialogBoxCreate("You purchased an additional CPU Core for your home computer! It now has " + dialogBoxCreate(
p.getHomeComputer().cpuCores + " cores."); "You purchased an additional CPU Core for your home computer! It now has " +
p.getHomeComputer().cpuCores + " cores."
);
} }
yesNoBoxClose(); yesNoBoxClose();
}); });
@ -226,15 +235,17 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate("Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + yesNoBoxCreate(
"Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " +
"lets you start with an additional Core Node in Hacking Missions.<br><br>" + "lets you start with an additional Core Node in Hacking Missions.<br><br>" +
"Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + "Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " +
"cost " + numeralWrapper.formatMoney(cost)); "cost " + numeralWrapper.formatMoney(cost)
);
} }
/** /**
* Create a popup that lets the player upgrade the RAM on his/her home computer * Create a popup that lets the player upgrade the RAM on his/her home computer
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function createUpgradeHomeRamPopup(p: IPlayer) { export function createUpgradeHomeRamPopup(p: IPlayer) {
const cost: number = p.getUpgradeHomeRamCost(); const cost: number = p.getUpgradeHomeRamCost();
@ -255,15 +266,17 @@ export function createUpgradeHomeRamPopup(p: IPlayer) {
yesNoBoxClose(); yesNoBoxClose();
}); });
yesNoBoxCreate("Would you like to purchase additional RAM for your home computer? <br><br>" + yesNoBoxCreate(
"Would you like to purchase additional RAM for your home computer? <br><br>" +
"This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" + "This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB. <br><br>" +
"This will cost " + numeralWrapper.format(cost, '$0.000a')); "This will cost " + numeralWrapper.format(cost, '$0.000a')
);
} }
/** /**
* Attempt to purchase a TOR router * Attempt to purchase a TOR router
* @param p - Player object * @param {IPlayer} p - Player object
*/ */
export function purchaseTorRouter(p: IPlayer) { export function purchaseTorRouter(p: IPlayer) {
if (p.hasTorRouter()) { if (p.hasTorRouter()) {
@ -285,7 +298,9 @@ export function purchaseTorRouter(p: IPlayer) {
p.getHomeComputer().serversOnNetwork.push(darkweb.ip); p.getHomeComputer().serversOnNetwork.push(darkweb.ip);
darkweb.serversOnNetwork.push(p.getHomeComputer().ip); darkweb.serversOnNetwork.push(p.getHomeComputer().ip);
dialogBoxCreate("You have purchased a Tor router!<br>" + dialogBoxCreate(
"You have purchased a Tor router!<br>" +
"You now have access to the dark web from your home computer<br>" + "You now have access to the dark web from your home computer<br>" +
"Use the scan/scan-analyze commands to search for the dark web connection."); "Use the scan/scan-analyze commands to search for the dark web connection."
);
} }

@ -1,4 +1,10 @@
// Calculate a script's RAM usage /**
* Implements RAM Calculation functionality.
*
* Uses the acorn.js library to parse a script's code into an AST and
* recursively walk through that AST, calculating RAM usage along
* the way
*/
import * as walk from "acorn-walk"; import * as walk from "acorn-walk";
import { RamCalculationErrorCode } from "./RamCalculationErrorCodes"; import { RamCalculationErrorCode } from "./RamCalculationErrorCodes";
@ -15,19 +21,26 @@ const specialReferenceWHILE = "__SPECIAL_referenceWhile";
// The global scope of a script is registered under this key during parsing. // The global scope of a script is registered under this key during parsing.
const memCheckGlobalKey = ".__GLOBAL__"; const memCheckGlobalKey = ".__GLOBAL__";
// Calcluates the amount of RAM a script uses. Uses parsing and AST walking only, /**
// rather than NetscriptEvaluator. This is useful because NetscriptJS code does * Parses code into an AST and walks through it recursively to calculate
// not work under NetscriptEvaluator. * RAM usage. Also accounts for imported modules.
* @param {Script[]} otherScripts - All other scripts on the server. Used to account for imported scripts
* @param {string} codeCopy - The code being parsed
* @param {WorkerScript} workerScript - Object containing RAM costs of Netscript functions. Also used to
* keep track of what functions have/havent been accounted for
*/
async function parseOnlyRamCalculate(otherScripts, code, workerScript) { async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
try { try {
// Maps dependent identifiers to their dependencies. /**
// * Maps dependent identifiers to their dependencies.
// The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__. *
// It depends on all the functions declared in the module, all the global scopes * The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__.
// of its imports, and any identifiers referenced in this global scope. Each * It depends on all the functions declared in the module, all the global scopes
// function depends on all the identifiers referenced internally. * of its imports, and any identifiers referenced in this global scope. Each
// We walk the dependency graph to calculate RAM usage, given that some identifiers * function depends on all the identifiers referenced internally.
// reference Netscript functions which have a RAM cost. * We walk the dependency graph to calculate RAM usage, given that some identifiers
* reference Netscript functions which have a RAM cost.
*/
let dependencyMap = {}; let dependencyMap = {};
// Scripts we've parsed. // Scripts we've parsed.
@ -48,19 +61,20 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
} }
} }
// Splice all the references in. // Splice all the references in
//Spread syntax not supported in edge, use Object.assign instead
//dependencyMap = {...dependencyMap, ...result.dependencyMap};
dependencyMap = Object.assign(dependencyMap, result.dependencyMap); dependencyMap = Object.assign(dependencyMap, result.dependencyMap);
} }
// Parse the initial module, which is the "main" script that is being run
const initialModule = "__SPECIAL_INITIAL_MODULE__"; const initialModule = "__SPECIAL_INITIAL_MODULE__";
parseCode(code, initialModule); parseCode(code, initialModule);
// Process additional modules, which occurs if the "main" script has any imports
while (parseQueue.length > 0) { while (parseQueue.length > 0) {
// Get the code from the server.
const nextModule = parseQueue.shift(); const nextModule = parseQueue.shift();
// Additional modules can either be imported from the web (in which case we use
// a dynamic import), or from other in-game scripts
let code; let code;
if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) { if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) {
try { try {
@ -136,10 +150,8 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
} }
} }
// Check if this identifier is a function in the workerscript env. // Check if this identifier is a function in the workerScript environment.
// If it is, then we need to get its RAM cost. // If it is, then we need to get its RAM cost.
//
// TODO it would be simpler to just reference a dictionary.
try { try {
function applyFuncRam(func) { function applyFuncRam(func) {
if (typeof func === "function") { if (typeof func === "function") {
@ -170,7 +182,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
workerScript.loadedFns[ref] = true; workerScript.loadedFns[ref] = true;
} }
// This accounts for namespaces (Bladeburner, CodingCOntract) // This accounts for namespaces (Bladeburner, CodingCpntract, etc.)
let func; let func;
if (ref in workerScript.env.vars.bladeburner) { if (ref in workerScript.env.vars.bladeburner) {
func = workerScript.env.vars.bladeburner[ref]; func = workerScript.env.vars.bladeburner[ref];
@ -196,9 +208,12 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) {
} }
} }
// Parses one script and calculates its ram usage, for the global scope and each function. /**
// Returns a cost map and a dependencyMap for the module. Returns a reference map to be joined * Helper function that parses a single script. It returns a map of all dependencies,
// onto the main reference map, and a list of modules that need to be parsed. * which are items in the code's AST that potentially need to be evaluated
* for RAM usage calculations. It also returns an array of additional modules
* that need to be parsed (i.e. are 'import'ed scripts).
*/
function parseOnlyCalculateDeps(code, currentModule) { function parseOnlyCalculateDeps(code, currentModule) {
const ast = parse(code, {sourceType:"module", ecmaVersion: 8}); const ast = parse(code, {sourceType:"module", ecmaVersion: 8});
@ -296,6 +311,12 @@ function parseOnlyCalculateDeps(code, currentModule) {
return {dependencyMap: dependencyMap, additionalModules: additionalModules}; return {dependencyMap: dependencyMap, additionalModules: additionalModules};
} }
/**
* Calculate's a scripts RAM Usage
* @param {string} codeCopy - The script's code
* @param {Script[]} otherScripts - All other scripts on the server.
* Used to account for imported scripts
*/
export async function calculateRamUsage(codeCopy, otherScripts) { export async function calculateRamUsage(codeCopy, otherScripts) {
// We don't need a real WorkerScript for this. Just an object that keeps // We don't need a real WorkerScript for this. Just an object that keeps
// track of whatever's needed for RAM calculations // track of whatever's needed for RAM calculations

@ -69,22 +69,20 @@ export class RunningScript {
if (script == null) { return; } if (script == null) { return; }
this.filename = script.filename; this.filename = script.filename;
this.args = args; this.args = args;
this.server = script.server;
this.server = script.server; //IP Address only
this.ramUsage = script.ramUsage; this.ramUsage = script.ramUsage;
} }
log(txt: string): void { log(txt: string): void {
if (this.logs.length > Settings.MaxLogCapacity) { if (this.logs.length > Settings.MaxLogCapacity) {
//Delete first element and add new log entry to the end.
//TODO Eventually it might be better to replace this with circular array
//to improve performance
this.logs.shift(); this.logs.shift();
} }
let logEntry = txt; let logEntry = txt;
if (FconfSettings.ENABLE_TIMESTAMPS) { if (FconfSettings.ENABLE_TIMESTAMPS) {
logEntry = "[" + getTimestamp() + "] " + logEntry; logEntry = "[" + getTimestamp() + "] " + logEntry;
} }
this.logs.push(logEntry); this.logs.push(logEntry);
this.logUpd = true; this.logUpd = true;
} }

@ -57,7 +57,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip
if (totalPrice == null) { return false; } if (totalPrice == null) { return false; }
if (Player.money.lt(totalPrice)) { if (Player.money.lt(totalPrice)) {
if (tixApi) { if (tixApi) {
workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this potiion. You need ${numeralWrapper.formatMoney(totalPrice)}`); workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}`);
} else if (opts.suppressDialog !== true) { } else if (opts.suppressDialog !== true) {
dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`);
} }