text: // ************ Big Feature related ************
  
  function respecBuyables(layer) {
  	if (!layers[layer].buyables) return
  	if (!layers[layer].buyables.respec) return
  	if (!player[layer].noRespecConfirm && !confirm(tmp[layer].buyables.respecMessage || "Are you sure you want to respec? This will force you to do a \"" + (tmp[layer].name ? tmp[layer].name : layer) + "\" reset as well!")) return
  	run(layers[layer].buyables.respec, layers[layer].buyables)
  	updateBuyableTemp(layer)
  	document.activeElement.blur()
  }
  
  function canAffordUpgrade(layer, id) {
  	let upg = tmp[layer].upgrades[id]
  	if(tmp[layer].deactivated) return false
  	if (tmp[layer].upgrades[id].canAfford !== undefined) return tmp[layer].upgrades[id].canAfford
  	let cost = tmp[layer].upgrades[id].cost
  	return canAffordPurchase(layer, upg, cost)
  }
  
  function canBuyBuyable(layer, id) {
  	let b = temp[layer].buyables[id]
  	return (b.unlocked && run(b.canAfford, b) && player[layer].buyables[id].lt(b.purchaseLimit) && !tmp[layer].deactivated)
  }
  
  
  
  function canAffordPurchase(layer, thing, cost) {
  
  	if (thing.currencyInternalName) {
  		let name = thing.currencyInternalName
  		if (thing.currencyLocation) {
  			return !(thing.currencyLocation[name].lt(cost))
  		}
  		else if (thing.currencyLayer) {
  			let lr = thing.currencyLayer
  			return !(player[lr][name].lt(cost))
  		}
  		else {
  			return !(player[name].lt(cost))
  		}
  	}
  	else {
  		return !(player[layer].points.lt(cost))
  	}
  }
  
  function buyUpgrade(layer, id) {
  	buyUpg(layer, id)
  }
  
  function buyUpg(layer, id) {
  	if (!tmp[layer].upgrades || !tmp[layer].upgrades[id]) return
  	let upg = tmp[layer].upgrades[id]
  	if (!player[layer].unlocked) return
  	if (!tmp[layer].upgrades[id].unlocked) return
  	if (player[layer].upgrades.includes(id)) return
  	if (upg.canAfford === false) return
  	let pay = layers[layer].upgrades[id].pay
  	if (pay !== undefined)
  		run(pay, layers[layer].upgrades[id])
  	else {
  		let cost = tmp[layer].upgrades[id].cost
  
  		if (upg.currencyInternalName) {
  			let name = upg.currencyInternalName
  			if (upg.currencyLocation) {
  				if (upg.currencyLocation[name].lt(cost)) return
  				upg.currencyLocation[name] = upg.currencyLocation[name].sub(cost)
  			}
  			else if (upg.currencyLayer) {
  				let lr = upg.currencyLayer
  				if (player[lr][name].lt(cost)) return
  				player[lr][name] = player[lr][name].sub(cost)
  			}
  			else {
  				if (player[name].lt(cost)) return
  				player[name] = player[name].sub(cost)
  			}
  		}
  		else {
  			if (player[layer].points.lt(cost)) return
  			player[layer].points = player[layer].points.sub(cost)
  			if(layer == "qol") player.qol.spentPoints = player.qol.spentPoints.add(cost)
  		}
  	}
  	player[layer].upgrades.push(id);
  	if (upg.onPurchase != undefined)
  		run(upg.onPurchase, upg)
  }
  
  function buyMaxBuyable(layer, id) {
  	if (!player[layer].unlocked) return
  	if (!tmp[layer].buyables[id].unlocked) return
  	if (!tmp[layer].buyables[id].canBuy) return
  	if (!layers[layer].buyables[id].buyMax) return
  
  	run(layers[layer].buyables[id].buyMax, layers[layer].buyables[id])
  	updateBuyableTemp(layer)
  }
  
  function buyBuyable(layer, id) {
  	if (!player[layer].unlocked) return
  	if (!tmp[layer].buyables[id].unlocked) return
  	if (!tmp[layer].buyables[id].canBuy) return
  
  	run(layers[layer].buyables[id].buy, layers[layer].buyables[id])
  	updateBuyableTemp(layer)
  }
  
  function clickClickable(layer, id) {
  	if (!player[layer].unlocked || tmp[layer].deactivated) return
  	if (!tmp[layer].clickables[id].unlocked) return
  	if (!tmp[layer].clickables[id].canClick) return
  
  	run(layers[layer].clickables[id].onClick, layers[layer].clickables[id])
  	updateClickableTemp(layer)
  }
  
  function clickGrid(layer, id) {
  	if (!player[layer].unlocked  || tmp[layer].deactivated) return
  	if (!run(layers[layer].grid.getUnlocked, layers[layer].grid, id)) return
  	if (!gridRun(layer, 'getCanClick', player[layer].grid[id], id)) return
  
  	gridRun(layer, 'onClick', player[layer].grid[id], id)
  }
  
  // Function to determine if the player is in a challenge
  function inChallenge(layer, id) {
  	let challenge = player[layer].activeChallenge
  	if (!challenge) return false
  	id = toNumber(id)
  	if (challenge == id) return true
  
  	if (layers[layer].challenges[challenge].countsAs)
  		return tmp[layer].challenges[challenge].countsAs.includes(id)
  }
  
  // ************ Misc ************
  
  var onTreeTab = true
  function showTab(name) {
  	if (LAYERS.includes(name) && !layerunlocked(name)) return
  	if (player.tab !== name) clearParticles(function(p) {return p.layer === player.tab})
  	if (player.tab === name && isPlainObject(tmp[name].tabFormat)) {
  		player.subtabs[name].mainTabs = Object.keys(layers[name].tabFormat)[0]
  	}
  	var toTreeTab = name == "none"
  	player.tab = name
  	if (player.navTab == "none" && (tmp[name].row !== "side") && (tmp[name].row !== "otherside")) player.lastSafeTab = name
  	delete player.notify[name]
  	updateTabFormats()
  	needCanvasUpdate = true
  	document.activeElement.blur()
  
  }
  
  function showNavTab(name) {
  	if (LAYERS.includes(name) && !layerunlocked(name)) return
  	if (player.navTab !== name) clearParticles(function(p) {return p.layer === player.navTab})
  
  	var toTreeTab = name == "tree"
  	player.navTab = name
  	player.notify[name] = false
  	updateTabFormats()
  	needCanvasUpdate = true
  }
  
  
  function goBack() {
  	if (player.navTab !== "none") showTab("none")
  	else showTab(player.lastSafeTab)
  }
  
  function layOver(obj1, obj2) {
  	for (let x in obj2) {
  		if (obj2[x] instanceof Decimal) obj1[x] = new Decimal(obj2[x])
  		else if (obj2[x] instanceof Object) layOver(obj1[x], obj2[x]);
  		else obj1[x] = obj2[x];
  	}
  }
  
  function prestigeNotify(layer) {
  	if (layers[layer].prestigeNotify) return layers[layer].prestigeNotify()
  	
  	if (isPlainObject(tmp[layer].tabFormat)) {
  		for (subtab in tmp[layer].tabFormat){
  			if (subtabResetNotify(layer, 'mainTabs', subtab))
  				return true
  		}
  	}
  	for (family in tmp[layer].microtabs) {
  		for (subtab in tmp[layer].microtabs[family]){
  			if (subtabResetNotify(layer, family, subtab))
  				return true
  		}
  	}
  	if (tmp[layer].autoPrestige || tmp[layer].passiveGeneration) return false
  	else if (tmp[layer].type == "static") return tmp[layer].canReset
  	else if (tmp[layer].type == "normal") return (tmp[layer].canReset && (tmp[layer].resetGain.gte(player[layer].points.div(10))))
  	else return false
  }
  
  function notifyLayer(name) {
  	if (player.tab == name || !layerunlocked(name)) return
  	player.notify[name] = 1
  }
  
  function subtabShouldNotify(layer, family, id) {
  	let subtab = {}
  	if (family == "mainTabs") subtab = tmp[layer].tabFormat[id]
  	else subtab = tmp[layer].microtabs[family][id]
  
  	if (subtab.embedLayer) return tmp[subtab.embedLayer].notify
  	else return subtab.shouldNotify
  }
  
  function subtabResetNotify(layer, family, id) {
  	let subtab = {}
  	if (family == "mainTabs") subtab = tmp[layer].tabFormat[id]
  	else subtab = tmp[layer].microtabs[family][id]
  	if (subtab.embedLayer) return tmp[subtab.embedLayer].prestigeNotify
  	else return subtab.prestigeNotify
  }
  
  function nodeShown(layer) {
  	if (layerShown(layer)) return true
  	switch (layer) {
  		case "idk":
  			return player.idk.unlocked
  			break;
  	}
  	return false
  }
  
  function layerunlocked(layer) {
  	if (tmp[layer] && tmp[layer].type == "none") return (player[layer].unlocked)
  	return LAYERS.includes(layer) && (player[layer].unlocked || (tmp[layer].canReset && tmp[layer].layerShown))
  }
  
  function keepGoing() {
  	player.keepGoing = true;
  	needCanvasUpdate = true;
  	goBack()
  }
  
  function toNumber(x) {
  	if (x.mag !== undefined) return x.toNumber()
  	if (x + 0 !== x) return parseFloat(x)
  	return x
  }
  
  function updateMilestones(layer) {
  	for (id in layers[layer].milestones) {
  		if (!(hasMilestone(layer, id)) && layers[layer].milestones[id].done()) {
  			player[layer].milestones.push(id)
  			if (tmp[layer].milestonePopups || tmp[layer].milestonePopups === undefined) doPopup("milestone", tmp[layer].milestones[id].requirementDescription, "Milestone Gotten!", 3, tmp[layer].color);
  			player[layer].lastMilestone = id
  		}
  	}
  }
  
  function updateAchievements(layer) {
  	for (id in layers[layer].achievements) {
  		if (isPlainObject(layers[layer].achievements[id]) && !(hasAchievement(layer, id)) && layers[layer].achievements[id].done()) {
  			player[layer].achievements.push(id)
  			if (layers[layer].achievements[id].onComplete) layers[layer].achievements[id].onComplete()
  			if (tmp[layer].achievementPopups || tmp[layer].achievementPopups === undefined) doPopup("achievement", tmp[layer].achievements[id].name, "Achievement Gotten!", 3, tmp[layer].color);
  		}
  	}
  }
  
  function addTime(diff, layer) {
  	let data = player
  	let time = data.timePlayed
  	if (layer) {
  		data = data[layer]
  		time = data.time
  	}
  
  	//I am not that good to perfectly fix that leak. ~ DB Aarex
  	if (time + 0 !== time) {
  		console.log("Memory leak detected. Trying to fix...")
  		time = toNumber(time)
  		if (isNaN(time) || time == 0) {
  			console.log("Couldn't fix! Resetting...")
  			time = layer ? player.timePlayed : 0
  			if (!layer) player.timePlayedReset = true
  		}
  	}
  	time += toNumber(diff)
  
  	if (layer) data.time = time
  	else data.timePlayed = time
  }
  
  shiftDown = false
  ctrlDown = false
  
  document.onkeydown = function (e) {
  	if (player === undefined) return;
  	if (gameEnded && !player.keepGoing) return;
  	shiftDown = e.shiftKey
  	ctrlDown = e.ctrlKey
  	let key = e.key
  	if (ctrlDown) key = "ctrl+" + key
  	if (onFocused) return
  	if (ctrlDown && hotkeys[key]) e.preventDefault()
  	if (hotkeys[key]) {
  		let k = hotkeys[key]
  		if (player[k.layer].unlocked && tmp[k.layer].hotkeys[k.id].unlocked)
  			k.onPress()
  	}
  }
  
  document.onkeyup = function (e) {
  	shiftDown = e.shiftKey
  	ctrlDown = e.ctrlKey
  }
  
  var onFocused = false
  function focused(x) {
  	onFocused = x
  }
  
  
  function isFunction(obj) {
  	return !!(obj && obj.constructor && obj.call && obj.apply);
  };
  
  function isPlainObject(obj) {
  	return (!!obj) && (obj.constructor === Object)
  }
  
  document.title = modInfo.name
  
  // Converts a string value to whatever it's supposed to be
  function toValue(value, oldValue) {
  	if (oldValue instanceof Decimal) {
  		value = new Decimal (value)
  		if (value.eq(decimalNaN)) return decimalZero
  		return value
  	}
  	if (!isNaN(oldValue)) 
  		return parseFloat(value) || 0
  	return value
  }
  
  // Variables that must be defined to display popups
  var activePopups = [];
  var popupID = 0;
  
  // Function to show popups
  function doPopup(type = "none", text = "This is a test popup.", title = "", timer = 3, color = "") {
  	switch (type) {
  		case "achievement":
  			popupTitle = "Achievement Unlocked!";
  			popupType = "achievement-popup"
  			break;
  		case "challenge":
  			popupTitle = "Challenge Complete";
  			popupType = "challenge-popup"
  			break;
  		default:
  			popupTitle = "Something Happened?";
  			popupType = "default-popup"
  			break;
  	}
  	if (title != "") popupTitle = title;
  	popupMessage = text;
  	popupTimer = timer;
  
  	activePopups.push({ "time": popupTimer, "type": popupType, "title": popupTitle, "message": (popupMessage + "\n"), "id": popupID, "color": color })
  	popupID++;
  }
  
  
  //Function to reduce time on active popups
  function adjustPopupTime(diff) {
  	for (popup in activePopups) {
  		activePopups[popup].time -= diff;
  		if (activePopups[popup]["time"] < 0) {
  			activePopups.splice(popup, 1); // Remove popup when time hits 0
  		}
  	}
  }
  
  function run(func, target, args = null) {
  	if (isFunction(func)) {
  		let bound = func.bind(target)
  		return bound(args)
  	}
  	else
  		return func;
  }
  
  function gridRun(layer, func, data, id) {
  	if (isFunction(layers[layer].grid[func])) {
  		let bound = layers[layer].grid[func].bind(layers[layer].grid)
  		return bound(data, id)
  	}
  	else
  		return layers[layer].grid[func];
  }