This commit is contained in:
thepaperpilot 2022-10-05 00:48:27 +00:00
parent b6d3405506
commit 0c62426490
530 changed files with 48502 additions and 0 deletions

View file

@ -0,0 +1,524 @@
addLayer("c", {
layer: "c", // This is assigned automatically, both to the layer and all upgrades, etc. Shown here so you know about it
name: "Candies", // This is optional, only used in a few places, If absent it just uses the layer id.
symbol: "C", // This appears on the layer's node. Default is the id with the first letter capitalized
position: 0, // Horizontal position within a row. By default it uses the layer id and sorts in alphabetical order
startData() { return {
unlocked: true,
points: new Decimal(0),
best: new Decimal(0),
total: new Decimal(0),
buyables: {}, // You don't actually have to initialize this one
beep: false,
}},
color: "#4BDC13",
requires: new Decimal(10), // Can be a function that takes requirement increases into account
resource: "lollipops", // Name of prestige currency
baseResource: "candies", // Name of resource prestige is based on
baseAmount() {return player.points}, // Get the current amount of baseResource
type: "normal", // normal: cost to gain currency depends on amount gained. static: cost depends on how much you already have
exponent: 0.5, // Prestige currency exponent
base: 5, // Only needed for static layers, base of the formula (b^(x^exp))
roundUpCost: false, // True if the cost needs to be rounded up (use when baseResource is static?)
canBuyMax() {}, // Only needed for static layers with buy max
gainMult() { // Calculate the multiplier for main currency from bonuses
mult = new Decimal(1)
if (hasUpgrade(this.layer, 166)) mult = mult.times(2) // These upgrades don't exist
if (hasUpgrade(this.layer, 120)) mult = mult.times(upgradeEffect(this.layer, 120))
return mult
},
gainExp() { // Calculate the exponent on main currency from bonuses
return new Decimal(1)
},
row: 0, // Row the layer is in on the tree (0 is the first row)
effect() {
return { // Formulas for any boosts inherent to resources in the layer. Can return a single value instead of an object if there is just one effect
waffleBoost: (true == false ? 0 : Decimal.pow(player[this.layer].points, 0.2)),
icecreamCap: (player[this.layer].points * 10)
}},
effectDescription() { // Optional text to describe the effects
eff = this.effect();
eff.waffleBoost = eff.waffleBoost.times(buyableEffect(this.layer, 11).first)
return "which are boosting waffles by "+format(eff.waffleBoost)+" and increasing the Ice Cream cap by "+format(eff.icecreamCap)
},
infoboxes:{
coolInfo: {
title: "Lore",
titleStyle: {'color': '#FE0000'},
body: "DEEP LORE!",
bodyStyle: {'background-color': "#0000EE"}
}
},
milestones: {
0: {requirementDescription: "3 Lollipops",
done() {return player[this.layer].best.gte(3)}, // Used to determine when to give the milestone
effectDescription: "Unlock the next milestone",
},
1: {requirementDescription: "4 Lollipops",
unlocked() {return hasMilestone(this.layer, 0)},
done() {return player[this.layer].best.gte(4)},
effectDescription: "You can toggle beep and boop (which do nothing)",
toggles: [
["c", "beep"], // Each toggle is defined by a layer and the data toggled for that layer
["f", "boop"]],
style() {
if(hasMilestone(this.layer, this.id)) return {
'background-color': '#1111DD'
}},
},
},
challenges: {
rows: 2,
cols: 12,
11: {
name: "Fun",
completionLimit: 3,
challengeDescription() {return "Makes the game 0% harder<br>"+challengeCompletions(this.layer, this.id) + "/" + this.completionLimit + " completions"},
unlocked() { return player[this.layer].best.gt(0) },
goal: new Decimal("20"),
currencyDisplayName: "lollipops", // Use if using a nonstandard currency
currencyInternalName: "points", // Use if using a nonstandard currency
currencyLayer: this.layer, // Leave empty if not in a layer
rewardEffect() {
let ret = player[this.layer].points.add(1).tetrate(0.02)
return ret;
},
rewardDisplay() { return format(this.rewardEffect())+"x" },
countsAs: [12, 21], // Use this for if a challenge includes the effects of other challenges. Being in this challenge "counts as" being in these.
rewardDescription: "Says hi",
onComplete() {console.log("hiii")} // Called when you complete the challenge
},
},
upgrades: {
rows: 2,
cols: 3,
11: {
title: "Generator of Genericness",
description: "Gain 1 Point every second.",
cost: new Decimal(1),
unlocked() { return player[this.layer].unlocked }, // The upgrade is only visible when this is true
},
12: {
description: "Candy generation is faster based on your unspent Lollipops.",
cost: new Decimal(1),
unlocked() { return (hasUpgrade(this.layer, 11))},
effect() { // Calculate bonuses from the upgrade. Can return a single value or an object with multiple values
let ret = player[this.layer].points.add(1).pow(player[this.layer].upgrades.includes(24)?1.1:(player[this.layer].upgrades.includes(14)?0.75:0.5))
if (ret.gte("1e20000000")) ret = ret.sqrt().times("1e10000000")
return ret;
},
effectDisplay() { return format(this.effect())+"x" }, // Add formatting to the effect
},
13: {
description: "Unlock a <b>secret subtab</b> and make this layer act if you unlocked it first.",
cost: new Decimal(69),
currencyDisplayName: "candies", // Use if using a nonstandard currency
currencyInternalName: "points", // Use if using a nonstandard currency
currencyLocation: "", // The object in player data that the currency is contained in
unlocked() { return (hasUpgrade(this.layer, 12))},
onPurchase() { // This function triggers when the upgrade is purchased
player[this.layer].unlockOrder = 0
},
style() {
if (hasUpgrade(this.layer, this.id)) return {
'background-color': '#1111dd'
}
else if (!canAffordUpgrade(this.layer, this.id)) {
return {
'background-color': '#dd1111'
}
} // Otherwise use the default
},
},
22: {
title: "This upgrade doesn't exist",
description: "Or does it?.",
currencyLocation() {return player[this.layer].buyables}, // The object in player data that the currency is contained in
currencyDisplayName: "exhancers", // Use if using a nonstandard currency
currencyInternalName: 11, // Use if using a nonstandard currency
cost: new Decimal(3),
unlocked() { return player[this.layer].unlocked }, // The upgrade is only visible when this is true
},
},
buyables: {
rows: 1,
cols: 12,
showRespec: true,
respec() { // Optional, reset things and give back your currency. Having this function makes a respec button appear
player[this.layer].points = player[this.layer].points.add(player[this.layer].spentOnBuyables) // A built-in thing to keep track of this but only keeps a single value
resetBuyables(this.layer)
doReset(this.layer, true) // Force a reset
},
respecText: "Respec Thingies", // Text on Respec button, optional
11: {
title: "Exhancers", // Optional, displayed at the top in a larger font
cost(x=player[this.layer].buyables[this.id]) { // cost for buying xth buyable, can be an object if there are multiple currencies
if (x.gte(25)) x = x.pow(2).div(25)
let cost = Decimal.pow(2, x.pow(1.5))
return cost.floor()
},
effect(x=player[this.layer].buyables[this.id]) { // Effects of owning x of the items, x is a decimal
let eff = {}
if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1))
else eff.first = Decimal.pow(1/25, x.times(-1).pow(1.1))
if (x.gte(0)) eff.second = x.pow(0.8)
else eff.second = x.times(-1).pow(0.8).times(-1)
return eff;
},
display() { // Everything else displayed in the buyable button after the title
let data = tmp[this.layer].buyables[this.id]
return "Cost: " + format(data.cost) + " lollipops\n\
Amount: " + player[this.layer].buyables[this.id] + "\n\
Adds + " + format(data.effect.first) + " things and multiplies stuff by " + format(data.effect.second)
},
unlocked() { return player[this.layer].unlocked },
canAfford() {
return player[this.layer].points.gte(tmp[this.layer].buyables[this.id].cost)},
buy() {
cost = tmp[this.layer].buyables[this.id].cost
player[this.layer].points = player[this.layer].points.sub(cost)
player[this.layer].buyables[this.id] = player[this.layer].buyables[this.id].add(1)
player[this.layer].spentOnBuyables = player[this.layer].spentOnBuyables.add(cost) // This is a built-in system that you can use for respeccing but it only works with a single Decimal value
},
buyMax() {}, // You'll have to handle this yourself if you want
style: {'height':'222px'},
sellOne() {
let amount = getBuyableAmount(this.layer, this.id)
if (amount.lte(0)) return // Only sell one if there is at least one
setBuyableAmount(this.layer, this.id, amount.sub(1))
player[this.layer].points = player[this.layer].points.add(this.cost())
},
},
},
doReset(resettingLayer){ // Triggers when this layer is being reset, along with the layer doing the resetting. Not triggered by lower layers resetting, but is by layers on the same row.
if(layers[resettingLayer].row > this.row) layerDataReset(this.layer, ["upgrades", "challenges"]) // This is actually the default behavior
},
layerShown() {return true}, // Condition for when layer appears on the tree
automate() {
}, // Do any automation inherent to this layer if appropriate
resetsNothing() {return false},
onPrestige(gain) {
return
}, // Useful for if you gain secondary resources or have other interesting things happen to this layer when you reset it. You gain the currency after this function ends.
hotkeys: [
{key: "c", description: "C: reset for lollipops or whatever", onPress(){if (canReset(this.layer)) doReset(this.layer)}},
{key: "ctrl+c", description: "Ctrl+c: respec things", onPress(){if (player[this.layer].unlocked) respecBuyables(this.layer)}},
],
increaseUnlockOrder: [], // Array of layer names to have their order increased when this one is first unlocked
microtabs: {
stuff: {
first: {
content: ["upgrades", ["display-text", function() {return "confirmed"}]]
},
second: {
content: [["upgrade", 11],
["row", [["upgrade", 11], "blank", "blank", ["upgrade", 11],]],
["display-text", function() {return "double confirmed"}]]
},
},
otherStuff: {
// There could be another set of microtabs here
}
},
bars: {
longBoi: {
fillStyle: {'background-color' : "#FFFFFF"},
baseStyle: {'background-color' : "#696969"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 300,
height: 30,
progress() {
return (player.points.log(10).div(10)).toNumber()
},
display() {
return format(player.points) + " / 1e10 points"
},
unlocked: true,
},
tallBoi: {
fillStyle: {'background-color' : "#4BEC13"},
baseStyle: {'background-color' : "#000000"},
textStyle: {'text-shadow': '0px 0px 2px #000000'},
borderStyle() {return {'border-width': "7px"}},
direction: UP,
width: 50,
height: 200,
progress() {
return player.points.div(100)
},
display() {
return formatWhole((player.points.div(1)).min(100)) + "%"
},
unlocked: true,
},
flatBoi: {
fillStyle: {'background-color' : "#FE0102"},
baseStyle: {'background-color' : "#222222"},
textStyle: {'text-shadow': '0px 0px 2px #000000'},
borderStyle() {return {}},
direction: UP,
width: 100,
height: 30,
progress() {
return player.c.points.div(50)
},
unlocked: true,
},
},
// Optional, lets you format the tab yourself by listing components. You can create your own components in v.js.
tabFormat: {
"main tab": {
buttonStyle() {return {'color': 'orange'}},
content:
["main-display",
"prestige-button", "resource-display",
["blank", "5px"], // Height
["raw-html", function() {return "<button onclick='console.log(`yeet`)'>'HI'</button>"}],
["display-text",
function() {return 'I have ' + format(player.points) + ' pointy points!'},
{"color": "red", "font-size": "32px", "font-family": "Comic Sans MS"}],
"h-line", "milestones", "blank", "upgrades", "challenges"],
},
thingies: {
style() {return {'background-color': '#222222'}},
buttonStyle() {return {'border-color': 'orange'}},
content:[
["buyables", ""], "blank",
["row", [
["toggle", ["c", "beep"]], ["blank", ["30px", "10px"]], // Width, height
["display-text", function() {return "Beep"}], "blank", ["v-line", "200px"],
["column", [
["prestige-button", "", {'width': '150px', 'height': '80px'}],
["prestige-button", "", {'width': '100px', 'height': '150px'}],
]],
], {'width': '600px', 'height': '350px', 'background-color': 'green', 'border-style': 'solid'}],
"blank",
["display-image", "discord.png"],],
},
jail: {
content: [
["infobox", "coolInfo"],
["bar", "longBoi"], "blank",
["row", [
["column", [
["display-text", "Sugar level:", {'color': 'teal'}], "blank", ["bar", "tallBoi"]],
{'background-color': '#555555', 'padding': '15px'}],
"blank",
["column", [
["display-text", "idk"],
["blank", ['0', '50px']], ["bar", "flatBoi"]
]],
]],
"blank", ["display-text", "It's jail because \"bars\"! So funny! Ha ha!"],
],
},
illuminati: {
unlocked() {return (hasUpgrade("c", 13))},
content:[
["raw-html", function() {return "<h1> C O N F I R M E D </h1>"}], "blank",
["microtabs", "stuff", {'width': '600px', 'height': '350px', 'background-color': 'brown', 'border-style': 'solid'}]
]
}
},
style() {return {
//'background-color': '#3325CC'
}},
nodeStyle() {return { // Style on the layer node
'color': '#3325CC',
'text-decoration': 'underline'
}},
componentStyles: {
"challenge"() {return {'height': '200px'}},
"prestige-button"() {return {'color': '#AA66AA'}},
},
tooltip() { // Optional, tooltip displays when the layer is unlocked
let tooltip = formatWhole(player[this.layer].points) + " " + this.resource
if (player[this.layer].buyables[11].gt(0)) tooltip += "\n" + formatWhole(player[this.layer].buyables[11]) + " Exhancers"
return tooltip
},
shouldNotify() { // Optional, layer will be highlighted on the tree if true.
// Layer will automatically highlight if an upgrade is purchasable.
return (player.c.buyables[11] == 1)
},
resetDescription: "Melt your points into ",
})
// This layer is mostly minimal but it uses a custom prestige type and a clickable
addLayer("f", {
startData() { return {
unlocked: false,
points: new Decimal(0),
boop: false,
clickables: {[11]: "Start"} // Optional default Clickable state
}},
color: "#FE0102",
requires() {return new Decimal(10)},
resource: "farm points",
baseResource: "candies",
baseAmount() {return player.points},
type: "custom", // A "Custom" type which is effectively static
exponent: 0.5,
base: 3,
roundUpCost: true,
canBuyMax() {return hasAchievement('a', 13)},
row: 1,
layerShown() {return true},
branches: ["c"], // When this layer appears, a branch will appear from this layer to any layers here. Each entry can be a pair consisting of a layer id and a color.
tooltipLocked() { // Optional, tooltip displays when the layer is locked
return ("This weird farmer dinosaur will only see you if you have at least " + this.requires() + " candies. You only have " + formatWhole(player.points))
},
midsection: [
"blank", ['display-image', 'https://images.beano.com/store/24ab3094eb95e5373bca1ccd6f330d4406db8d1f517fc4170b32e146f80d?auto=compress%2Cformat&dpr=1&w=390'],
["display-text", "Bork bork!"]
],
// The following are only currently used for "custom" Prestige type:
prestigeButtonText() { //Is secretly HTML
if (!this.canBuyMax()) return "Hi! I'm a <u>weird dinosaur</u> and I'll give you a Farm Point in exchange for all of your candies and lollipops! (At least " + formatWhole(tmp[this.layer].nextAt) + " candies)"
if (this.canBuyMax()) return "Hi! I'm a <u>weird dinosaur</u> and I'll give you <b>" + formatWhole(tmp[this.layer].resetGain) + "</b> Farm Points in exchange for all of your candies and lollipops! (You'll get another one at " + formatWhole(tmp[layer].nextAtDisp) + " candies)"
},
getResetGain() {
return getResetGain(this.layer, useType = "static")
},
getNextAt(canMax=false) { //
return getNextAt(this.layer, canMax, useType = "static")
},
canReset() {
return tmp[this.layer].baseAmount.gte(tmp[this.layer].nextAt)
},
// This is also non minimal, a Clickable!
clickables: {
rows: 1,
cols: 1,
masterButtonPress() { // Optional, reset things and give back your currency. Having this function makes a respec button appear
if (getClickableState(this.layer, 11) == "Borkened...")
player[this.layer].clickables[11] = "Start"
},
masterButtonText() {return (getClickableState(this.layer, 11) == "Borkened...") ? "Fix the clickable!" : "Does nothing"}, // Text on Respec button, optional
11: {
title: "Clicky clicky!", // Optional, displayed at the top in a larger font
display() { // Everything else displayed in the buyable button after the title
let data = getClickableState(this.layer, this.id)
return "Current state:<br>" + data
},
unlocked() { return player[this.layer].unlocked },
canClick() {
return getClickableState(this.layer, this.id) !== "Borkened..."},
onClick() {
switch(getClickableState(this.layer, this.id)){
case "Start":
player[this.layer].clickables[this.id] = "A new state!"
break;
case "A new state!":
player[this.layer].clickables[this.id] = "Keep going!"
break;
case "Keep going!":
player[this.layer].clickables[this.id] = "Maybe that's a bit too far..."
break;
case "Maybe that's a bit too far...":
player[this.layer].clickables[this.id] = "Borkened..."
break;
default:
player[this.layer].clickables[this.id] = "Start"
break;
}
},
style() {
switch(getClickableState(this.layer, this.id)){
case "Start":
return {'background-color': 'green'}
break;
case "A new state!":
return {'background-color': 'yellow'}
break;
case "Keep going!":
return {'background-color': 'orange'}
break;
case "Maybe that's a bit too far...":
return {'background-color': 'red'}
break;
default:
return {}
break;
}},
},
},
},
)
// A side layer with achievements, with no prestige
addLayer("a", {
startData() { return {
unlocked: true,
points: new Decimal(0),
}},
color: "yellow",
resource: "achievement power",
row: "side",
layerShown() {return true},
tooltip() { // Optional, tooltip displays when the layer is locked
return ("Achievements")
},
achievements: {
rows: 2,
cols: 3,
11: {
name: "Get me!",
done() {return true}, // This one is a freebie
goalTooltip: "How did this happen?", // Shows when achievement is not completed
doneTooltip: "You did it!", // Showed when the achievement is completed
},
12: {
name: "Impossible!",
done() {return false},
goalTooltip: "Mwahahaha!", // Shows when achievement is not completed
doneTooltip: "HOW????", // Showed when the achievement is completed
},
13: {
name: "EIEIO",
done() {return player.f.points.gte(1)},
tooltip: "Get a farm point.\n\nReward: The dinosaur is now your friend (you can max Farm Points).", // Showed when the achievement is completed
onComplete() {console.log("Bork bork bork!")}
},
},
midsection: [
"achievements",
]
},
)
// A "ghost" layer which offsets f in the tree
addLayer("spook", {
startData() { return {
unlocked: true,
points: new Decimal(0),
}},
type: "none",
row: 1,
layerShown: "ghost",
},
)

View file

@ -0,0 +1,66 @@
let modInfo = {
name: "The Modding Tree",
id: "modbase",
pointsName: "points",
discordName: "",
discordLink: "",
changelogLink: "https://github.com/Acamaeda/The-Modding-Tree/blob/master/changelog.md",
offlineLimit: 1, // In hours
initialStartPoints: new Decimal (10) // Used for hard resets and new players
}
// Set your version in num and name
let VERSION = {
num: "2.1.3.1",
name: " We should have thought of this sooner!",
}
// If you add new functions anywhere inside of a layer, and those functions have an effect when called, add them here.
// (The ones here are examples, all official functions are already taken care of)
var doNotCallTheseFunctionsEveryTick = ["doReset", "buy", "onPurchase", "blowUpEverything"]
function getStartPoints(){
return new Decimal(modInfo.initialStartPoints)
}
// Determines if it should show points/sec
function canGenPoints(){
return hasUpgrade("c", 11)
}
// Calculate points/sec!
function getPointGen() {
if(!canGenPoints())
return new Decimal(0)
let gain = new Decimal(1)
if (hasUpgrade("c", 12)) gain = gain.times(upgradeEffect("c", 12))
return gain
}
// You can add non-layer related variables that should to into "player" and be saved here, along with default values
function addedPlayerData() { return {
weather: "Yes",
happiness: new Decimal(72),
}}
// Display extra things at the top of the page
var displayThings = [
function() {if (player.points.eq(69)) return "Tee hee!"},
function() {if (player.f.points.gt(1)) return `You have ${player.f.points} farm points. (Which do nothing.)`},
function() {if (inChallenge("c", 11)) return "The game is currently <h1>0%</h1> harder."},
]
// Determines when the game "ends"
function isEndgame() {
return player.points.gte(new Decimal("e280000000"))
}
// Less important things beyond this point!
// You can change this if you have things that can be messed up by long tick lengths
function maxTickLength() {
return(3600000) // Default is 1 hour which is just arbitrarily large
}

File diff suppressed because it is too large Load diff

79
gamedevtree/js/canvas.js Normal file
View file

@ -0,0 +1,79 @@
var canvas;
var ctx;
window.addEventListener("resize", (_=>resizeCanvas()));
function retrieveCanvasData() {
let treeCanv = document.getElementById("treeCanvas")
let treeTab = document.getElementById("treeTab")
if (treeCanv===undefined||treeCanv===null) return false;
if (treeTab===undefined||treeTab===null) return false;
canvas = treeCanv;
ctx = canvas.getContext("2d");
return true;
}
function resizeCanvas() {
if (!retrieveCanvasData()) return
canvas.width = 0;
canvas.height = 0;
canvas.width = document.getElementById("treeTab").scrollWidth;
canvas.height = document.getElementById("treeTab").scrollHeight;
drawTree();
}
var colors = {
default: {
1: "#ffffff",
2: "#bfbfbf",
3: "#7f7f7f",
},
aqua: {
1: "#bfdfff",
2: "#8fa7bf",
3: "#5f6f7f",
},
}
var colors_theme
function drawTree() {
if (!retrieveCanvasData()) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (layer in layers){
if (tmp[layer].layerShown && tmp[layer].branches){
for (branch in tmp[layer].branches)
{
drawTreeBranch(layer, tmp[layer].branches[branch])
}
}
}
}
function drawTreeBranch(num1, data) { // taken from Antimatter Dimensions & adjusted slightly
let num2 = data
let color_id = 1
if (Array.isArray(data)){
num2 = data[0]
color_id = data[1]
}
if(typeof(color_id) == "number")
color_id = colors_theme[color_id]
if (document.getElementById(num1) == null || document.getElementById(num2) == null)
return
let start = document.getElementById(num1).getBoundingClientRect();
let end = document.getElementById(num2).getBoundingClientRect();
let x1 = start.left + (start.width / 2) + (document.getElementById("treeTab").scrollLeft || document.body.scrollLeft);
let y1 = start.top + (start.height / 2) + (document.getElementById("treeTab").scrollTop || document.body.scrollTop) - 50;
let x2 = end.left + (end.width / 2) + (document.getElementById("treeTab").scrollLeft || document.body.scrollLeft);
let y2 = end.top + (end.height / 2) + (document.getElementById("treeTab").scrollTop || document.body.scrollTop) - 50;
ctx.lineWidth = 15;
ctx.beginPath();
ctx.strokeStyle = color_id
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}

11
gamedevtree/js/colors.js Normal file
View file

@ -0,0 +1,11 @@
const updatesColor = "#4BDC13"
const experienceColor = "#FF5642"
const cashColor = "#F5A833"
const refactoringColor = "#4CABF5"
const schoolColor = "#917567"
const fameColor = "#F564E7"
const apiColor = "#AADB60"
const timeFluxColor = "#9F52F0"
const diplomaColor = "#6212FA"
const lecturesColor = "#09DE89"
const goodwillColor = "#156B25"

377
gamedevtree/js/game.js Normal file
View file

@ -0,0 +1,377 @@
var player;
var needCanvasUpdate = true;
var NaNalert = false;
var gameEnded = false;
// Don't change this
const TMT_VERSION = {
tmtNum: "2.1.3.1",
tmtName: "We should have thought of this sooner!"
}
function getResetGain(layer, useType = null) {
let type = useType
if (!useType) type = tmp[layer].type
if(tmp[layer].type == "none")
return new Decimal (0)
if (tmp[layer].gainExp.eq(0)) return new Decimal(0)
if (type=="static") {
if ((!tmp[layer].canBuyMax) || tmp[layer].baseAmount.lt(tmp[layer].requires)) return new Decimal(1)
let gain = tmp[layer].baseAmount.div(tmp[layer].requires).div(tmp[layer].gainMult).max(1).log(tmp[layer].base).times(tmp[layer].gainExp).pow(Decimal.pow(tmp[layer].exponent, -1))
return gain.floor().sub(player[layer].points).add(1).max(1);
} else if (type=="normal"){
if (tmp[layer].baseAmount.lt(tmp[layer].requires)) return new Decimal(0)
let gain = tmp[layer].baseAmount.div(tmp[layer].requires).pow(tmp[layer].exponent).times(tmp[layer].gainMult).pow(tmp[layer].gainExp)
if (gain.gte("e1e7")) gain = gain.sqrt().times("e5e6")
return gain.floor().max(0);
} else if (type=="custom"){
return layers[layer].getResetGain()
} else {
return new Decimal(0)
}
}
function getNextAt(layer, canMax=false, useType = null) {
let type = useType
if (!useType) type = tmp[layer].type
if(tmp[layer].type == "none")
return new Decimal (Infinity)
if (tmp[layer].gainMult.lte(0)) return new Decimal(Infinity)
if (tmp[layer].gainExp.lte(0)) return new Decimal(Infinity)
if (type=="static")
{
if (!tmp[layer].canBuyMax) canMax = false
let amt = player[layer].points.plus((canMax&&tmp[layer].baseAmount.gte(tmp[layer].nextAt))?tmp[layer].resetGain:0)
let extraCost = Decimal.pow(tmp[layer].base, amt.pow(tmp[layer].exponent).div(tmp[layer].gainExp)).times(tmp[layer].gainMult)
let cost = extraCost.times(tmp[layer].requires).max(tmp[layer].requires)
if (tmp[layer].roundUpCost) cost = cost.ceil()
return cost;
} else if (type=="normal"){
let next = tmp[layer].resetGain.add(1)
if (next.gte("e1e7")) next = next.div("e5e6").pow(2)
next = next.root(tmp[layer].gainExp).div(tmp[layer].gainMult).root(tmp[layer].exponent).times(tmp[layer].requires).max(tmp[layer].requires)
if (tmp[layer].roundUpCost) next = next.ceil()
return next;
} else if (type=="custom"){
return layers[layer].getNextAt(canMax)
} else {
return new Decimal(0)
}}
// Return true if the layer should be highlighted. By default checks for upgrades only.
function shouldNotify(layer){
for (id in tmp[layer].upgrades){
if (!isNaN(id)){
if (canAffordUpgrade(layer, id) && !hasUpgrade(layer, id) && tmp[layer].upgrades[id].unlocked){
return true
}
}
}
if (layers[layer].type === "static" && (layers[layer].canBuyMax == null || !layers[layer].canBuyMax()) && canReset(layer)) {
return true
}
if (layers[layer].shouldNotify){
return layers[layer].shouldNotify()
}
else
return false
}
function canReset(layer)
{
if(tmp[layer].type == "normal")
return tmp[layer].baseAmount.gte(tmp[layer].requires)
else if(tmp[layer].type== "static")
return tmp[layer].baseAmount.gte(tmp[layer].nextAt)
if(tmp[layer].type == "none")
return false
else
return layers[layer].canReset()
}
function rowReset(row, layer) {
for (lr in ROW_LAYERS[row]){
if(layers[lr].doReset) {
player[lr].activeChallenge = null // Exit challenges on any row reset on an equal or higher row
layers[lr].doReset(layer)
}
else
if(tmp[layer].row > tmp[lr].row && row !== "side") layerDataReset(lr)
}
}
function layerDataReset(layer, keep = []) {
let storedData = {unlocked: player[layer].unlocked} // Always keep unlocked
for (thing in keep) {
if (player[layer][keep[thing]] !== undefined)
storedData[keep[thing]] = player[layer][keep[thing]]
}
layOver(player[layer], layers[layer].startData());
player[layer].upgrades = []
player[layer].milestones = []
player[layer].challenges = getStartChallenges(layer)
resetBuyables(layer)
if (layers[layer].clickables && !player[layer].clickables)
player[layer].clickables = getStartClickables(layer)
for (thing in storedData) {
player[layer][thing] =storedData[thing]
}
}
function resetBuyables(layer){
if (layers[layer].buyables)
player[layer].buyables = getStartBuyables(layer)
player[layer].spentOnBuyables = new Decimal(0)
}
function addPoints(layer, gain) {
player[layer].points = player[layer].points.add(gain).max(0)
if (player[layer].best) player[layer].best = player[layer].best.max(player[layer].points)
if (player[layer].total) player[layer].total = player[layer].total.add(gain)
}
function generatePoints(layer, diff) {
addPoints(layer, tmp[layer].resetGain.times(diff))
}
var prevOnReset
function doReset(layer, force=false) {
let row = tmp[layer].row
if (!force) {
if (tmp[layer].baseAmount.lt(tmp[layer].requires)) return;
let gain = tmp[layer].resetGain
if (tmp[layer].type=="static") {
if (tmp[layer].baseAmount.lt(tmp[layer].nextAt)) return;
gain =(tmp[layer].canBuyMax ? gain : 1)
}
if (tmp[layer].type=="custom") {
if (!tmp[layer].canReset) return;
}
if (layers[layer].onPrestige)
layers[layer].onPrestige(gain)
addPoints(layer, gain)
updateMilestones(layer)
updateAchievements(layer)
if (!player[layer].unlocked) {
player[layer].unlocked = true;
needCanvasUpdate = true;
if (tmp[layer].increaseUnlockOrder){
lrs = tmp[layer].increaseUnlockOrder
for (lr in lrs)
if (!player[lrs[lr]].unlocked) player[lrs[lr]].unlockOrder++
}
}
tmp[layer].baseAmount = new Decimal(0) // quick fix
}
if (tmp[layer].resetsNothing) return
/*
for (layerResetting in layers) {
if (row >= layers[layerResetting].row && (!force || layerResetting != layer)) completeChallenge(layerResetting)
}
*/
prevOnReset = {...player} //Deep Copy
player.points = (row == 0 ? new Decimal(0) : getStartPoints())
for (let x = row; x >= 0; x--) rowReset(x, layer)
rowReset("side", layer)
prevOnReset = undefined
updateTemp()
updateTemp()
}
function resetRow(row) {
if (prompt('Are you sure you want to reset this row? It is highly recommended that you wait until the end of your current run before doing this! Type "I WANT TO RESET THIS" to confirm')!="I WANT TO RESET THIS") return
let pre_layers = ROW_LAYERS[row-1]
let layers = ROW_LAYERS[row]
let post_layers = ROW_LAYERS[row+1]
rowReset(row+1, post_layers[0])
doReset(pre_layers[0], true)
for (let layer in layers) {
player[layer].unlocked = false
if (player[layer].unlockOrder) player[layer].unlockOrder = 0
}
player.points = getStartPoints()
updateTemp();
resizeCanvas();
}
function startChallenge(layer, x) {
let enter = false
if (!player[layer].unlocked) return
if (player[layer].activeChallenge == x) {
completeChallenge(layer, x)
delete player[layer].activeChallenge
} else {
enter = true
}
if(enter) {
player[layer].activeChallenge = x
if (isFunction(layers[layer].challenges[x].onStart)) layers[layer].challenges[x].onStart()
}
doReset(layer, true)
updateChallengeTemp(layer)
}
function canCompleteChallenge(layer, x)
{
if (x != player[layer].activeChallenge) return
let challenge = tmp[layer].challenges[x]
if (challenge.currencyInternalName){
let name = challenge.currencyInternalName
if (challenge.currencyLocation){
return !(challenge.currencyLocation[name].lt(challenge.goal))
}
else if (challenge.currencyLayer){
let lr = challenge.currencyLayer
return !(player[lr][name].lt(readData(challenge.goal)))
}
else {
return !(player[name].lt(challenge.goal))
}
}
else {
return !(player[layer].points.lt(challenge.goal))
}
}
function completeChallenge(layer, x) {
var x = player[layer].activeChallenge
if (!x) return
if (! canCompleteChallenge(layer, x)){
delete player[layer].activeChallenge
return
}
if (player[layer].challenges[x] < tmp[layer].challenges[x].completionLimit) {
needCanvasUpdate = true
player[layer].challenges[x] += 1
if (layers[layer].challenges[x].onComplete) layers[layer].challenges[x].onComplete()
}
delete player[layer].activeChallenge
updateChallengeTemp(layer)
}
VERSION.withoutName = "v" + VERSION.num + (VERSION.pre ? " Pre-Release " + VERSION.pre : VERSION.pre ? " Beta " + VERSION.beta : "")
VERSION.withName = VERSION.withoutName + (VERSION.name ? ": " + VERSION.name : "")
function gameLoop(diff) {
if (isEndgame() || gameEnded) gameEnded = 1
if (isNaN(diff)) diff = 0
if (gameEnded && !player.keepGoing) {
diff = 0
player.tab = "gameEnded"
}
if (player.devSpeed) diff *= player.devSpeed
let limit = maxTickLength()
if(diff > limit)
diff = limit
addTime(diff)
if (layers.t) diff *= layers.t.effect()
if(diff > limit)
diff = limit
player.points = player.points.add(tmp.pointGen.times(diff)).max(0)
for (x = 0; x <= maxRow; x++){
for (item in TREE_LAYERS[x]) {
let layer = TREE_LAYERS[x][item].layer
if (layers[layer].update) layers[layer].update(diff);
}
}
for (row in OTHER_LAYERS){
for (item in OTHER_LAYERS[row]) {
let layer = OTHER_LAYERS[row][item].layer
if (layers[layer].update) layers[layer].update(diff);
}
}
for (x = maxRow; x >= 0; x--){
for (item in TREE_LAYERS[x]) {
let layer = TREE_LAYERS[x][item].layer
if (layers[layer].automate) layers[layer].automate();
}
}
for (row in OTHER_LAYERS){
for (item in OTHER_LAYERS[row]) {
let layer = OTHER_LAYERS[row][item].layer
if (layers[layer].automate) layers[layer].automate();
}
}
for (layer in layers){
if (layers[layer].milestones) updateMilestones(layer);
if (layers[layer].achievements) updateAchievements(layer)
}
if (player.hasNaN&&!NaNalert) {
clearInterval(interval);
player.autosave = false;
NaNalert = true;
alert("We have detected a corruption in your save. Please visit one of the discords in the info panel for help.")
}
}
function hardReset() {
if (!confirm("Are you sure you want to do this? You will lose all your progress!")) return
player = getStartPlayer()
save();
window.location.reload();
}
var ticking = false
var interval = setInterval(function() {
if (player===undefined||tmp===undefined) return;
if (ticking) return;
if (gameEnded&&!player.keepGoing) return;
ticking = true
let now = Date.now()
let diff = (now - player.time) / 1e3
if (player.offTime !== undefined) {
if (player.offTime.remain > modInfo.offlineLimit * 3600000) player.offTime.remain = modInfo.offlineLimit * 3600000
if (player.offTime.remain > 0) {
let offlineDiff = Math.max(player.offTime.remain / 10, diff)
player.offTime.remain -= offlineDiff
diff += offlineDiff
}
if (!player.offlineProd || player.offTime.remain <= 0) delete player.offTime
}
if (player.devSpeed) diff *= player.devSpeed
player.time = now
if (needCanvasUpdate) resizeCanvas();
needCanvasUpdate = false;
updateTemp();
gameLoop(diff)
ticking = false
}, 50)

View file

@ -0,0 +1,179 @@
var layers = {}
function layerShown(layer){
return layers[layer].layerShown();
}
var LAYERS = Object.keys(layers);
var hotkeys = {};
var maxRow = 0;
function updateHotkeys()
{
hotkeys = {};
for (layer in layers){
hk = layers[layer].hotkeys
if (hk){
for (id in hk){
hotkeys[hk[id].key] = hk[id]
hotkeys[hk[id].key].layer = layer
}
}
}
}
var ROW_LAYERS = {}
var TREE_LAYERS = {}
var OTHER_LAYERS = {}
function updateLayers(){
LAYERS = Object.keys(layers);
ROW_LAYERS = {}
TREE_LAYERS = {}
OTHER_LAYERS = {}
for (layer in layers){
layers[layer].layer = layer
if (layers[layer].upgrades){
for (thing in layers[layer].upgrades){
if (!isNaN(thing)){
layers[layer].upgrades[thing].id = thing
layers[layer].upgrades[thing].layer = layer
if (layers[layer].upgrades[thing].unlocked === undefined)
layers[layer].upgrades[thing].unlocked = true
}
}
}
if (layers[layer].milestones){
for (thing in layers[layer].milestones){
if (!isNaN(thing)){
layers[layer].milestones[thing].id = thing
layers[layer].milestones[thing].layer = layer
if (layers[layer].milestones[thing].unlocked === undefined)
layers[layer].milestones[thing].unlocked = true
}
}
}
if (layers[layer].achievements){
for (thing in layers[layer].achievements){
if (!isNaN(thing)){
layers[layer].achievements[thing].id = thing
layers[layer].achievements[thing].layer = layer
if (layers[layer].achievements[thing].unlocked === undefined)
layers[layer].achievements[thing].unlocked = true
}
}
}
if (layers[layer].challenges){
for (thing in layers[layer].challenges){
if (!isNaN(thing)){
layers[layer].challenges[thing].id = thing
layers[layer].challenges[thing].layer = layer
if (layers[layer].challenges[thing].unlocked === undefined)
layers[layer].challenges[thing].unlocked = true
if (layers[layer].challenges[thing].completionLimit === undefined)
layers[layer].challenges[thing].completionLimit = 1
}
}
}
if (layers[layer].buyables){
layers[layer].buyables.layer = layer
for (thing in layers[layer].buyables){
if (!isNaN(thing)){
layers[layer].buyables[thing].id = thing
layers[layer].buyables[thing].layer = layer
if (layers[layer].buyables[thing].unlocked === undefined)
layers[layer].buyables[thing].unlocked = true
}
}
}
if (layers[layer].clickables){
layers[layer].clickables.layer = layer
for (thing in layers[layer].clickables){
if (!isNaN(thing)){
layers[layer].clickables[thing].id = thing
layers[layer].clickables[thing].layer = layer
if (layers[layer].clickables[thing].unlocked === undefined)
layers[layer].clickables[thing].unlocked = true
}
}
}
if (layers[layer].bars){
layers[layer].bars.layer = layer
for (thing in layers[layer].bars){
layers[layer].bars[thing].id = thing
layers[layer].bars[thing].layer = layer
if (layers[layer].bars[thing].unlocked === undefined)
layers[layer].bars[thing].unlocked = true
}
}
if (layers[layer].infoboxes){
for (thing in layers[layer].infoboxes){
layers[layer].infoboxes[thing].id = thing
layers[layer].infoboxes[thing].layer = layer
if (layers[layer].infoboxes[thing].unlocked === undefined)
layers[layer].infoboxes[thing].unlocked = true
}
}
if(!layers[layer].componentStyles) layers[layer].componentStyles = {}
if(layers[layer].symbol === undefined) layers[layer].symbol = layer.charAt(0).toUpperCase() + layer.slice(1)
if(layers[layer].unlockOrder === undefined) layers[layer].unlockOrder = []
if(layers[layer].gainMult === undefined) layers[layer].gainMult = new Decimal(1)
if(layers[layer].gainExp === undefined) layers[layer].gainExp = new Decimal(1)
if(layers[layer].type === undefined) layers[layer].type = "none"
if(layers[layer].base === undefined || layers[layer].base <= 1) layers[layer].base = 2
let row = layers[layer].row
if(!ROW_LAYERS[row]) ROW_LAYERS[row] = {}
if(!TREE_LAYERS[row] && !isNaN(row)) TREE_LAYERS[row] = []
if(!OTHER_LAYERS[row] && isNaN(row)) OTHER_LAYERS[row] = []
ROW_LAYERS[row][layer]=layer;
let position = (layers[layer].position !== undefined ? layers[layer].position : layer)
if (!isNaN(row)) TREE_LAYERS[row].push({layer: layer, position: position})
else OTHER_LAYERS[row].push({layer: layer, position: position})
if (maxRow < layers[layer].row) maxRow = layers[layer].row
}
for (row in OTHER_LAYERS) {
OTHER_LAYERS[row].sort((a, b) => (a.position > b.position) ? 1 : -1)
}
for (row in TREE_LAYERS) {
TREE_LAYERS[row].sort((a, b) => (a.position > b.position) ? 1 : -1)
}
updateHotkeys()
}
function addLayer(layerName, layerData){ // Call this to add layers from a different file!
layers[layerName] = layerData
}
// If data is a function, return the result of calling it. Otherwise, return the data.
function readData(data, args=null){
if (!!(data && data.constructor && data.call && data.apply))
return data(args);
else
return data;
}
function someLayerUnlocked(row){
for (layer in ROW_LAYERS[row])
if (player[layer].unlocked)
return true
return false
}
// This isn't worth making a .ts file over
const UP = 0
const DOWN = 1
const LEFT = 2
const RIGHT = 3

266
gamedevtree/js/layers/a.js Normal file
View file

@ -0,0 +1,266 @@
addLayer("a", {
name: "api",
symbol: "A",
color: apiColor,
branches: [ 'r' ],
row: 3,
position: 1,
resource: "endpoints",
baseResource: "refactors",
infoboxes: {
lore: {
title: "api",
body: `All this <span style="color: ${refactoringColor}">refactoring</span> has given you a new sense of perspective on how all these different game engines tend to work, and you have an idea for a new <span style="color: ${apiColor}">Application Programming Interface (API)</span> that could simplify everything enormously, making almost everything easier to implement. The more <span style="color: ${refactoringColor}">refactoring experience</span> you have, the more <span style="color: ${apiColor}">API end points</span> you can use to implement your design.<br/><br/>` +
`Designing your <span style="color: ${apiColor}">API</span> means spending your <span style="color: ${apiColor}">endpoints</span> on adding or improving the various bonuses available to you.`
}
},
resetDescription: "Design ",
startData() { return {
unlocked: false,
points: new Decimal(0),
unused: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || player.r.total.gte(8) },
type: "static",
requires: new Decimal(10),
base: new Decimal(1.2),
baseAmount() { return player.r.points },
exponent: 1,
gainMult() {
mult = new Decimal(1)
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
onPrestige(gain) {
player[this.layer].unused = player[this.layer].unused.add(gain)
},
tooltip() { return `${formatWhole(player.a.unused)} endpoints` },
hotkeys: [
{
key: "a",
description: "Press A to design API endpoints",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
["display-text", () => inChallenge("d", 12) ? `<h2 style="color: red;">Disabled during ${layers.d.challenges[player.d.activeChallenge].name} degree plan</h2>` : ""],
["display-text", () => `You have <h2 style="color: ${tmp.a.color}; text-shadow: ${tmp.a.color} 0px 0px 10px">${formatWhole(player.a.unused)}</h2> endpoints`],
["display-text", () => `You have earned a total of ${player.a.points} endpoints.`],
"blank",
"prestige-button",
"blank",
"buyables",
"blank",
"milestones"
],
buyables: {
rows: 2,
cols: 3,
respec() {
player.a.unused = player.a.points
setBuyableAmount(this.layer, 11, 0)
setBuyableAmount(this.layer, 12, 0)
setBuyableAmount(this.layer, 13, 0)
setBuyableAmount(this.layer, 21, 0)
setBuyableAmount(this.layer, 22, 0)
setBuyableAmount(this.layer, 23, 0)
doReset("a", true)
},
respecText: "Re-design API",
11: {
title: "/refactoring/bonus",
display() {
const cost = this.cost()
return `Each endpoint squares refactoring bonuses.<br/>Currently: ^${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(7500).mul(new Decimal(1).add(amt))
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(2).pow(Decimal.mul(getBuyableAmount(this.layer, this.id), buyableEffect("a", 23)))
}
},
12: {
title: "/motivation/boost",
display() {
const cost = this.cost()
return `These endpoints delay the productivity slowdown by 10 raised to the power of (5 raised to the power of endpoints).<br/>Currently: /${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(10000).mul(new Decimal(2).pow(amt))
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(10).pow(new Decimal(5).pow(Decimal.mul(getBuyableAmount(this.layer, this.id), buyableEffect("a", 23))).sub(1))
}
},
13: {
title: "/updates",
display() {
const cost = this.cost()
return `Each endpoint quadruples update gain.<br/>Currently: x${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(2).pow(amt).mul(50000)
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(4).pow(Decimal.mul(getBuyableAmount(this.layer, this.id), buyableEffect("a", 23)))
},
unlocked() { return challengeCompletions("d", 11) > 0 }
},
21: {
title: "/experience",
display() {
const cost = this.cost()
return `Each endpoint multiplies experience gain by 50x.<br/>Currently: x${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(1000).mul(new Decimal(5).pow(new Decimal(1).add(amt)))
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(50).pow(Decimal.mul(getBuyableAmount(this.layer, this.id), buyableEffect("a", 23)))
}
},
22: {
title: "/refactoring/prod",
display() {
const cost = this.cost()
return `Each endpoint raises the extra slowdown effects of refactoring to the ^.2 power.<br/>Currently: ^${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(500).mul(new Decimal(5).pow(new Decimal(2).add(amt)))
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(.2).pow(Decimal.mul(getBuyableAmount(this.layer, this.id), buyableEffect("a", 23)))
}
},
23: {
title: "/api/v2",
display() {
const cost = this.cost()
return `Each endpoint multiplies all other endpoint effects by 50%.<br/>Currently: x${format(this.effect())}<br/>Requires ${formatWhole(cost.endpoints)} endpoints and ${format(cost.updates)} updates.`
},
cost(x) {
const amt = x || getBuyableAmount(this.layer, this.id)
return {
endpoints: new Decimal(1).add(amt),
updates: new Decimal(2).pow(amt).mul(75000)
}
},
canAfford() {
const cost = this.cost()
return player[this.layer].unused.gte(cost.endpoints) && player.u.points.gte(cost.updates)
},
buy() {
const cost = this.cost()
player[this.layer].unused = player[this.layer].unused.sub(cost.endpoints)
player.u.points = player.u.points.sub(cost.updates)
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return inChallenge("d", 12) ? new Decimal(1) : new Decimal(1.5).pow(getBuyableAmount(this.layer, this.id))
},
unlocked() { return challengeCompletions("d", 11) > 0 }
}
},
milestones: {
0: {
requirementDescription: "1 total API endpoints",
effectDescription: "Retain the first, second, fourth, and eighth refactors milestones",
done() { return player[this.layer].points.gte(1) }
},
1: {
requirementDescription: "2 total API endpoints",
effectDescription: "Buying refactors will buy as many as you can afford",
done() { return player[this.layer].points.gte(2) }
},
2: {
requirementDescription: "3 total API endpoints",
effectDescription: "Retain all refactors milestones",
done() { return player[this.layer].points.gte(3) }
},
3: {
requirementDescription: "4 total API endpoints",
effectDescription: "Unlock a new Degree program",
done() { return player[this.layer].points.gte(4) }
},
4: {
requirementDescription: "5 total API endpoints",
effectDescription: "Row 4 resets don't reset refactorings",
done() { return player[this.layer].points.gte(5) }
}
}
})

253
gamedevtree/js/layers/c.js Normal file
View file

@ -0,0 +1,253 @@
addLayer("c", {
name: "cash",
symbol: "C",
color: cashColor,
branches: [ 'u' ],
row: 1,
position: 1,
resource: "cash",
baseResource: "updates",
infoboxes: {
lore: {
title: "cash",
body: `<span style="color: ${cashColor}">Selling</span> your game to a publisher means needing to start over on a new one, but you can use the <span style="color: ${cashColor}">money</span> to finally upgrade your PC! You're confident buying new hardware and such is the best way to work more efficiently.`
}
},
resetDescription: "Sell game to publisher for ",
startData() { return {
unlocked: false,
total: new Decimal(0),
points: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || hasUpgrade("e", 12) },
type: "normal",
requires: new Decimal(10),
baseAmount() { return player.u.points },
exponent: 1.5,
gainMult() {
mult = new Decimal(100).mul(buyableEffect("f", 12))
if (hasUpgrade("f", 12) && hasUpgrade("g", 12)) mult = mult.mul(upgradeEffect("f", 12))
if (hasUpgrade("l", 11)) mult = mult.mul(upgradeEffect("l", 11))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
onPrestige(gain) {
if (hasMilestone("d", 1)) addPoints("e", getResetGain("e"))
},
doReset(resettingLayer) {
if (['s', 'f', 't', 'd', 'l', 'g'].includes(resettingLayer)) {
const shouldKeepUpgrades = {
11: hasMilestone("f", 0),
12: hasMilestone("f", 1),
13: hasMilestone("f", 2),
14: hasMilestone("f", 3),
21: hasMilestone("f", 4),
22: hasMilestone("f", 5),
23: hasMilestone("f", 6),
24: hasMilestone("f", 7),
111: hasMilestone("s", 3),
112: hasMilestone("s", 3),
113: hasMilestone("s", 3),
114: hasMilestone("s", 3),
121: hasMilestone("s", 3),
122: hasMilestone("s", 3),
123: hasMilestone("s", 3),
124: hasMilestone("s", 3)
}
const upgradesToKeep = []
for (let upgrade of player[this.layer].upgrades) {
if (shouldKeepUpgrades[upgrade]) {
upgradesToKeep.push(upgrade)
}
}
layerDataReset(this.layer)
player[this.layer].upgrades = upgradesToKeep
}
},
resetsNothing() { return hasMilestone("s", 5) },
hotkeys: [
{
key: "c",
description: "Press C to sell your game to a publisher",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
["display-text", function() {
return hasUpgrade("e", 13) && tmp.c.resetGain.times ? `(${format(tmp.c.resetGain.times(layers.c.revenue(1)))}/sec)` : ""
},
{ "marginTop": "-1em", "display": "block" }
],
"blank",
"prestige-button",
"blank",
() => hasUpgrade("e", 13) ? ["display-text", "Equipment", { "font-size": "32px" }] : [],
() => hasUpgrade("e", 13) ? "blank" : [],
"buyables",
"blank",
"upgrades",
() => hasUpgrade("e", 13) ? ["display-text", "Revenue", { "font-size": "32px" }] : [],
() => hasUpgrade("e", 13) ? "blank" : [],
() => hasUpgrade("e", 13) ? ["row", [["upgrade", 111], ["upgrade", 112], ["upgrade", 113], ["upgrade", 114]]] : [],
() => hasUpgrade("g", 11) && !inChallenge("d", 11) ? ["row", [["upgrade", 121], ["upgrade", 122], ["upgrade", 123], ["upgrade", 124]]] : []
],
update(diff) {
generatePoints("c", this.revenue(diff))
},
shouldNotify() {
return canAffordPurchase("c", layers[this.layer].buyables[11], layers[this.layer].buyables[11].cost())
},
upgrades: {
rows: 2,
cols: 4,
11: {
title: "Buy premium text editor",
description: "Purchase a text editor, allowing you to double your productivity",
cost: new Decimal(80)
},
12: {
title: "Buy premium git client",
description: "Purchase a git client, allowing you to double your productivity",
cost: new Decimal(100)
},
13: {
title: "Buy ambient sound machine",
description: "Purchase an overpriced machine to do a website's job, allowing you to double your productivity",
cost: new Decimal(800)
},
14: {
title: "Buy Keurig",
description: "Purchase an overhyped coffee machine, allowing you to double your productivity",
cost: new Decimal(1200)
},
21: {
title: "Buy incense burner",
description: "Purchase an overpriced incense burner, allowing you to double your productivity",
cost: new Decimal(2500)
},
22: {
title: "Buy mechanical keyboard",
description: "Purchase an overpriced keyboard, allowing you to double your productivity at the expense of your coworkers'",
cost: new Decimal(4000)
},
23: {
title: "Buy massaging chair",
description: "Purchase an overpriced chair, allowing you to double your productivity",
cost: new Decimal(10000)
},
24: {
title: "Buy sensory deprivation egg",
description: "Purchase an isolation tank, allowing you to double your productivity",
cost: new Decimal(100000)
},
// 1XX represents revenue upgrades
// since they appear in a different tab they can have whatever id I want to give them,
// because I'll be manually including them in a grid. Making them 1XX means they won't show up
// in the normal grid of upgrades unless it gets 11 rows
111: {
title: "Add a banner ad",
description: "Yum, a delicious $1 cpm, which equates to automatically earning .1% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(10),
unlocked() { return hasUpgrade("e", 13) }
},
112: {
title: "Add an interactive banner ad",
description: "By adding interactivity to the banner ad you can earn an additional .2% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(20),
unlocked() { return hasUpgrade("c", 111) }
},
113: {
title: "Add pre-game ad",
description: "Making players watch an ad before playing your game puts another .2% of cash gain per second directly in your pocket",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(30),
unlocked() { return hasUpgrade("c", 112) }
},
114: {
title: "Add an interstitial ad",
description: "Placing ads between levels further engages the players and compensates you accordingly, earning an additional .5% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(40),
unlocked() { return hasUpgrade("c", 113) }
},
121: {
title: "Add cosmetic microtransactions",
description: "Cosmetic microtransactions allow players to support you ethically, earning an additional 9% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(10000),
unlocked() { return hasUpgrade("g", 11) && !inChallenge("d", 11) }
},
122: {
title: "Add cosmetic loot crates",
description: "Adding some randomness to the cosmetics can encourage players to spend even more, earning an additional 90% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(12500),
unlocked() { return hasUpgrade("g", 11) && !inChallenge("d", 11) }
},
123: {
title: "Add loot crate weapons",
description: "Adding just a little bit of gameplay advantages to paying customers makes sense, and earns an additional 900% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(15000),
unlocked() { return hasUpgrade("g", 11) && !inChallenge("d", 11) }
},
124: {
title: "Add gacha mechanics",
description: "Completely disregarding the consumer's wellbeing allows you to earn an additional 9000% of cash gain per second",
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(20000),
unlocked() { return hasUpgrade("g", 11) && !inChallenge("d", 11) }
}
},
buyables: {
rows: 1,
cols: 1,
11: {
title: "Upgrade hardware",
cost() { return new Decimal(100).mul(new Decimal(2).pow(getBuyableAmount("c", 11))).round() },
display() { return `Each upgrade additively raises your base productivity to the +.25 power.<br/><br/>Currently: ^${format(this.effect())}<br/><br/>Next upgrade cost: ${format(this.cost())} cash` },
canAfford() { return player[this.layer].points.gte(this.cost()) },
effect() { return new Decimal(0.25).add(buyableEffect("s", 22)).mul(getBuyableAmount("c", 11)).add(1) },
buy() {
if (!hasUpgrade("l", 11) || inChallenge("d", 21))
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount("c", 11, getBuyableAmount("c", 11).add(1))
}
}
},
revenue(diff) {
let cpm = 0
if (hasUpgrade("c",111)) cpm += 1
if (hasUpgrade("c",112)) cpm += 2
if (hasUpgrade("c",113)) cpm += 2
if (hasUpgrade("c",114)) cpm += 5
if (hasUpgrade("c",121) && hasUpgrade("g", 11) && !inChallenge("d", 11)) cpm += 90
if (hasUpgrade("c",122) && hasUpgrade("g", 11) && !inChallenge("d", 11)) cpm += 900
if (hasUpgrade("c",123) && hasUpgrade("g", 11) && !inChallenge("d", 11)) cpm += 9000
if (hasUpgrade("c",124) && hasUpgrade("g", 11) && !inChallenge("d", 11)) cpm += 90000
return diff * cpm / 1000
}
})

175
gamedevtree/js/layers/d.js Normal file
View file

@ -0,0 +1,175 @@
addLayer("d", {
name: "diplomas",
symbol: "D",
color: diplomaColor,
branches: [ 'r', 's', 'f' ],
row: 3,
position: 3,
resource: "diplomas",
baseResource: "enrollments",
infoboxes: {
lore: {
title: "diplomas",
body: `Are you ready for all that time and <span style="color: ${cashColor}">money</span> to pay off? Your <span style="color: ${diplomaColor}">university</span> is finally giving you a piece of paper to recognize your achievements! This paper doesn't really do much, but it might be a good stepping stone to other opportunities.<br/><br/>` +
"You'll also be able to get specific degrees for certain fields by demonstrating your mastery of the subject."
}
},
resetDescription: "Graduate for ",
startData() { return {
unlocked: false,
points: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || (player.a.points.gte(2) && player.g.points.gte(2)) },
type: "static",
requires: new Decimal(10),
base: new Decimal(1.2),
baseAmount() { return player.s.points },
exponent: 1,
gainMult() {
mult = new Decimal(1)
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
effect() {
return player.d.points.mul(player.d.points.add(1)).div(8)
},
effectDescription() {
return `which give you ${format(this.effect())} free levels in each class.`
},
hotkeys: [
{
key: "d",
description: "Press D to graduate",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
"prestige-button",
"blank",
"challenges",
"blank",
"upgrades",
"blank",
"milestones"
],
update(diff) {
if (challengeCompletions("d", 21) > 0 && canReset("r")) {
doReset("r")
}
if (hasMilestone("d", 2) && canReset("s")) {
doReset("s")
}
if (challengeCompletions("d", 22) > 0 && canReset("f")) {
doReset("f")
}
if (hasMilestone("d", 3) && player.d["auto-school"]) {
[11, 12, 21, 22].forEach(id => {
while (buyBuyable("s", id)) { }
})
}
},
shouldNotify() {
return (inChallenge("d", 11) && canCompleteChallenge("d", 11)) ||
(inChallenge("d", 12) && canCompleteChallenge("d", 12)) ||
(inChallenge("d", 21) && canCompleteChallenge("d", 21)) ||
(inChallenge("d", 22) && canCompleteChallenge("d", 22))
},
upgrades: {
rows: 1,
cols: 1,
11: {
title: "Write Thesis",
description: "Finally, the culmination of all your work: Your Magnum Opus, \"The Game Dev Tree\". Completing this work finishes the game.",
cost: new Decimal("ee12"),
currencyDisplayName: "hours of work",
currencyInternalName: "points",
currencyLocation: () => player,
unlocked() { return hasMilestone("d", 4) }
}
},
challenges: {
rows: 2,
cols: 2,
11: {
name: "B.S. in Computer Science",
challengeDescription: "Demonstrate your subject mastery by causing a Diploma reset, and disabling all benefits from row 4 layers except for Diplomas and API.",
rewardDescription: "Unlock 2 new purchasable endpoints",
goal: new Decimal(8),
currencyDisplayName: "enrollments",
currencyInternalName: "points",
currencyLayer: "s",
unlocked() { return hasMilestone("a", 3) },
style: { width: "400px", height: "320px" }
},
12: {
name: "B.A. in Marketing",
challengeDescription: "Demonstrate your subject mastery by causing a Diploma reset, and disabling all benefits from row 4 layers except for Diplomas and Good Will.",
rewardDescription: "Spent goodwill also counts towards bonuses, at 50% efficiency",
goal: new Decimal(8),
currencyDisplayName: "enrollments",
currencyInternalName: "points",
currencyLayer: "s",
unlocked() { return hasMilestone("g", 1) },
onStart() { player.r.milestones = [] },
style: { width: "400px", height: "320px" }
},
21: {
name: "M.S. in Computer Science",
challengeDescription: "Demonstrate your subject mastery by causing a Diploma reset, and disabling all benefits from row 4 layers except for Diplomas and Time Flux.",
rewardDescription: "Unlock a 9th ring and Refactors reset nothing and are purchased automatically",
goal: new Decimal(12),
currencyDisplayName: "enrollments",
currencyInternalName: "points",
currencyLayer: "s",
unlocked() { return hasMilestone("d", 4) },
countsAs: [ 11, 12 ],
style: { width: "400px", height: "320px" }
},
22: {
name: "M.A. in Marketing",
challengeDescription: "Demonstrate your subject mastery by causing a Diploma reset, and disabling all benefits from row 4 layers except for Diplomas and Lectures.",
rewardDescription: "Unlock another TA and Fame resets nothing and is purchased automatically",
goal: new Decimal(12),
currencyDisplayName: "enrollments",
currencyInternalName: "points",
currencyLayer: "s",
unlocked() { return hasMilestone("d", 4) },
countsAs: [ 11, 12 ],
style: { width: "400px", height: "320px" }
}
},
milestones: {
0: {
requirementDescription: "1 total diplomas",
effectDescription: "Releasing updates does not reset hours of work",
done() { return player[this.layer].points.gte(1) }
},
1: {
requirementDescription: "2 total diplomas",
effectDescription: "Resetting either the experience or cash layer gives what would've been gained from both",
done() { return player[this.layer].points.gte(2) }
},
2: {
requirementDescription: "3 total diplomas",
effectDescription: "School resets nothing and is automatically purchased",
done() { return player[this.layer].points.gte(3) }
},
3: {
requirementDescription: "4 total diplomas",
effectDescription: "Automatically take classes you can afford",
toggles: [["d", "auto-school"]],
done() { return player[this.layer].points.gte(4) }
},
4: {
requirementDescription: "7 total diplomas",
effectDescription: "Unlock Masters Degrees and Thesis Upgrade",
done() { return player[this.layer].points.gte(7) },
unlocked() { return player.t.unlocked && player.l.unlocked }
}
}
})

121
gamedevtree/js/layers/e.js Normal file
View file

@ -0,0 +1,121 @@
addLayer("e", {
name: "experience",
symbol: "E",
color: experienceColor,
branches: [ 'u' ],
row: 1,
position: 0,
resource: "experience",
baseResource: "updates",
infoboxes: {
lore: {
title: "experience",
body: "Look, maybe that idea just wasn't very good. You just saw a comment online that gave you this idea for a much better game! You can't even bear to think of your current game now that this new idea is in your head!<br/><br/>" +
"Don't worry, though, you're sure your time spent on that last idea will surely help you on this one."
}
},
resetDescription: "Start Over for ",
startData() { return {
unlocked: false,
total: new Decimal(0),
points: new Decimal(0),
exp: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || player.u.best.gte(3) },
type: "normal",
requires: new Decimal(5),
baseAmount() { return player.u.points },
exponent: 2,
gainMult() {
mult = new Decimal(1).mul(buyableEffect("f", 13)).mul(buyableEffect("a", 21))
if (hasUpgrade("f", 11) && hasUpgrade("g", 12) && !inChallenge("d", 11)) mult = mult.mul(upgradeEffect("f", 11))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
onPrestige(gain) {
if (hasMilestone("d", 1)) addPoints("c", getResetGain("c"))
},
doReset(resettingLayer) {
if (['r', 's', 'a', 't', 'd', 'l'].includes(resettingLayer)) {
const shouldKeepUpgrades = {
11: hasMilestone("r", 2),
12: hasMilestone("r", 0),
13: hasMilestone("r", 0),
21: hasMilestone("r", 4),
22: hasMilestone("r", 5),
23: hasMilestone("r", 6)
}
const upgradesToKeep = []
for (let upgrade of player[this.layer].upgrades) {
if (shouldKeepUpgrades[upgrade]) {
upgradesToKeep.push(upgrade)
}
}
layerDataReset(this.layer)
player[this.layer].upgrades = upgradesToKeep
}
},
resetsNothing() { return hasMilestone("s", 5) },
effect() { return player.e.points.pow(buyableEffect("s", 11)).sqrt().add(1) },
effectDescription() {
return `multiplying base productivity by ${format(this.effect())}x.`
},
hotkeys: [
{
key: "e",
description: "Press E to scrap your game and start over",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
["display-text", () => `Your total experience is also delaying the productivity slow down by ${format(player.e.total.mul(layers.r.effect()).mul(buyableEffect("a", 12)))} hours.`],
"blank",
"prestige-button",
"blank",
"upgrades"
],
upgrades: {
rows: 2,
cols: 3,
11: {
title: "Learn a new programming language",
description: "Wow! This programming language is so much easier to write in! Total experience now effects update gain",
cost: new Decimal(2),
effect() { return player.e.total.times(layers.r.effect()).clampMin(1).log10().add(1) }
},
12: {
title: "Contact publisher",
description: "Use your experience to contact a publisher, unlocking an adjacent prestige layer",
cost: new Decimal(10)
},
13: {
title: "Contact ad company",
description: "Use your experience to contact an ad provider, unlocking passive cash generation",
cost: new Decimal(25),
unlocked() { return hasUpgrade("e", 12) }
},
21: {
title: "Read Game Programming Patterns",
description: "This treasure trove of a book makes me twice as productive",
cost: new Decimal(100),
unlocked() { return hasUpgrade("e", 12) }
},
22: {
title: "Subscribe to Sebastian Lague",
description: "Just being subscribed infuses you with enough knowledge to make you twice as productive",
cost: new Decimal(200),
unlocked() { return hasUpgrade("e", 12) }
},
23: {
title: "Play Davey Wreden's games",
description: "Davey Wreden's insights on the relationships between games and their creators and players make you once again twice as productive",
cost: new Decimal(250),
unlocked() { return hasUpgrade("e", 12) }
}
}
})

262
gamedevtree/js/layers/f.js Normal file
View file

@ -0,0 +1,262 @@
addLayer("f", {
name: "fame",
symbol: "F",
color: fameColor,
branches: [ 'c' ],
row: 2,
position: 3,
resource: "fame",
baseResource: "cash",
infoboxes: {
lore: {
title: "fame",
body: `You've started accumulating a name for yourself. Some people even recognize your name, and check out your new releases. The more <span style="color: ${fameColor}">fans</span> you have, the more quickly you attract more. Time to take advantage of that!<br/><br/>` +
`By creating social media accounts you can harness your <span style="color: ${fameColor}">fan base</span> for all sorts of benefits! In fact, you may as well create some alt accounts while you're at it: the more the merrier, when <span style="color: ${fameColor}">fame</span> is involved!`
}
},
resetDescription: "Elevate your social status by ",
startData() { return {
unlocked: false,
best: new Decimal(0),
points: new Decimal(0),
fans: new Decimal(1)
}},
layerShown() { return player[this.layer].unlocked || player.u.best.gte(30) },
type: "static",
requires: new Decimal(2500),
base: new Decimal(4),
baseAmount() { return player.c.points },
exponent: 1.25,
gainMult() {
mult = new Decimal(1)
if (hasUpgrade("g", 23) && !inChallenge("d", 11)) mult = mult.div(upgradeEffect("g", 23))
if (hasUpgrade("l", 12)) mult = mult.div(upgradeEffect("l", 12))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
canBuyMax() { return true },
effect() {
return {
doubleFrequency: player[this.layer].points.gte(1) ? new Decimal(60).div(player[this.layer].points.mul(layers.g.effect())).div(buyableEffect("f", 11).add(1)) : new Decimal("infinity"),
productivityMult: player[this.layer].fans.mul(layers.g.effect()).clampMin(10).log10(),
fanMult: buyableEffect("f", 11),
cashMult: buyableEffect("f", 12),
expMult: buyableEffect("f", 13),
upgMult: buyableEffect("f", 14)
}
},
effectDescription() {
return player[this.layer].points.lessThan(1) ? "" : `which double your amount of fans every ${format(this.effect().doubleFrequency)} seconds.`
},
doReset(resettingLayer) {
if (['d', 'l', 'g'].includes(resettingLayer)) {
layerDataReset(this.layer, hasMilestone("g", 2) && !inChallenge("d", 11) ? [ 'milestones', 'upgrades' ] : [])
if (hasMilestone("g", 0) && !inChallenge("d", 11)) player[this.layer].fans = new Decimal(1000)
if (hasMilestone("g", 1) && !inChallenge("d", 11)) {
setBuyableAmount("f", 11, new Decimal(1))
setBuyableAmount("f", 12, new Decimal(1))
setBuyableAmount("f", 13, new Decimal(1))
setBuyableAmount("f", 14, new Decimal(1))
}
}
},
resetsNothing() { return challengeCompletions("d", 22) > 0 },
hotkeys: [
{
key: "f",
description: "Press F to elevate your social status",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
["display-text", function() {
const { productivityMult, fanMult, cashMult, expMult, upgMult } = layers.f.effect()
let text = `<br/>You currently have ${format(player.f.fans.floor(), 4)} fans, which currently:`
text += `<br/>Multiplies productivity by ${format(productivityMult)}x`
if (getBuyableAmount("f", 11).gte(1)) text += `<br/>Multiplies fan gain by ${format(fanMult)}x due to discord`
if (getBuyableAmount("f", 12).gte(1)) text += `<br/>Multiplies cash gain by ${format(cashMult)}x due to patreon`
if (getBuyableAmount("f", 13).gte(1)) text += `<br/>Multiplies experience gain by ${format(expMult)}x due to twitch`
if (getBuyableAmount("f", 14).gte(1)) text += `<br/>Multiplies update gain by ${format(upgMult)}x due to github`
return text
}],
"blank",
"prestige-button",
"blank",
"buyables",
"blank",
"upgrades",
"blank",
["display-text", () => `Your best fame is ${player.f.best}`],
"milestones"
],
update(diff) {
if (player[this.layer].points.gte(1)) {
const freq = this.effect().doubleFrequency
if (freq.gt(0))
player[this.layer].fans = player[this.layer].fans.mul(new Decimal(2).pow(new Decimal(diff).div(freq)))
}
if (hasUpgrade("l", 12) && !inChallenge("d", 21))
[11, 12, 13, 14].forEach(id => {
if (layers.f.buyables[id].canAfford()) layers.f.buyables[id].buy()
})
},
buyables: {
rows: 1,
cols: 4,
11: {
title: "Discord",
cost() { return getBuyableAmount("f", 11).add(1) },
display() { return getBuyableAmount("f", 11).gte(1) ? `Each alt account raises your discord effect on fan gain to the ^1.1 power.<br/><br/>Next upgrade cost: ${this.cost()} fame` : `Create a discord, boosting your fan gain the more fans you have<br/><br/>Unlock cost: ${this.cost()} fame` },
canAfford() { return player[this.layer].points.gte(this.cost()) },
effect() {
if (getBuyableAmount("f", 11).lte(0)) return new Decimal(1)
let effect = new Decimal(1.1).pow(getBuyableAmount("f", 11).sub(1)).mul(player[this.layer].fans.clampMin(10).log10().pow(0.3)).mul(layers.g.effect()).add(1)
if (hasUpgrade("f", 14) && hasUpgrade("g", 12) && !inChallenge("d", 11)) effect = effect.mul(upgradeEffect("f", 14))
return effect
},
buy() {
if (!hasUpgrade("l", 12) || inChallenge("d", 21)) player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount("f", 11, getBuyableAmount("f", 11).add(1))
}
},
12: {
title: "Patreon",
cost() { return getBuyableAmount("f", 12).add(1).mul(2) },
display() { return getBuyableAmount("f", 12).gte(1) ? `Each alt account raises your patreon effect on cash gain to the ^1.1 power.<br/><br/>Next upgrade cost: ${this.cost()} fame` : `Create a patreon, boosting your cash gain the more fans you have<br/><br/>Unlock cost: ${this.cost()} fame` },
canAfford() { return player[this.layer].points.gte(this.cost()) },
effect() {
if (getBuyableAmount("f", 12).lte(0)) return new Decimal(1)
return new Decimal(1.1).pow(getBuyableAmount("f", 12).sub(1)).mul(player[this.layer].fans.clampMin(10).log2().sqrt()).mul(layers.g.effect()).add(1)
},
buy() {
if (!hasUpgrade("l", 12) || inChallenge("d", 21)) player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount("f", 12, getBuyableAmount("f", 12).add(1))
}
},
13: {
title: "Twitch",
cost() { return getBuyableAmount("f", 13).add(1).mul(2) },
display() { return getBuyableAmount("f", 13).gte(1) ? `Each alt account raises your twitch effect on experience gain to the ^1.1 power.<br/><br/>Next upgrade cost: ${this.cost()} fame` : `Create a twitch where you stream development and get instant feedback, boosting your experience gain the more fans you have<br/><br/>Unlock cost: ${this.cost()} fame` },
canAfford() { return player[this.layer].points.gte(this.cost()) },
effect() {
if (getBuyableAmount("f", 11).lte(0)) return new Decimal(1)
return new Decimal(1.1).pow(getBuyableAmount("f", 13).sub(1)).mul(player[this.layer].fans.clampMin(10).log2().pow(0.25)).mul(layers.g.effect()).add(1)
},
buy() {
if (!hasUpgrade("l", 12) || inChallenge("d", 21)) player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount("f", 13, getBuyableAmount("f", 13).add(1))
}
},
14: {
title: "Github",
cost() { return getBuyableAmount("f", 14).add(1).mul(3) },
display() { return getBuyableAmount("f", 14).gte(1) ? `Each alt account raises your github effect on update gain to the ^1.1 power.<br/><br/>Next upgrade cost: ${this.cost()} fame` : `Add a link in the game to the github repo, boosting your update gain the more fans you have<br/><br/>Unlock cost: ${this.cost()} fame` },
canAfford() { return player[this.layer].points.gte(this.cost()) },
effect() {
if (getBuyableAmount("f", 11).lte(0)) return new Decimal(1)
return new Decimal(1.1).pow(getBuyableAmount("f", 14).sub(1)).mul(player[this.layer].fans.clampMin(10).log10().pow(0.25)).mul(layers.g.effect()).add(1)
},
buy() {
if (!hasUpgrade("l", 12) || inChallenge("d", 21)) player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount("f", 14, getBuyableAmount("f", 14).add(1))
}
}
},
upgrades: {
rows: 1,
cols: 4,
11: {
title: "Create p2p botnet",
description() { return `Include malware in a game to use your players to pirate textbooks, increasing experience gain based on number of fans.` },
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(15000),
unlocked() { return hasUpgrade("g", 12) && !inChallenge("d", 11) },
effect() { return player.f.fans.clampMin(10).log10().log(1.5).mul(layers.g.effect()).add(1) }
},
12: {
title: "Use botnet for scalping",
description() { return `Use your botnet to buy limited supply items before legitimate buyers, increasing cash gain based on number of fans.` },
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(20000),
unlocked() { return hasUpgrade("g", 12) && !inChallenge("d", 11) },
effect() { return player.f.fans.clampMin(10).log10().log2().mul(layers.g.effect()).add(1) }
},
13: {
title: "Use botnet for distributed processing",
description() { return `Use your botnet to automatically analyze your games with deep learning, increasing refactoring gain based on number of fans.` },
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(30000),
unlocked() { return hasUpgrade("g", 12) && !inChallenge("d", 11) },
effect() { return player.f.fans.clampMin(10).log10().log2().sqrt().mul(layers.g.effect()).add(1) }
},
14: {
title: "Use botnet for social media manipulation",
description() { return `Use your botnet to automatically like posts on social media, increasing fan gain based on number of fans.` },
currencyDisplayName: "updates",
currencyInternalName: "points",
currencyLocation() { return player.u },
cost: new Decimal(50000),
unlocked() { return hasUpgrade("g", 12) && !inChallenge("d", 11) },
effect() { return player.f.fans.clampMin(10).log10().log10().mul(layers.g.effect()).add(1) }
}
},
milestones: {
0: {
requirementDescription: "1 best fame",
effectDescription: "Retain the first equipment upgrade",
done() { return player[this.layer].best.gte(1) }
},
1: {
requirementDescription: "2 best fame",
effectDescription: "Retain the second equipment upgrade",
done() { return player[this.layer].best.gte(2) }
},
2: {
requirementDescription: "3 best fame",
effectDescription: "Retain the third equipment upgrade",
done() { return player[this.layer].best.gte(3) },
unlocked() { return hasMilestone("f", 0) }
},
3: {
requirementDescription: "4 best fame",
effectDescription: "Retain the fourth equipment upgrade",
done() { return player[this.layer].best.gte(4) },
unlocked() { return hasMilestone("f", 1) }
},
4: {
requirementDescription: "5 best fame",
effectDescription: "Retain the fifth equipment upgrade",
done() { return player[this.layer].best.gte(5) },
unlocked() { return hasMilestone("f", 2) }
},
5: {
requirementDescription: "6 best fame",
effectDescription: "Retain the sixth equipment upgrade",
done() { return player[this.layer].best.gte(6) },
unlocked() { return hasMilestone("f", 3) }
},
6: {
requirementDescription: "7 best fame",
effectDescription: "Retain the seventh equipment upgrade",
done() { return player[this.layer].best.gte(7) },
unlocked() { return hasMilestone("f", 4) }
},
7: {
requirementDescription: "8 best fame",
effectDescription: "Retain the eighth equipment upgrade",
done() { return player[this.layer].best.gte(8) },
unlocked() { return hasMilestone("f", 5) }
}
}
})

151
gamedevtree/js/layers/g.js Normal file
View file

@ -0,0 +1,151 @@
addLayer("g", {
name: "good will",
symbol: "G",
color: goodwillColor,
branches: [ 'f' ],
row: 3,
position: 5,
resource: "good will",
baseResource: "fame",
infoboxes: {
lore: {
title: "good will",
body: `Your massive amounts of <span style="color: ${fameColor}">fans</span> and <span style="color: ${fameColor}">fame</span> have gotten your games an amount of <span style="color: ${goodwillColor}">good will</span>. Players are more likely to become <span style="color: ${fameColor}">fans</span>, and trust your games will be good for consumers and fun to boot...<br/><br/>` +
`<i>However</i>, this also means there's lots to gain by cashing in that <span style="color: ${goodwillColor}">good will</span>. Buying these upgrades will have powerful effects, but <span style="color: ${goodwillColor}">good will</span> is <span style="text-shadow: 0px 0px 4px">permanently</span> harder to gain the more you've earned, so it may be better to hold off until you have a lot of <span style="color: ${fameColor}">fans</span> before spending <span style="color: ${goodwillColor}">good will</span>.`
}
},
resetDescription: "Get acknowledged as trustworthy by your fans for ",
startData() { return {
unlocked: false,
points: new Decimal(0),
unused: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || player.f.best.gte(6) },
type: "static",
requires: new Decimal(8),
base: new Decimal(1.25),
baseAmount() { return player.f.points },
exponent: 0.9,
gainMult() {
mult = new Decimal(1)
if (hasUpgrade("l", 15)) mult = mult.div(upgradeEffect("l", 15))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
onPrestige(gain) {
player[this.layer].unused = player[this.layer].unused.add(gain)
},
effect() {
if (inChallenge("d", 11))
return new Decimal(1)
let ret = player.g.unused.add(1)
if (challengeCompletions("d", 12) > 0) ret = ret.add(player[this.layer].points.sub(player[this.layer].unused).div(2))
ret = ret.pow(2.5)
if (hasUpgrade("l", 15)) ret = ret.pow(2)
return ret
},
effectDescription() {
return `which multiplies your fame and fan effects by x${formatWhole(this.effect())}.`
},
tooltip() { return `${formatWhole(player.g.unused)} good will` },
hotkeys: [
{
key: "g",
description: "Press G to get acknowledged as trustworthy by your fans",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
["display-text", () => inChallenge("d", 11) ? `<h2 style="color: red;">Disabled during ${layers.d.challenges[player.d.activeChallenge].name} degree plan</h2>` : ""],
["display-text", () => `You have <h2 style="color: ${tmp.g.color}; text-shadow: ${tmp.g.color} 0px 0px 10px">${formatWhole(player.g.unused)}</h2> good will, ${layers.g.effectDescription()}`],
["display-text", () => `You have earned a total of ${player.g.points} good will.`],
"blank",
"prestige-button",
"blank",
["display-text", () => `<button onClick="layers.g.respec()" class="longUpg ${player.g.unlocked ? 'can' : 'locked'}">Respec<br>(Causes good will reset)</button>`],
"blank",
"upgrades",
"milestones"
],
upgrades: {
rows: 2,
cols: 3,
11: {
title: "Surprise Mechanics",
description: "Unlock a series of powerful revenue upgrades",
cost: new Decimal(1),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g
},
12: {
title: "Trojans and Worms",
description: "Unlock a series of powerful fame upgrades",
cost: new Decimal(1),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g
},
13: {
title: "Let them make their own content",
description: "Unlock a series of powerful update upgrades",
cost: new Decimal(1),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g
},
21: {
title: "Trick fans into refactoring for you",
description: "Use free labor to multiply refactors gain based on your fame",
cost: new Decimal(2),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g,
effect() { return player.f.points.pow(2).add(1) }
},
22: {
title: "Hack into college databases",
description: "Manipulate your GPA to multiply enrollment gain based on your refactors",
cost: new Decimal(2),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g,
effect() { return player.r.points.pow(2).add(1) }
},
23: {
title: "Run for Student Government",
description: "Take advantage of your local colleges to spread the word about your games and multiply fame gain based on enrollments",
cost: new Decimal(2),
currencyDisplayName: "goodwill",
currencyInternalName: "unused",
currencyLocation: () => player.g,
effect() { return player.s.points.pow(2).add(1) }
}
},
milestones: {
0: {
requirementDescription: "1 total good will",
effectDescription: "Start row 4 resets with 1000 fans",
done() { return player[this.layer].points.gte(1) }
},
1: {
requirementDescription: "2 total good will",
effectDescription: "Start row 4 resets with 1 of each social media account, and unlock a new Degree program",
done() { return player[this.layer].points.gte(2) }
},
2: {
requirementDescription: "3 total good will",
effectDescription: "Retain fame milestones and upgrades",
done() { return player[this.layer].points.gte(3) }
}
},
respec() {
player.g.upgrades = []
player.g.unused = player.g.points
doReset('g', true)
}
})

237
gamedevtree/js/layers/l.js Normal file
View file

@ -0,0 +1,237 @@
addLayer("l", {
name: "lectures",
symbol: "L",
color: lecturesColor,
branches: [ 's', 'f' ],
row: 3,
position: 4,
resource: "lectures",
baseResource: "fame",
infoboxes: {
lore: {
title: "lectures",
body: `Realizing you are both <span style="color: ${fameColor}">famous</span> and an extensive <span style="color: ${schoolColor}">multi-college education</span>, you have the bright idea of becoming an <span style="color: ${lecturesColor}">adjunt professor</span>. It won't benefit you directly, but with enough <span style="color: ${lecturesColor}">lectures</span> under your belt you should be able to start hiring <span style="color: ${lecturesColor}">Teacher Assistants</span> to <del>take advantage of</del> help you out.<br/><br/>` +
`Each <span style="color: ${lecturesColor}">TA</span> you unlock will gain experience over time - the more <span style="color: ${lecturesColor}">lectures</span> you teach, the more experience they can earn! As they level up they'll be able to boost different layers, in addition to their regular duty.`
}
},
resetDescription: "Teach ",
startData() { return {
unlocked: false,
points: new Decimal(0),
gabenExp: new Decimal(0),
gabenLevel: new Decimal(0),
lExp: new Decimal(0),
lLevel: new Decimal(0),
carmackExp: new Decimal(0),
carmackLevel: new Decimal(0),
thompsonExp: new Decimal(0),
thompsonLevel: new Decimal(0),
meierExp: new Decimal(0),
meierLevel: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || challengeCompletions("d", 12) > 0 },
type: "normal",
requires: new Decimal(13),
baseAmount() { return player.f.points },
exponent: 10,
gainMult() {
mult = new Decimal(1)
return mult
},
gainExp() {
return new Decimal(1)
},
effect() {
return player[this.layer].points.sqrt()
},
effectDescription() {
return `which generate ${format(this.effect())} experience for your TAs every second`
},
update(diff) {
const effect = this.effect().mul(diff)
if (hasUpgrade(this.layer, 11)) player[this.layer].gabenExp = player[this.layer].gabenExp.add(effect)
if (player[this.layer].gabenExp.gte(this.bars.gaben.cost())) {
player[this.layer].gabenLevel = player[this.layer].gabenLevel.add(1)
player[this.layer].gabenExp = new Decimal(0)
}
if (hasUpgrade(this.layer, 12)) player[this.layer].lExp = player[this.layer].lExp.add(effect)
if (player[this.layer].lExp.gte(this.bars.l.cost())) {
player[this.layer].lLevel = player[this.layer].lLevel.add(1)
player[this.layer].lExp = new Decimal(0)
}
if (hasUpgrade(this.layer, 13)) player[this.layer].carmackExp = player[this.layer].carmackExp.add(effect)
if (player[this.layer].carmackExp.gte(this.bars.carmack.cost())) {
player[this.layer].carmackLevel = player[this.layer].carmackLevel.add(1)
player[this.layer].carmackExp = new Decimal(0)
}
if (hasUpgrade(this.layer, 14)) player[this.layer].thompsonExp = player[this.layer].thompsonExp.add(effect)
if (player[this.layer].thompsonExp.gte(this.bars.thompson.cost())) {
player[this.layer].thompsonLevel = player[this.layer].thompsonLevel.add(1)
player[this.layer].thompsonExp = new Decimal(0)
}
if (hasUpgrade(this.layer, 15)) player[this.layer].meierExp = player[this.layer].meierExp.add(effect)
if (player[this.layer].meierExp.gte(this.bars.meier.cost())) {
player[this.layer].meierLevel = player[this.layer].meierLevel.add(1)
player[this.layer].meierExp = new Decimal(0)
}
},
roundUpCost: true,
hotkeys: [
{
key: "l",
description: "Press L to teach lectures",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
["display-text", () => inChallenge("d", 21) ? `<h2 style="color: red;">Disabled during ${layers.d.challenges[player.d.activeChallenge].name} degree plan</h2>` : ""],
"main-display",
"prestige-button",
"blank",
["display-text", "<h2>Gabriel Newell</h2>"],
["row", [["upgrade", 11], "blank", ["bar", "gaben"]]],
"blank",
["display-text", () => hasUpgrade("l", 11) ? "<h2>L</h2>" : ""],
["row", [["upgrade", 12], "blank", ["bar", "l"]]],
"blank",
["display-text", () => hasUpgrade("l", 12) ? "<h2>Jean Carmack</h2>" : ""],
["row", [["upgrade", 13], "blank", ["bar", "carmack"]]],
"blank",
["display-text", () => hasUpgrade("l", 13) ? "<h2>Jen Thompson</h2>" : ""],
["row", [["upgrade", 14], "blank", ["bar", "thompson"]]],
"blank",
["display-text", () => challengeCompletions("d", 22) > 0 ? "<h2>Sidney Meier</h2>" : ""],
["row", [["upgrade", 15], "blank", ["bar", "meier"]]]
],
bars: {
gaben: {
fillStyle: {'background-color' : "#1b2838"},
baseStyle: {'background-color' : "#171a21"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 400,
height: 140,
progress() {
return (player[this.layer].gabenExp.div(this.cost())).toNumber()
},
display() {
return `Current TA Level: ${formatWhole(player[this.layer].gabenLevel)}<br/><br/>${format(player[this.layer].gabenExp)} / ${formatWhole(this.cost())} to next level`
},
cost() { return new Decimal(4).pow(player[this.layer].gabenLevel).mul(2000) },
unlocked: true
},
l: {
fillStyle: {'background-color' : "#2B5293"},
baseStyle: {'background-color' : "#2b772b"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 400,
height: 140,
progress() {
return (player[this.layer].lExp.div(this.cost())).toNumber()
},
display() {
return `Current TA Level: ${formatWhole(player[this.layer].lLevel)}<br/><br/>${format(player[this.layer].lExp)} / ${formatWhole(this.cost())} to next level`
},
cost() { return new Decimal(100).pow(player[this.layer].lLevel).mul(2401) },
unlocked() { return hasUpgrade("l", 11) }
},
carmack: {
fillStyle: {'background-color' : "#cb5e29"},
baseStyle: {'background-color' : "#692f17"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 400,
height: 140,
progress() {
return (player[this.layer].carmackExp.div(this.cost())).toNumber()
},
display() {
return `Current TA Level: ${formatWhole(player[this.layer].carmackLevel)}<br/><br/>${format(player[this.layer].carmackExp)} / ${formatWhole(this.cost())} to next level`
},
cost() { return new Decimal(6).pow(player[this.layer].carmackLevel).mul(10000) },
unlocked() { return hasUpgrade("l", 12) }
},
thompson: {
fillStyle: {'background-color' : "#ffffff"},
baseStyle: {'background-color' : "#000000"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 400,
height: 140,
progress() {
return (player[this.layer].thompsonExp.div(this.cost())).toNumber()
},
display() {
return `Current TA Level: ${formatWhole(player[this.layer].thompsonLevel)}<br/><br/>${format(player[this.layer].thompsonExp)} / ${formatWhole(this.cost())} to next level`
},
cost() { return new Decimal(12).pow(player[this.layer].thompsonLevel).mul(50000) },
unlocked() { return hasUpgrade("l", 13) }
},
meier: {
fillStyle: {'background-color' : "#947728"},
baseStyle: {'background-color' : "#04467a"},
textStyle: {'color': '#04e050'},
borderStyle() {return {}},
direction: RIGHT,
width: 400,
height: 140,
progress() {
return (player[this.layer].meierExp.div(this.cost())).toNumber()
},
display() {
return `Current TA Level: ${formatWhole(player[this.layer].meierLevel)}<br/><br/>${format(player[this.layer].meierExp)} / ${formatWhole(this.cost())} to next level`
},
cost() { return new Decimal(12).pow(player[this.layer].meierLevel).mul(50000) },
unlocked() { return challengeCompletions("d", 22) > 0 }
}
},
upgrades: {
rows: 1,
cols: 5,
11: {
title: "Hire Gabriel",
cost: new Decimal(1),
description() { return "<br/>Gabriel will make upgrading hardware not spend any cash, and increase cash gain based on level<br/>" },
effect() { return inChallenge("d", 21) ? new Decimal(1) : new Decimal(2).pow(player[this.layer].gabenLevel) },
effectDisplay() { return `${format(this.effect())}x cash gain` }
},
12: {
title: "Hire L",
cost: new Decimal(50),
description() { return "<br/>L will autopurchase alt accounts and not spend any fame, and increase fame gain based on level<br/>" },
effect() { return inChallenge("d", 21) ? new Decimal(1) : player[this.layer].lLevel.add(1).pow(.9) },
effectDisplay() { return `${format(this.effect())}x fame gain` },
unlocked() { return hasUpgrade("l", 11) }
},
13: {
title: "Hire Jean",
cost: new Decimal(2000),
description() { return "<br/>Jean will square updates gain, and increase updates gain based on level<br/>" },
effect() { return inChallenge("d", 21) ? new Decimal(1) : new Decimal(1.75).pow(player[this.layer].carmackLevel) },
effectDisplay() { return `${format(this.effect())}x update gain` },
unlocked() { return hasUpgrade("l", 12) }
},
14: {
title: "Hire Jen",
cost: new Decimal(60000),
description() { return "<br/>Jen will make taking classes not spend any cash, and increase enrollments gain based on level<br/>" },
effect() { return inChallenge("d", 21) ? new Decimal(1) : new Decimal(1.5).pow(player[this.layer].thompsonLevel) },
effectDisplay() { return `${format(this.effect())}x enrollments gain` },
unlocked() { return hasUpgrade("l", 13) }
},
15: {
title: "Hire Sidney",
cost: new Decimal(1200000),
description() { return "<br/>Sidney will square good will affect, and increase good will gain based on level<br/>" },
effect() { return inChallenge("d", 21) ? new Decimal(1) : new Decimal(1.05).pow(player[this.layer].meierLevel) },
effectDisplay() { return `${format(this.effect())}x good will gain` },
unlocked() { return challengeCompletions("d", 22) > 0 }
}
}
})

220
gamedevtree/js/layers/r.js Normal file
View file

@ -0,0 +1,220 @@
addLayer("r", {
name: "refactors",
symbol: "R",
color: refactoringColor,
branches: [ 'e' ],
row: 2,
position: 1,
resource: "refactors",
baseResource: "experience",
infoboxes: {
lore: {
title: "refactors",
body: `After working on a game for so long, you start to realize some initial design decisions haven't really scaled well over so many countless <span style="color: ${updatesColor}">updates</span>.<br/><br/>` +
"You do, however, have some ideas on how you would've structured it to better support the features you've since added, and will continue to add. All you have to do is rewrite a couple parts of it.<br/><br/>" +
`You'll take a minor setback on your <span style="color: ${experienceColor}">experience</span> on a changing codebase, but it'll pay off dividends in the long run!<br/><br/>` +
`This layer also unlocks "<span style="color: ${refactoringColor}">Refactorings</span>": Enabling these actively refactors your codebase, increasing your productivity based on the hours worked. However, the productivity slow down will be much quicker due to the grueling work.`
}
},
resetDescription: "Re-design your game framework for ",
startData() { return {
unlocked: false,
total: new Decimal(0),
points: new Decimal(0),
renameVariablesHoursWorked: new Decimal(0),
encapsulateFieldHoursWorked: new Decimal(0),
optimizeFormulasHoursWorked: new Decimal(0),
rollLibraryHoursWorked: new Decimal(0)
}},
layerShown() { return player[this.layer].unlocked || player.u.best.gte(30) },
type: "static",
requires: new Decimal(500),
base: new Decimal(2.5),
baseAmount() { return player.e.points },
exponent: 1.25,
gainMult() {
mult = new Decimal(1)
if (hasUpgrade("f", 13) && hasUpgrade("g", 12) && !inChallenge("d", 11)) mult = mult.div(upgradeEffect("f", 13))
if (hasUpgrade("g", 21) && !inChallenge("d", 11)) mult = mult.div(upgradeEffect("g", 21))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
canBuyMax() { return hasMilestone("a", 1) && !inChallenge("d", 12) },
effect() { return player[this.layer].points.pow(player[this.layer].points.mul(1.5)).add(1) },
effectDescription() {
return `multiplying all bonuses based on total experience by ${format(this.effect())}x.`
},
doReset(resettingLayer) {
if (['a', 't', 'd'].includes(resettingLayer)) {
const keep = []
if (hasMilestone("a", 2) && !inChallenge("d", 12)) keep.push('milestones')
if (hasMilestone("a", 4) && !inChallenge("d", 12)) keep.push('renameVariablesHoursWorked', 'encapsulateFieldHoursWorked', 'optimizeFormulasHoursWorked', 'rollLibraryHoursWorked')
layerDataReset(this.layer, keep)
if (hasMilestone("a", 0) && !hasMilestone("a", 2) && !inChallenge("d", 12)) {
player[this.layer].milestones.push(0)
player[this.layer].milestones.push(1)
player[this.layer].milestones.push(3)
player[this.layer].milestones.push(7)
}
}
},
resetsNothing() { return challengeCompletions("d", 21) > 0 },
hotkeys: [
{
key: "r",
description: "Press R to re-design your game framework",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
"prestige-button",
"blank",
"clickables",
"blank",
["display-text", () => `You've performed a total of ${player.r.total} refactors`],
"milestones"
],
update(diff) {
if (getClickableState("r", 11))
player.r.renameVariablesHoursWorked = player.r.renameVariablesHoursWorked.add(tmp.pointGen.mul(diff))
else if (getClickableState("r", 12))
player.r.encapsulateFieldHoursWorked = player.r.encapsulateFieldHoursWorked.add(tmp.pointGen.mul(diff))
else if (getClickableState("r", 13))
player.r.optimizeFormulasHoursWorked = player.r.optimizeFormulasHoursWorked.add(tmp.pointGen.mul(diff))
else if (getClickableState("r", 14))
player.r.rollLibraryHoursWorked = player.r.rollLibraryHoursWorked.add(tmp.pointGen.mul(diff))
},
clickables: {
rows: 1,
cols: 4,
11: {
title: "Rename variables",
display: function() {
return `Take time to rename your variables more sensibly, making your productivity slow down even stronger but you gain a boost to productivity based on hours of work produced with this active.\n\nCurrently: ${format(clickableEffect("r", 11))}x productivity`
},
effect: function() {
if (player.r.renameVariablesHoursWorked.lessThan(1)) return new Decimal(1)
return player.r.renameVariablesHoursWorked.log(10).pow(2).add(1).pow(buyableEffect("a", 11))
},
unlocked() { return hasMilestone("r", 0) },
canClick: function() { return !getClickableState("r", 12) && !getClickableState("r", 13) && !getClickableState("r", 14) },
onClick: function() {
setClickableState("r", 11, !getClickableState("r", 11))
},
style: {
"height": "180px",
"width": "200px"
}
},
12: {
title: "Encapsulate fields",
display: function() {
return `Take time to comply with arbitrary programming practices, making your productivity slow down even more strongly but you gain another boost to productivity based on hours of work produced with this active.\n\nCurrently: ${format(clickableEffect("r", 12))}x productivity`
},
effect: function() {
if (player.r.encapsulateFieldHoursWorked.lessThan(1)) return new Decimal(1)
return player.r.encapsulateFieldHoursWorked.log(10).pow(2).add(1).pow(buyableEffect("a", 11))
},
unlocked() { return hasMilestone("r", 1) },
canClick: function() { return !getClickableState("r", 11) && !getClickableState("r", 13) && !getClickableState("r", 14) },
onClick: function() {
setClickableState("r", 12, !getClickableState("r", 12))
},
style: {
"height": "180px",
"width": "200px"
}
},
13: {
title: "Optimize formulas",
display: function() {
return `Take time to figure out how to get that darn bottleneck to O(1), making your productivity slow down like really strongly but you gain yet another boost to productivity based on hours of work produced with this active.\n\nCurrently: ${format(clickableEffect("r", 13))}x productivity`
},
effect: function() {
if (player.r.optimizeFormulasHoursWorked.lessThan(1)) return new Decimal(1)
return player.r.optimizeFormulasHoursWorked.log(10).pow(2).add(1).pow(buyableEffect("a", 11))
},
unlocked() { return hasMilestone("r", 3) },
canClick: function() { return !getClickableState("r", 11) && !getClickableState("r", 12) && !getClickableState("r", 14) },
onClick: function() {
setClickableState("r", 13, !getClickableState("r", 13))
},
style: {
"height": "180px",
"width": "200px"
}
},
14: {
title: "Roll your own library",
display: function() {
return `Take time to replace that slow library with your own, making your productivity slow down most strongly but you gain, surprising no one, another boost to productivity based on hours of work produced with this active.\n\nCurrently: ${format(clickableEffect("r", 14))}x productivity`
},
effect: function() {
if (player.r.rollLibraryHoursWorked.lessThan(1)) return new Decimal(1)
return player.r.rollLibraryHoursWorked.log(10).pow(2).add(1).pow(buyableEffect("a", 11))
},
unlocked() { return hasMilestone("r", 7) },
canClick: function() { return !getClickableState("r", 11) && !getClickableState("r", 12) && !getClickableState("r", 13) },
onClick: function() {
setClickableState("r", 14, !getClickableState("r", 14))
},
style: {
"height": "180px",
"width": "200px"
}
}
},
milestones: {
0: {
requirementDescription: "1 refactor",
effectDescription: "Unlock first refactoring, and retain the second and third Experience upgrades",
done() { return player[this.layer].total.gte(1) || (player.a.unlocked && !inChallenge("d", 12)) }
},
1: {
requirementDescription: "2 refactors",
effectDescription: "Unlock second refactoring",
done() { return player[this.layer].total.gte(2) || (player.a.unlocked && !inChallenge("d", 12)) }
},
2: {
requirementDescription: "3 refactors",
effectDescription: "Retain the first Experience upgrade",
done() { return player[this.layer].total.gte(3) },
unlocked() { return player[this.layer].total.gte(1) || (player.a.unlocked && !inChallenge("d", 12)) }
},
3: {
requirementDescription: "4 refactors",
effectDescription: "Unlock third refactoring",
done() { return player[this.layer].total.gte(4) },
unlocked() { return player[this.layer].total.gte(2) || (player.a.unlocked && !inChallenge("d", 12)) }
},
4: {
requirementDescription: "5 refactors",
effectDescription: "Retain the fourth Experience upgrade",
done() { return player[this.layer].total.gte(5) },
unlocked() { return player[this.layer].total.gte(3) || (player.a.unlocked && !inChallenge("d", 12)) }
},
5: {
requirementDescription: "6 refactors",
effectDescription: "Retain the fifth Experience upgrade",
done() { return player[this.layer].total.gte(6) },
unlocked() { return player[this.layer].total.gte(4) || (player.a.unlocked && !inChallenge("d", 12)) }
},
6: {
requirementDescription: "7 refactors",
effectDescription: "Retain the sixth Experience upgrade",
done() { return player[this.layer].total.gte(7) },
unlocked() { return player[this.layer].total.gte(5) || (player.a.unlocked && !inChallenge("d", 12)) }
},
7: {
requirementDescription: "8 refactors",
effectDescription: "Unlock fourth refactoring",
done() { return player[this.layer].total.gte(8) },
unlocked() { return player[this.layer].total.gte(6) || (player.a.unlocked && !inChallenge("d", 12)) }
}
}
})

210
gamedevtree/js/layers/s.js Normal file
View file

@ -0,0 +1,210 @@
addLayer("s", {
name: "school",
symbol: "S",
color: schoolColor,
branches: [ 'e', 'c' ],
row: 2,
position: 2,
resource: "enrollments",
baseResource: "experience",
infoboxes: {
lore: {
title: "school",
body: `With all this <span style="color: ${experienceColor}">programming experience</span>, you think you finally have what it takes to go to college. It'll take a lot of <span style="color: ${cashColor}">cash</span>, and require a lot of <span style="color: ${experienceColor}">experience</span> to get accepted, but the payoff for a formal education should maybe probably help you make games faster.<br/><br/>` +
`Each class has very powerful effects, but costs an exorbitant amount of <span style="color: ${cashColor}">cash</span>. Enrolling in multiple colleges helps you take further advantage of this knowledge.<br/><br/>` +
`Additionally, the more classes you take the more you should be able to write quick automation scripts to push out <span style="color: ${updatesColor}">builds</span> and such automatically!`
}
},
resetDescription: "Apply to another college for ",
startData() { return {
unlocked: false,
best: new Decimal(0),
points: new Decimal(0),
classes: new Decimal(0),
time: new Decimal(0),
"auto-update": false,
"auto-upgradehardware": false,
"auto-experience": false,
"auto-cash": false
}},
layerShown() { return player[this.layer].unlocked || (player.r.total.gte(1) && player.f.best.gte(1)) },
type: "static",
requires: new Decimal(1e6),
base: new Decimal(8),
baseAmount() { return player.e.points },
exponent: 1.2,
gainMult() {
mult = new Decimal(1)
if (hasUpgrade("g", 22) && !inChallenge("d", 11)) mult = mult.div(upgradeEffect("g", 22))
if (hasUpgrade("l", 14)) mult = mult.div(upgradeEffect("l", 14))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
effect() {
return new Decimal(1).add(new Decimal(0.05).mul(player.s.points))
},
effectDescription() {
return `which raise your class effects to the ^${format(this.effect())} power.`
},
doReset(resettingLayer) {
if (['t', 'd', 'l'].includes(resettingLayer)) {
layerDataReset(this.layer, ["auto-both", "auto-cash", "auto-experience", "auto-update", "auto-upgradehardware"])
const buyablesAmount = layers.d.effect()
setBuyableAmount("s", 11, buyablesAmount)
setBuyableAmount("s", 12, buyablesAmount)
setBuyableAmount("s", 21, buyablesAmount)
setBuyableAmount("s", 22, buyablesAmount)
updateMilestones(this.layer)
}
},
resetsNothing() { return hasMilestone("d", 2) },
hotkeys: [
{
key: "s",
description: "Press S to apply for college",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
"prestige-button",
"blank",
"buyables",
"blank",
["display-text", () => `You've taken a total of ${player.s.classes.add(layers.d.effect().times(4))} classes`],
"milestones"
],
update(diff) {
if (hasMilestone("s", 1) && player.s["auto-update"] && canReset("u")) {
doReset("u")
}
player.s.time = player.s.time.add(diff)
if (player.s.time.gte(new Decimal(1).div(buyableEffect("s", 21)))) {
if (hasMilestone("s", 2) && player.s["auto-upgradehardware"]) {
while (buyBuyable("c", 11)) { }
}
if (hasMilestone("s", 4)) {
if (hasMilestone("d", 1)) {
if (player.s["auto-both"] && (canReset("e") || canReset("c"))) {
if (hasMilestone("s", 5)) {
const mul = player.s.time.div(new Decimal(1).div(buyableEffect("s", 21))).sqrt()
addPoints("e", getResetGain("e").mul(mul))
addPoints("c", getResetGain("c").mul(mul))
} else doReset("e")
}
} else {
if (player.s["auto-experience"] && canReset("e")) {
doReset("e")
}
if (player.s["auto-cash"] && canReset("c")) {
doReset("c")
}
}
}
player.s.time = new Decimal(0)
}
},
shouldNotify() {
if (!player.s.best.gt(0)) return false
return canAffordPurchase("c", layers[this.layer].buyables[11], layers[this.layer].buyables[11].cost()) ||
canAffordPurchase("c", layers[this.layer].buyables[12], layers[this.layer].buyables[12].cost()) ||
canAffordPurchase("c", layers[this.layer].buyables[21], layers[this.layer].buyables[21].cost()) ||
canAffordPurchase("c", layers[this.layer].buyables[22], layers[this.layer].buyables[22].cost())
},
buyables: {
rows: 2,
cols: 2,
11: {
title: "CS 1337 Computer Science",
cost() { return getBuyableAmount("s", 11).sub(layers.d.effect()).add(6).pow10() },
display() { return `Each class additively raises the effectiveness of experience on productivity to the power of +.025<br/><br/>Currently: ^${format(this.effect())}<br/><br/>Next upgrade cost: ${format(this.cost())} cash` },
canAfford() { return player.c.points.gte(this.cost()) && player.s.best.gte(1) },
effect() { return getBuyableAmount("s", 11).pow(layers.s.effect()).mul(0.025).add(1) },
buy() {
if (!hasUpgrade("l", 14) || inChallenge("d", 21)) player.c.points = player.c.points.sub(this.cost())
setBuyableAmount("s", 11, getBuyableAmount("s", 11).add(1))
player[this.layer].classes = player[this.layer].classes.add(1)
}
},
12: {
title: "CS 2305 Discrete Math",
cost() { return getBuyableAmount("s", 12).sub(layers.d.effect()).mul(2).add(8).pow10() },
display() { return `Each class divides the productivity slowdown modifier exponent by 1.05<br/><br/>Currently: ^${format(this.effect())}<br/><br/>Next upgrade cost: ${format(this.cost())} cash` },
canAfford() { return player.c.points.gte(this.cost()) && player.s.best.gte(1) },
effect() { return new Decimal(1).div(new Decimal(1.05).pow(getBuyableAmount("s", 12))) },
buy() {
if (!hasUpgrade("l", 14) || inChallenge("d", 21)) player.c.points = player.c.points.sub(this.cost())
setBuyableAmount("s", 12, getBuyableAmount("s", 12).add(1))
player[this.layer].classes = player[this.layer].classes.add(1)
}
},
21: {
title: "CS 3354 Software Engineering",
cost() { return getBuyableAmount("s", 21).sub(layers.d.effect()).mul(3).add(9).pow10() },
display() { return `Each class additively speeds this layer's automation milestones by 50%<br/><br/>Currently: x${format(this.effect())}<br/><br/>Next upgrade cost: ${format(this.cost())} cash` },
canAfford() { return player.c.points.gte(this.cost()) && player.s.best.gte(1) },
effect() { return new Decimal(.5).mul(getBuyableAmount("s", 21).pow(layers.s.effect())).add(1) },
buy() {
if (!hasUpgrade("l", 14) || inChallenge("d", 21)) player.c.points = player.c.points.sub(this.cost())
setBuyableAmount("s", 21, getBuyableAmount("s", 21).add(1))
player[this.layer].classes = player[this.layer].classes.add(1)
}
},
22: {
title: "CS 4352 Human Computer Interactions",
cost() { return getBuyableAmount("s", 22).sub(layers.d.effect()).mul(4).add(10).pow10() },
display() { return `Each class increases the effect of upgrading hardware by +.05<br/><br/>Currently: +${format(this.effect())}<br/><br/>Next upgrade cost: ${format(this.cost())} cash` },
canAfford() { return player.c.points.gte(this.cost()) && player.s.best.gte(1) },
effect() { return getBuyableAmount("s", 22).pow(layers.s.effect()).mul(0.05) },
buy() {
if (!hasUpgrade("l", 14) || inChallenge("d", 21)) player.c.points = player.c.points.sub(this.cost())
setBuyableAmount("s", 22, getBuyableAmount("s", 22).add(1))
player[this.layer].classes = player[this.layer].classes.add(1)
}
}
},
milestones: {
0: {
requirementDescription: "1 class taken",
effectDescription: "Retain all Update upgrades",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(1) }
},
1: {
requirementDescription: "3 classes taken",
effectDescription: "Automatically reset Update layer",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(3) },
toggles: [["s", "auto-update"]],
unlocked() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(1) }
},
2: {
requirementDescription: "5 classes taken",
effectDescription: "Automatically buy Upgrade Hardware every second",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(5) },
toggles: [["s", "auto-upgradehardware"]],
unlocked() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(3) }
},
3: {
requirementDescription: "7 classes taken",
effectDescription: "Retain Cash Revenue upgrades",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(7) },
unlocked() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(5) }
},
4: {
requirementDescription: "9 classes taken",
effectDescription: "Automatically reset Experience and Cash layers every second<br/>(Recommended to only do 1 at a time)",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(9) },
toggles() { return hasMilestone("d", 1) ? [["s", "auto-both"]] : [["s", "auto-experience"], ["s", "auto-cash"]] },
unlocked() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(7) }
},
5: {
requirementDescription: "25 classes taken",
effectDescription: "Experience and Cash layers reset nothing, and the previous milestone can reset multiple times per tick, with diminishing returns",
done() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(25) },
unlocked() { return player[this.layer].classes.add(layers.d.effect().times(4)).gte(9) }
}
}
})

274
gamedevtree/js/layers/t.js Normal file
View file

@ -0,0 +1,274 @@
addLayer("t", {
name: "time flux",
symbol: "T",
color: timeFluxColor,
branches: [ 'r', 's' ],
row: 3,
position: 2,
resource: "time flux",
baseResource: "refactors",
infoboxes: {
lore: {
title: "time flux",
body: `In a discussion with an advisor from yet another <span style="color: ${schoolColor}">college</span>, you notice an interesting course in the advanced Computer Science degree: <span style="color: ${timeFluxColor}">Chronomancy</span>. Interest piqued, you enroll and start learning about methods of <span style="color: ${timeFluxColor}">time manipulation</span> (which is remarkably similar to <span style="color: ${refactoringColor}">refactoring</span>. Who knew!). Now that could be useful!<br/><br/>` +
`<span style="color: ${timeFluxColor}">Time flux</span> can be used to charge up <span style="color: ${timeFluxColor}">rings</span> that can produce <span style="color: ${timeFluxColor}">time shards</span>, which in turn can speed up <span style="color: ${timeFluxColor}">time</span>. <span style="color: ${timeFluxColor}">Rings</span> can be placed inside each other using more <span style="color: ${timeFluxColor}">time flux</span>, and make the <span style="color: ${timeFluxColor}">inner ring</span> work faster.`
}
},
resetDescription: "Refactor time for ",
startData() { return {
unlocked: false,
points: new Decimal(0),
shards: new Decimal(0),
rings: new Array(8).fill(new Decimal(0))
}},
layerShown() { return player[this.layer].unlocked || challengeCompletions("d", 11) > 0 },
type: "normal",
requires: new Decimal(21),
baseAmount() { return player.r.points },
exponent: 25,
gainMult() {
mult = new Decimal(1)
mult = mult.mul(player.r.points.sub(21).pow(1.5).add(1))
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
effect() {
if (inChallenge("d", 22)) return 1
let highestRing = player.t.rings.findIndex(r => r.lte(0))
if (highestRing == -1) highestRing = 8
highestRing++
return player[this.layer].shards.clampMin(1).log10().pow(Decimal.div(highestRing, 4)).add(1).toNumber()
},
update(diff) {
player[this.layer].rings[7] = player[this.layer].rings[7].add(buyableEffect(this.layer, 91).rate.mul(diff))
player[this.layer].rings[6] = player[this.layer].rings[6].add(buyableEffect(this.layer, 81).rate.mul(diff))
player[this.layer].rings[5] = player[this.layer].rings[5].add(buyableEffect(this.layer, 71).rate.mul(diff))
player[this.layer].rings[4] = player[this.layer].rings[4].add(buyableEffect(this.layer, 61).rate.mul(diff))
player[this.layer].rings[3] = player[this.layer].rings[3].add(buyableEffect(this.layer, 51).rate.mul(diff))
player[this.layer].rings[2] = player[this.layer].rings[2].add(buyableEffect(this.layer, 41).rate.mul(diff))
player[this.layer].rings[1] = player[this.layer].rings[1].add(buyableEffect(this.layer, 31).rate.mul(diff))
player[this.layer].rings[0] = player[this.layer].rings[0].add(buyableEffect(this.layer, 21).rate.mul(diff))
player[this.layer].shards = player[this.layer].shards.add(buyableEffect(this.layer, 11).mul(diff))
},
hotkeys: [
{
key: "t",
description: "Press T to refactor time",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
["display-text", () => inChallenge("d", 22) ? `<h2 style="color: red;">Disabled during ${layers.d.challenges[player.d.activeChallenge].name} degree plan</h2>` : ""],
"main-display",
"prestige-button",
"blank",
["display-text", () => `You have ${format(player.t.shards)} time shards, speeding up time by ${format(layers.t.effect())}x`],
"blank",
"buyables"
],
buyables: {
rows: 9,
cols: 1,
11: {
title: "1st Ring",
display() {
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double time shards generation.<br/>Currently: ${format(this.effect())}/sec<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Generate time shards.<br/>Currently: ${format(this.effect())}/sec<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(1).pow(1.5)).pow(x || getBuyableAmount(this.layer, this.id)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
if (getBuyableAmount(this.layer, this.id).gt(0))
return new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).times(buyableEffect(this.layer, 21).multiplier)
return new Decimal(0)
},
style: { width: "600px", height: "120px" }
},
21: {
title: "2nd Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 1st Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 1st Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(2).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[0].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 31).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
31: {
title: "3rd Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 2nd Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 2nd Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(3).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[1].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 41).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
41: {
title: "4th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 3rd Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 3rd Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(4).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[2].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 51).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
51: {
title: "5th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 4th Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 4th Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(5).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[3].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 61).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
61: {
title: "6th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 5th Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 5th Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(6).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[4].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 71).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
71: {
title: "7th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 6th Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 6th Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(7).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[5].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 81).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
81: {
title: "8th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 7th Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 7th Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(8).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[6].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)).mul(buyableEffect("t", 91).multiplier) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" }
},
91: {
title: "9th Ring",
display() {
const effect = this.effect()
if (getBuyableAmount(this.layer, this.id).gt(0))
return `Double how quickly the multiplier to the 8th Ring increases.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
return `Power up the 8th Ring over time.<br/>Currently: ${format(effect.multiplier)}x (+${format(effect.rate)}/sec)<br/>Requires ${formatWhole(this.cost())} time flux.`
},
cost(x) { return new Decimal(2).pow(new Decimal(9).pow(1.5)).pow(Decimal.add(x || getBuyableAmount(this.layer, this.id), 1)) },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, new Decimal(1).add(getBuyableAmount(this.layer, this.id)))
},
effect() {
return {
multiplier: player.t.rings[7].add(1),
rate: getBuyableAmount(this.layer, this.id).gt(0) ? new Decimal(2).pow(getBuyableAmount(this.layer, this.id).sub(1)) : new Decimal(0)
}
},
style: { width: "600px", height: "120px" },
unlocked() { return challengeCompletions("d", 21) > 0 }
}
}
})

138
gamedevtree/js/layers/u.js Normal file
View file

@ -0,0 +1,138 @@
addLayer("u", {
name: "updates",
symbol: "U",
color: updatesColor,
row: 0,
position: 0,
resource: "updates",
baseResource: "hours of work",
infoboxes: {
lore: {
title: "updates",
body: "You've started working on this great little game idea you've had kicking around for awhile! Unfortunately, the longer you work on it the less your productivity seems to translate into hours of work :/<br/><br/>" +
"Also, if you're familiar with other TPT mods, you should know this one works differently: layers are only reset along branches!"
}
},
resetDescription: "Release new build for ",
startData() { return {
unlocked: true,
best: new Decimal(0),
points: new Decimal(0),
}},
layerShown: true,
type: "static",
requires: new Decimal(5),
base: new Decimal(5),
baseAmount() { return player.points },
exponent: 0.5,
gainMult() {
mult = new Decimal(1).div(buyableEffect("f", 12))
if (hasUpgrade("u", 21)) mult = mult.div(2)
if (hasUpgrade("e", 11)) mult = mult.div(upgradeEffect("e", 11))
if (hasUpgrade("l", 13)) mult = mult.div(upgradeEffect("l", 13))
if (!inChallenge("d", 11)) {
if (hasUpgrade("u", 31) && hasUpgrade("g", 13)) mult = mult.div(10)
if (hasUpgrade("u", 41) && hasUpgrade("g", 13)) mult = mult.div(upgradeEffect("u", 41))
if (hasUpgrade("u", 32) && hasUpgrade("g", 13)) mult = mult.pow(2)
if (hasUpgrade("u", 42) && hasUpgrade("g", 13)) mult = mult.pow(upgradeEffect("u", 42))
}
mult = mult.pow(buyableEffect("a", 13))
if (hasUpgrade("l", 13) && !inChallenge("d", 21)) mult = mult.pow(2)
return mult
},
gainExp() {
return new Decimal(1)
},
roundUpCost: true,
canBuyMax() { return true },
doReset(resettingLayer) {
if (resettingLayer != 'u') {
const keep = [ 'best' ]
if (hasMilestone("s", 0)) keep.push('upgrades')
layerDataReset(this.layer, keep)
}
},
resetsNothing() { return hasMilestone("d", 0) },
hotkeys: [
{
key: "u",
description: "Press U to release a new build",
onPress() { if (canReset(this.layer)) doReset(this.layer) }
}
],
tabFormat: [
["infobox", "lore"],
"main-display",
"prestige-button",
"blank",
"upgrades"
],
upgrades: {
rows: 4,
cols: 2,
11: {
title: "Convince your friend to help",
description: "Double your productivity by convincing your friend to help with your game",
cost: new Decimal(5),
currencyDisplayName: "hours of work",
currencyInternalName: "points",
currencyLocation: ""
},
12: {
title: "Create a github repo",
description: "Increase your productivity by a massive 50% by opening the floodgates to countless open source developers",
cost: new Decimal(1),
unlocked() { return hasUpgrade("u", 11) }
},
21: {
title: "Bug fixes count as updates, right?",
description: "Double your update gain by counting the follow-up bug fixing patch as a separate update",
cost: new Decimal(20),
currencyDisplayName: "hours of work",
currencyInternalName: "points",
currencyLocation: "",
unlocked() { return hasUpgrade("u", 11) }
},
22: {
title: "Motivation Momentum",
description: "Increase productivity by how many current updates have been released",
cost: new Decimal(3),
effect() { return player.u.points.add(1) },
unlocked() { return hasUpgrade("u", 12) }
},
31: {
title: "Cosmetics Economy",
description: "Let the community make and sell cosmetics in-game, giving 10x update gain",
cost: new Decimal(25000),
unlocked() { return hasUpgrade("g", 13) && !inChallenge("d", 11) }
},
32: {
title: "Workshop Support",
description: "Add support for community made mods, squaring update gain",
cost: new Decimal(50000),
unlocked() { return hasUpgrade("g", 13) && !inChallenge("d", 11) }
},
41: {
title: "Featured creators",
description: "Make a featured section for popular community creators, multiplying update gain based on the amount of good will",
cost: new Decimal(100000),
effect() {
let ret = player.g.unused
if (challengeCompletions("d", 12) > 0) ret = ret.add(player.g.points.sub(player.g.unused).div(2))
return ret.pow10()
},
unlocked() { return hasUpgrade("g", 13) && !inChallenge("d", 11) }
},
42: {
title: "Community Updates",
description: "Bundle a bunch of mods together and release them as an \"update\", raising update gain to a power based on the amount of good will",
cost: new Decimal(250000),
effect() {
let ret = player.g.unused
if (challengeCompletions("d", 12) > 0) ret = ret.add(player.g.points.sub(player.g.unused).div(2))
return ret.add(1)
},
unlocked() { return hasUpgrade("g", 13) && !inChallenge("d", 11) }
}
}
})

106
gamedevtree/js/mod.js Normal file
View file

@ -0,0 +1,106 @@
let modInfo = {
name: "The Game Dev Tree",
id: "gamedevtree",
author: "thepaperpilot",
pointsName: "hours of work",
endgame: new Decimal("e50"),
discordName: "The Paper Pilot Community Server",
discordLink: "https://discord.gg/WzejVAx",
changelogLink: "https://github.com/thepaperpilot/The-Modding-Tree/blob/gamedevtree/changelog.md",
offlineLimit: 5, // In hours
initialStartPoints: new Decimal (0) // Used for hard resets and new players
}
// Set your version in num and name
let VERSION = {
num: "1.0.4",
name: "Version Bump [rebalanced,debuggedx3]",
}
// If you add new functions anywhere inside of a layer, and those functions have an effect when called, add them here.
// (The ones here are examples, all official functions are already taken care of)
var doNotCallTheseFunctionsEveryTick = ["blowUpEverything", "onStart"]
function getStartPoints(){
return new Decimal(modInfo.initialStartPoints)
}
// Determines if it should show points/sec
function canGenPoints(){
return true
}
// Calculate points/sec!
function getPointGen() {
if(!canGenPoints())
return new Decimal(0)
let gain = layers.e.effect()
gain = gain.pow(buyableEffect("c", 11))
gain = gain.mul(clickableEffect("r", 11))
gain = gain.mul(clickableEffect("r", 12))
gain = gain.mul(clickableEffect("r", 13))
gain = gain.mul(clickableEffect("r", 14))
gain = gain.mul(layers.f.effect().productivityMult)
if (hasUpgrade("u", 11)) gain = gain.mul(2)
if (hasUpgrade("u", 12)) gain = gain.mul(1.5)
if (hasUpgrade("u", 22)) gain = gain.mul(upgradeEffect("u", 22))
for (let r = 1; r <= 2; r++)
for (let c = 1; c <= 4; c++)
if (hasUpgrade("c", r * 10 + c)) gain = gain.mul(2)
// Apply productivity slow downs
let slowDownModifier = player.points.add(gain.sqrt()).sub(player.e.total.times(layers.r.effect())).clampMin(0).div(buyableEffect("a", 12)).pow(buyableEffect("s", 12)).clampMin(1)
gain = gain.divide(slowDownModifier.sqrt())
gain = gain.divide(slowDownModifier.sqrt().clampMin(10).log10().pow(2))
slowDownModifier = slowDownModifier.pow(buyableEffect("a", 22))
if (getClickableState("r", 11)) {
gain = gain.divide(slowDownModifier.pow(.25))
} else if (getClickableState("r", 12)) {
gain = gain.divide(slowDownModifier.pow(.25))
gain = gain.divide(slowDownModifier.pow(.125))
} else if (getClickableState("r", 13)) {
gain = gain.divide(slowDownModifier.pow(.25))
gain = gain.divide(slowDownModifier.pow(.125))
gain = gain.divide(slowDownModifier.pow(.0625))
} else if (getClickableState("r", 14)) {
gain = gain.divide(slowDownModifier.pow(.25))
gain = gain.divide(slowDownModifier.pow(.125))
gain = gain.divide(slowDownModifier.pow(.0625))
gain = gain.divide(slowDownModifier.pow(.03125))
}
return gain
}
// You can add non-layer related variables that should to into "player" and be saved here, along with default values
function addedPlayerData() { return {
hqTree: true
}}
// Display extra things at the top of the page
var displayThings = [
"<br/>",
() => player.points < 24 * 3 ? "<br/>" :
player.points < 24 * 365 * 3 ? `equivalent to ${format(player.points.div(24))} days of work` :
player.points < 24 * 365 * 300 ? `equivalent to ${format(player.points.div(24 * 365))} years of work` :
player.points < 24 * 365 * 3000000 ? `equivalent to ${format(player.points.div(24 * 365 * 100))} centuries of work` :
player.points < 24 * 365 * 3000000000 ? `equivalent to ${format(player.points.div(24 * 365 * 1000000))} epochs of work` :
new Decimal(24 * 365).times("3e1000").gte(player.points) ? `equivalent to ${format(player.points.div(24 * 365 * 1000000000))} eons of work` :
`equivalent to heat death ^${format(player.points.log(new Decimal(24).mul(365).mul("1e1000")))} of work`
]
// Determines when the game "ends"
function isEndgame() {
return hasUpgrade("d", 11)
}
// Less important things beyond this point!
// You can change this if you have things that can be messed up by long tick lengths
function maxTickLength() {
return(3600000000) // Default is 1 hour which is just arbitrarily large
}

160
gamedevtree/js/temp.js Normal file
View file

@ -0,0 +1,160 @@
var tmp = {}
// Tmp will not call these
var activeFunctions = [
"startData", "onPrestige", "doReset", "update", "automate",
"buy", "buyMax", "respec", "onComplete", "onPurchase", "onPress", "onClick", "masterButtonPress",
"sellOne", "sellAll",
]
var noCall = doNotCallTheseFunctionsEveryTick
for (item in noCall) {
activeFunctions.push(noCall[item])
}
function setupTemp() {
tmp = {}
tmp.pointGen = {}
tmp.displayThings = []
setupTempData(layers, tmp)
for (layer in layers){
tmp[layer].resetGain = {}
tmp[layer].nextAt = {}
tmp[layer].nextAtDisp = {}
tmp[layer].notify = {}
tmp[layer].canReset = {}
tmp[layer].activeChallenge = {}
tmp[layer].prestigeButtonText = {}
setupBarStyles(layer)
}
}
function setupTempData(layerData, tmpData) {
for (item in layerData){
if (layerData[item] == null) {
tmpData[item] = null
}
else if (layerData[item] instanceof Decimal)
tmpData[item] = layerData[item]
else if (Array.isArray(layerData[item])) {
tmpData[item] = []
setupTempData(layerData[item], tmpData[item])
}
else if ((!!layerData[item]) && (layerData[item].constructor === Object)) {
tmpData[item] = {}
setupTempData(layerData[item], tmpData[item])
}
else if (isFunction(layerData[item]) && !activeFunctions.includes(item)){
tmpData[item] = new Decimal(1) // The safest thing to put probably?
} else {
tmpData[item] = layerData[item]
}
}
}
function updateTemp() {
if (tmp === undefined)
setupTemp()
updateTempData(layers, tmp)
for (layer in layers){
tmp[layer].resetGain = getResetGain(layer)
tmp[layer].nextAt = getNextAt(layer)
tmp[layer].nextAtDisp = getNextAt(layer, true)
tmp[layer].notify = shouldNotify(layer)
tmp[layer].canReset = canReset(layer)
tmp[layer].activeChallenge = player[layer].activeChallenge
tmp[layer].prestigeButtonText = prestigeButtonText(layer)
constructBarStyles(layer)
}
tmp.pointGen = getPointGen()
tmp.displayThings = []
for (thing in displayThings){
let text = displayThings[thing]
if (isFunction(text)) text = text()
tmp.displayThings.push(text)
}
}
function updateTempData(layerData, tmpData) {
for (item in layerData){
if (Array.isArray(layerData[item])) {
updateTempData(layerData[item], tmpData[item])
}
else if ((!!layerData[item]) && (layerData[item].constructor === Object)) {
updateTempData(layerData[item], tmpData[item])
}
else if (isFunction(layerData[item]) && !activeFunctions.includes(item)){
Vue.set(tmpData, item, layerData[item]())
}
}
}
function updateChallengeTemp(layer)
{
updateTempData(layers[layer].challenges, tmp[layer].challenges)
}
function updateBuyableTemp(layer)
{
updateTempData(layers[layer].buyables, tmp[layer].buyables)
}
function updateClickableTemp(layer)
{
updateTempData(layers[layer].clickables, tmp[layer].clickables)
}
var DIR_MARGINS = ["margin-bottom", "margin-top", "margin-right", "margin-left"]
function constructBarStyles(layer){
if (layers[layer].bars === undefined)
return
for (id in layers[layer].bars){
if (id !== "layer") {
let bar = tmp[layer].bars[id]
if (bar.progress instanceof Decimal)
bar.progress = bar.progress.toNumber()
bar.progress = (1 -Math.min(Math.max(bar.progress, 0), 1)) * 100
bar.dims = {'width': bar.width + "px", 'height': bar.height + "px"}
let dir = bar.direction
bar.fillDims = {'width': (bar.width + 0.5) + "px", 'height': (bar.height + 0.5) + "px"}
if (dir !== undefined)
{
bar.fillDims['clip-path'] = 'inset(0% 50% 0% 0%)'
if(dir == UP){
bar.fillDims['clip-path'] = 'inset(' + bar.progress + '% 0% 0% 0%)'
}
else if(dir == DOWN){
bar.fillDims['clip-path'] = 'inset(0% 0% ' + bar.progress + '% 0%)'
}
else if(dir == RIGHT){
bar.fillDims['clip-path'] = 'inset(0% ' + bar.progress + '% 0% 0%)'
}
else if(dir == LEFT){
bar.fillDims['clip-path'] = 'inset(0% 0% 0% ' + bar.progress + '%)'
}
}
}
}
}
function setupBarStyles(layer){
if (layers[layer].bars === undefined)
return
for (id in layers[layer].bars){
let bar = tmp[layer].bars[id]
bar.dims = {}
let dir = bar.direction
bar.fillDims = {}
}
}

696
gamedevtree/js/utils.js Normal file
View file

@ -0,0 +1,696 @@
// ************ Number formatting ************
function exponentialFormat(num, precision) {
let e = num.log10().floor()
let m = num.div(Decimal.pow(10, e))
if(m.toStringWithDecimalPlaces(precision) == 10) {
m = new Decimal(1)
e = e.add(1)
}
return m.toStringWithDecimalPlaces(precision)+"e"+commaFormat(e, 0)
}
function commaFormat(num, precision) {
if (num === null || num === undefined) return "NaN"
if (num.mag < 0.001) return (0).toFixed(precision)
return num.toStringWithDecimalPlaces(precision).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
function regularFormat(num, precision) {
if (num === null || num === undefined) return "NaN"
if (num.eq(0)) return (0).toFixed(precision)
if (num.mag < 0.01) return num.toExponential(precision)
return num.toStringWithDecimalPlaces(precision)
}
function fixValue(x, y = 0) {
return x || new Decimal(y)
}
function sumValues(x) {
x = Object.values(x)
if (!x[0]) return new Decimal(0)
return x.reduce((a, b) => Decimal.add(a, b))
}
function format(decimal, precision=2) {
decimal = new Decimal(decimal)
if (isNaN(decimal.sign)||isNaN(decimal.layer)||isNaN(decimal.mag)) {
console.trace(decimal + " is NaN")
player.hasNaN = true;
return "NaN"
}
if (decimal.sign<0) return "-"+format(decimal.neg(), precision)
if (decimal.mag == Number.POSITIVE_INFINITY) return "Infinity"
if (decimal.gte("eeee1000")) {
var slog = decimal.slog()
if (slog.gte(1e6)) return "F" + format(slog.floor())
else return Decimal.pow(10, slog.sub(slog.floor())).toStringWithDecimalPlaces(3) + "F" + commaFormat(slog.floor(), 0)
} else if (decimal.gte("1e1000")) return exponentialFormat(decimal, 0)
else if (decimal.gte(1e9)) return exponentialFormat(decimal, precision)
else if (decimal.gte(1e3)) return commaFormat(decimal, 0)
else return regularFormat(decimal, precision)
}
function formatWhole(decimal) {
decimal = new Decimal(decimal)
if (decimal.gte(1e9)) return format(decimal, 2)
if (decimal.lte(0.95) && !decimal.eq(0)) return format(decimal, 2)
return format(decimal, 0)
}
function formatTime(s) {
if (s<60) return format(s)+"s"
else if (s<3600) return formatWhole(Math.floor(s/60))+"m "+format(s%60)+"s"
else return formatWhole(Math.floor(s/3600))+"h "+formatWhole(Math.floor(s/60)%60)+"m "+format(s%60)+"s"
}
function toPlaces(x, precision, maxAccepted) {
x = new Decimal(x)
let result = x.toStringWithDecimalPlaces(precision)
if (new Decimal(result).gte(maxAccepted)) {
result = new Decimal(maxAccepted-Math.pow(0.1, precision)).toStringWithDecimalPlaces(precision)
}
return result
}
// ************ Save stuff ************
function save() {
localStorage.setItem(modInfo.id, btoa(JSON.stringify(player)))
}
function startPlayerBase() {
return {
tab: "tree",
time: Date.now(),
autosave: true,
notify: {},
msDisplay: "always",
offlineProd: true,
versionType: modInfo.id,
version: VERSION.num,
beta: VERSION.beta,
timePlayed: 0,
keepGoing: false,
hasNaN: false,
hideChallenges: false,
showStory: true,
points: modInfo.initialStartPoints,
subtabs: {},
}
}
function getStartPlayer() {
playerdata = startPlayerBase()
if (addedPlayerData) {
extradata = addedPlayerData()
for (thing in extradata)
playerdata[thing] = extradata[thing]
}
playerdata.infoboxes = {}
for (layer in layers){
playerdata[layer] = layers[layer].startData()
playerdata[layer].buyables = getStartBuyables(layer)
if(playerdata[layer].clickables == undefined) playerdata[layer].clickables = getStartClickables(layer)
playerdata[layer].spentOnBuyables = new Decimal(0)
playerdata[layer].upgrades = []
playerdata[layer].milestones = []
playerdata[layer].achievements = []
playerdata[layer].challenges = getStartChallenges(layer)
if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) {
playerdata.subtabs[layer] = {}
playerdata.subtabs[layer].mainTabs = Object.keys(layers[layer].tabFormat)[0]
}
if (layers[layer].microtabs) {
if (playerdata.subtabs[layer] == undefined) playerdata.subtabs[layer] = {}
for (item in layers[layer].microtabs)
playerdata.subtabs[layer][item] = Object.keys(layers[layer].microtabs[item])[0]
}
if (layers[layer].infoboxes) {
if (playerdata.infoboxes[layer] == undefined) playerdata.infoboxes[layer] = {}
for (item in layers[layer].infoboxes)
playerdata.infoboxes[layer][item] = false
}
}
return playerdata
}
function getStartBuyables(layer){
let data = {}
if (layers[layer].buyables) {
for (id in layers[layer].buyables)
if (!isNaN(id))
data[id] = new Decimal(0)
}
return data
}
function getStartClickables(layer){
let data = {}
if (layers[layer].clickables) {
for (id in layers[layer].clickables)
if (!isNaN(id))
data[id] = ""
}
return data
}
function getStartChallenges(layer){
let data = {}
if (layers[layer].challenges) {
for (id in layers[layer].challenges)
if (!isNaN(id))
data[id] = 0
}
return data
}
function fixSave() {
defaultData = getStartPlayer()
fixData(defaultData, player)
for(layer in layers)
{
if (player[layer].best !== undefined) player[layer].best = new Decimal (player[layer].best)
if (player[layer].total !== undefined) player[layer].total = new Decimal (player[layer].total)
if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) {
if(!Object.keys(layers[layer].tabFormat).includes(player.subtabs[layer].mainTabs)) player.subtabs[layer].mainTabs = Object.keys(layers[layer].tabFormat)[0]
}
if (layers[layer].microtabs) {
for (item in layers[layer].microtabs)
if(!Object.keys(layers[layer].microtabs[item]).includes(player.subtabs[layer][item])) player.subtabs[layer][item] = Object.keys(layers[layer].microtabs[item])[0]
}
}
}
function fixData(defaultData, newData) {
for (item in defaultData){
if (defaultData[item] == null) {
if (newData[item] === undefined)
newData[item] = null
}
else if (Array.isArray(defaultData[item])) {
if (newData[item] === undefined)
newData[item] = defaultData[item]
else
fixData(defaultData[item], newData[item])
}
else if (defaultData[item] instanceof Decimal) { // Convert to Decimal
if (newData[item] === undefined)
newData[item] = defaultData[item]
else
newData[item] = new Decimal(newData[item])
}
else if ((!!defaultData[item]) && (defaultData[item].constructor === Object)) {
if (newData[item] === undefined || (defaultData[item].constructor !== Object))
newData[item] = defaultData[item]
else
fixData(defaultData[item], newData[item])
}
else {
if (newData[item] === undefined)
newData[item] = defaultData[item]
}
}
}
function load() {
let get = localStorage.getItem(modInfo.id);
if (get===null || get===undefined) player = getStartPlayer()
else player = Object.assign(getStartPlayer(), JSON.parse(atob(get)))
fixSave()
player.tab = "tree"
if (player.offlineProd) {
if (player.offTime === undefined) player.offTime = { remain: 0 }
player.offTime.remain += (Date.now() - player.time) / 1000
}
player.time = Date.now();
versionCheck();
changeTheme();
changeTreeQuality();
updateLayers()
setupTemp();
updateTemp();
updateTemp();
loadVue();
}
function exportSave() {
let str = btoa(JSON.stringify(player))
const el = document.createElement("textarea");
el.value = str;
document.body.appendChild(el);
el.select();
el.setSelectionRange(0, 99999);
document.execCommand("copy");
document.body.removeChild(el);
}
function importSave(imported=undefined, forced=false) {
if (imported===undefined) imported = prompt("Paste your save here")
try {
tempPlr = Object.assign(getStartPlayer(), JSON.parse(atob(imported)))
if(tempPlr.versionType != modInfo.id && !forced && !confirm("This save appears to be for a different mod! Are you sure you want to import?")) // Wrong save (use "Forced" to force it to accept.)
return
player = tempPlr;
player.versionType = modInfo.id
fixSave()
save()
window.location.reload()
} catch(e) {
return;
}
}
function versionCheck() {
let setVersion = true
if (player.versionType===undefined||player.version===undefined) {
player.versionType = modInfo.id
player.version = 0
}
if (setVersion) {
if (player.versionType == modInfo.id && VERSION.num > player.version) player.keepGoing = false
player.versionType = getStartPlayer().versionType
player.version = VERSION.num
player.beta = VERSION.beta
}
}
var saveInterval = setInterval(function() {
if (player===undefined) return;
if (gameEnded&&!player.keepGoing) return;
if (player.autosave) save();
}, 5000)
// ************ Themes ************
const themes = {
1: "aqua"
}
const theme_names = {
aqua: "Aqua"
}
function changeTheme() {
let aqua = player.theme == "aqua"
colors_theme = colors[player.theme || "default"]
document.body.style.setProperty('--background', aqua ? "#001f3f" : "#2a323d")
document.body.style.setProperty('--background_tooltip', aqua ? "rgba(0, 15, 31, 0.75)" : "rgba(0, 0, 0, 0.75)")
document.body.style.setProperty('--color', aqua ? "#bfdfff" : "#dfdfdf")
document.body.style.setProperty('--points', aqua ? "#dfefff" : "#ffffff")
document.body.style.setProperty("--locked", aqua ? "#c4a7b3" : "#bf8f8f")
}
function getThemeName() {
return player.theme ? theme_names[player.theme] : "Default"
}
function switchTheme() {
if (player.theme === undefined) player.theme = themes[1]
else {
player.theme = themes[Object.keys(themes)[player.theme] + 1]
if (!player.theme) delete player.theme
}
changeTheme()
resizeCanvas()
}
// ************ Options ************
function toggleOpt(name) {
if (name == "oldStyle" && styleCooldown>0) return;
player[name] = !player[name]
if (name == "hqTree") changeTreeQuality()
if (name == "oldStyle") updateStyle()
}
var styleCooldown = 0;
function updateStyle() {
styleCooldown = 1;
let css = document.getElementById("styleStuff")
css.href = player.oldStyle?"oldStyle.css":"style.css"
needCanvasUpdate = true;
}
function changeTreeQuality() {
var on = player.hqTree
document.body.style.setProperty('--hqProperty1', on ? "2px solid" : "4px solid")
document.body.style.setProperty('--hqProperty2a', on ? "-4px -4px 4px rgba(0, 0, 0, 0.25) inset" : "-4px -4px 4px rgba(0, 0, 0, 0) inset")
document.body.style.setProperty('--hqProperty2b', on ? "0px 0px 20px var(--background)" : "")
document.body.style.setProperty('--hqProperty3', on ? "2px 2px 4px rgba(0, 0, 0, 0.25)" : "none")
}
function toggleAuto(toggle) {
player[toggle[0]][toggle[1]] = !player[toggle[0]][toggle[1]]
}
function adjustMSDisp() {
let displays = ["always", "automation", "incomplete", "never"];
player.msDisplay = displays[(displays.indexOf(player.msDisplay)+1)%4]
}
function milestoneShown(layer, id) {
complete = player[layer].milestones.includes(id)
auto = layers[layer].milestones[id].toggles
switch(player.msDisplay) {
case "always":
return true;
break;
case "automation":
return (auto)||!complete
break;
case "incomplete":
return !complete
break;
case "never":
return false;
break;
}
return false;
}
// ************ Big Feature related ************
function respecBuyables(layer) {
if (!layers[layer].buyables) return
if (!layers[layer].buyables.respec) return
if (!confirm("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
layers[layer].buyables.respec()
updateBuyableTemp(layer)
}
function canAffordUpgrade(layer, id) {
let upg = tmp[layer].upgrades[id]
let cost = tmp[layer].upgrades[id].cost
return canAffordPurchase(layer, upg, cost)
}
function hasUpgrade(layer, id){
return (player[layer].upgrades.includes(toNumber(id)) || player[layer].upgrades.includes(id.toString()))
}
function hasMilestone(layer, id){
return (player[layer].milestones.includes(toNumber(id)) || player[layer].milestones.includes(id.toString()))
}
function hasAchievement(layer, id){
return (player[layer].achievements.includes(toNumber(id)) || player[layer].achievements.includes(id.toString()))
}
function hasChallenge(layer, id){
return (player[layer].challenges[id])
}
function challengeCompletions(layer, id){
return (player[layer].challenges[id])
}
function getBuyableAmount(layer, id){
return (player[layer].buyables[id])
}
function setBuyableAmount(layer, id, amt){
player[layer].buyables[id] = amt
}
function getClickableState(layer, id){
return (player[layer].clickables[id])
}
function setClickableState(layer, id, state){
player[layer].clickables[id] = state
}
function upgradeEffect(layer, id){
return (tmp[layer].upgrades[id].effect)
}
function challengeEffect(layer, id){
return (tmp[layer].challenges[id].effect)
}
function buyableEffect(layer, id){
return (tmp[layer].buyables[id].effect)
}
function clickableEffect(layer, id){
return (tmp[layer].clickables[id].effect)
}
function achievementEffect(layer, id){
return (tmp[layer].achievements[id].effect)
}
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 (!player[layer].unlocked) return
if (!tmp[layer].upgrades[id].unlocked) return
if (player[layer].upgrades.includes(id)) return
let upg = tmp[layer].upgrades[id]
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)
}
player[layer].upgrades.push(id);
if (upg.onPurchase != undefined)
upg.onPurchase()
}
function buyMaxBuyable(layer, id) {
if (!player[layer].unlocked) return false
if (!tmp[layer].buyables[id].unlocked) return false
if (!tmp[layer].buyables[id].canAfford) return false
if (!layers[layer].buyables[id].buyMax) return false
layers[layer].buyables[id].buyMax()
updateBuyableTemp(layer)
return true
}
function buyBuyable(layer, id) {
if (!player[layer].unlocked) return false
if (!tmp[layer].buyables[id].unlocked) return false
if (!tmp[layer].buyables[id].canAfford) return false
layers[layer].buyables[id].buy()
updateBuyableTemp(layer)
return true
}
function clickClickable(layer, id) {
if (!player[layer].unlocked) return false
if (!tmp[layer].clickables[id].unlocked) return false
if (!tmp[layer].clickables[id].canClick) return false
layers[layer].clickables[id].onClick()
updateClickableTemp(layer)
return true
}
// Function to determine if the player is in a challenge
function inChallenge(layer, id){
let challenge = player[layer].activeChallenge
if (challenge == null) return
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
var toTreeTab = name == "tree"
player.tab = name
if (toTreeTab != onTreeTab) {
requestAnimationFrame(() => {
document.getElementById("treeTab").className = toTreeTab ? "fullWidth" : "col left"
resizeCanvas()
})
onTreeTab = toTreeTab
}
delete player.notify[name]
}
function layOver(obj1, obj2) {
for (let x in obj2) {
if (obj2[x] instanceof Object && !(obj2[x] instanceof Decimal)) layOver(obj1[x], obj2[x]);
else obj1[x] = obj2[x];
}
}
function notifyLayer(name) {
if (player.tab == name || !layerunlocked(name)) return
player.notify[name] = 1
}
function nodeShown(layer) {
if (tmp[layer].layerShown) return true
switch(layer) {
case "idk":
return player.l.unlocked
break;
}
return false
}
function layerunlocked(layer) {
return LAYERS.includes(layer) && (player[layer].unlocked || (tmp[layer].baseAmount.gte(tmp[layer].requires) && tmp[layer].layerShown))
}
function keepGoing() {
player.keepGoing = true;
showTab("tree")
}
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 (!(player[layer].milestones.includes(id)) && layers[layer].milestones[id].done())
player[layer].milestones.push(id)
}
}
function updateAchievements(layer){
for (id in layers[layer].achievements){
if (!isNaN(id) && !(player[layer].achievements.includes(id)) && layers[layer].achievements[id].done()) {
player[layer].achievements.push(id)
if (layers[layer].achievements[id].onComplete) layers[layer].achievements[id].onComplete()
}
}
}
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
}
document.onkeydown = function(e) {
if (player===undefined) return;
if (gameEnded&&!player.keepGoing) return;
let shiftDown = e.shiftKey
let ctrlDown = e.ctrlKey
let key = e.key
if (ctrlDown) key = "ctrl+" + key
if (onFocused) return
if (ctrlDown && hotkeys[key]) e.preventDefault()
if(hotkeys[key]){
if (player[hotkeys[key].layer].unlocked)
hotkeys[key].onPress()
}
}
var onFocused = false
function focused(x) {
onFocused = x
}
function prestigeButtonText(layer)
{
if(tmp[layer].type == "normal")
return `${ player[layer].points.lt(1e3) ? (tmp[layer].resetDescription !== undefined ? tmp[layer].resetDescription : "Reset for ") : ""}+<b>${formatWhole(tmp[layer].resetGain)}</b> ${tmp[layer].resource} ${tmp[layer].resetGain.lt(100) && player[layer].points.lt(1e3) ? `<br><br>Next at ${ (tmp[layer].roundUpCost ? formatWhole(tmp[layer].nextAt) : format(tmp[layer].nextAt))} ${ tmp[layer].baseResource }` : ""}`
else if(tmp[layer].type== "static")
return `${tmp[layer].resetDescription !== undefined ? tmp[layer].resetDescription : "Reset for "}+<b>${formatWhole(tmp[layer].resetGain)}</b> ${tmp[layer].resource}<br><br>${player[layer].points.lt(30) ? (tmp[layer].baseAmount.gte(tmp[layer].nextAt)&&(tmp[layer].canBuyMax !== undefined) && tmp[layer].canBuyMax?"Next:":"Req:") : ""} ${formatWhole(tmp[layer].baseAmount)} / ${(tmp[layer].roundUpCost ? formatWhole(tmp[layer].nextAtDisp) : format(tmp[layer].nextAtDisp))} ${ tmp[layer].baseResource }
`
else if(tmp[layer].type == "none")
return ""
else
return layers[layer].prestigeButtonText()
}
function isFunction(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
};
document.title = modInfo.name

461
gamedevtree/js/v.js Normal file
View file

@ -0,0 +1,461 @@
var app;
function loadVue() {
// data = a function returning the content (actually HTML)
Vue.component('display-text', {
props: ['layer', 'data'],
template: `
<span class="instant" v-html="data"></span>
`
})
// data = a function returning the content (actually HTML)
Vue.component('raw-html', {
props: ['layer', 'data'],
template: `
<span class="instant" v-html="data"></span>
`
})
// Blank space, data = optional height in px or pair with width and height in px
Vue.component('blank', {
props: ['layer', 'data'],
template: `
<div class = "instant">
<div class = "instant" v-if="!data" v-bind:style="{'width': '8px', 'height': '17px'}"></div>
<div class = "instant" v-else-if="Array.isArray(data)" v-bind:style="{'width': data[0], 'height': data[1]}"></div>
<div class = "instant" v-else v-bind:style="{'width': '8px', 'height': data}"><br></div>
</div>
`
})
// Displays an image, data is the URL
Vue.component('display-image', {
props: ['layer', 'data'],
template: `
<img class="instant" v-bind:src= "data" v-bind:alt= "data">
`
})
// data = an array of Components to be displayed in a row
Vue.component('row', {
props: ['layer', 'data'],
template: `
<div class="upgTable instant" >
<div class="upgRow">
<div v-for="item in data">
<div v-if="!Array.isArray(item)" v-bind:is="item" :layer= "layer" v-bind:style="tmp[layer].componentStyles[item]"></div>
<div v-else-if="item.length==3" v-bind:style="[tmp[layer].componentStyles[item[0]], (item[2] ? item[2] : {})]" v-bind:is="item[0]" :layer= "layer" :data= "item[1]"></div>
<div v-else-if="item.length==2" v-bind:is="item[0]" :layer= "layer" :data= "item[1]" v-bind:style="tmp[layer].componentStyles[item[0]]"></div>
</div>
</div>
</div>
`
})
// data = an array of Components to be displayed in a column
Vue.component('column', {
props: ['layer', 'data'],
template: `
<div class="upgTable instant">
<div class="upgCol">
<div v-for="item in data">
<div v-if="!Array.isArray(item)" v-bind:is="item" :layer= "layer" v-bind:style="tmp[layer].componentStyles[item]"></div>
<div v-else-if="item.length==3" v-bind:style="[tmp[layer].componentStyles[item[0]], (item[2] ? item[2] : {})]" v-bind:is="item[0]" :layer= "layer" :data= "item[1]"></div>
<div v-else-if="item.length==2" v-bind:is="item[0]" :layer= "layer" :data= "item[1]" v-bind:style="tmp[layer].componentStyles[item[0]]"></div>
</div>
</div>
</div>
`
})
Vue.component('infobox', {
props: ['layer', 'data'],
template: `
<div class="story instant" v-if="tmp[layer].infoboxes && tmp[layer].infoboxes[data]!== undefined && tmp[layer].infoboxes[data].unlocked" v-bind:style="[{'border-color': tmp[layer].color, 'border-radius': player.infoboxes[layer][data] ? 0 : '8px'}, tmp[layer].infoboxes[data].style]">
<button class="story-title" v-bind:style="[{'background-color': tmp[layer].color}, tmp[layer].infoboxes[data].titleStyle]"
v-on:click="player.infoboxes[layer][data] = !player.infoboxes[layer][data]">
<span class="story-toggle">{{player.infoboxes[layer][data] ? "+" : "-"}}</span>
<span v-html="tmp[layer].infoboxes[data].title ? tmp[layer].infoboxes[data].title : (tmp[layer].name)"></span>
</button>
<div v-if="!player.infoboxes[layer][data]" class="story-text" v-bind:style="tmp[layer].infoboxes[data].bodyStyle">
<span v-html="tmp[layer].infoboxes[data].body ? tmp[layer].infoboxes[data].body : 'Blah'"></span>
</div>
</div>
`
})
// Data = width in px, by default fills the full area
Vue.component('h-line', {
props: ['layer', 'data'],
template:`
<hr class="instant" v-bind:style="data ? {'width': data} : {}" class="hl">
`
})
// Data = height in px, by default is bad
Vue.component('v-line', {
props: ['layer', 'data'],
template: `
<div class="instant" v-bind:style="data ? {'height': data} : {}" class="vl2"></div>
`
})
Vue.component('challenges', {
props: ['layer'],
template: `
<div v-if="tmp[layer].challenges" class="upgTable">
<div v-for="row in tmp[layer].challenges.rows" class="upgRow">
<div v-for="col in tmp[layer].challenges.cols">
<challenge v-if="tmp[layer].challenges[row*10+col]!== undefined && tmp[layer].challenges[row*10+col].unlocked" :layer = "layer" :data = "row*10+col" v-bind:style="tmp[layer].componentStyles.challenge"></challenge>
</div>
</div>
</div>
`
})
// data = id
Vue.component('challenge', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].challenges && tmp[layer].challenges[data]!== undefined && tmp[layer].challenges[data].unlocked && !(player.hideChallenges && hasChallenge(layer, [data]))" v-bind:class="{hChallenge: true, done: hasChallenge(layer, data), canComplete: player[layer].activeChallenge == data&&!hasChallenge(layer, data)&&canCompleteChallenge(layer, data)}" v-bind:style="tmp[layer].challenges[data].style">
<br><h3 v-html="tmp[layer].challenges[data].name"></h3><br><br>
<button v-bind:class="{ longUpg: true, can: true, [layer]: true }" v-bind:style="{'background-color': tmp[layer].color}" v-on:click="startChallenge(layer, data)">{{tmp[layer].activeChallenge==(data)?(canCompleteChallenge(layer, data)?"Finish":"Exit Early"):(hasChallenge(layer, data)?"Completed":"Start")}}</button><br><br>
<span v-html="tmp[layer].challenges[data].challengeDescription"></span><br>
Goal: {{format(tmp[layer].challenges[data].goal)}} {{tmp[layer].challenges[data].currencyDisplayName ? tmp[layer].challenges[data].currencyDisplayName : "points"}}<br>
Reward: <span v-html="tmp[layer].challenges[data].rewardDescription"></span><br>
<span v-if="tmp[layer].challenges[data].rewardEffect!==undefined">Currently: <span v-html="(tmp[layer].challenges[data].rewardDisplay) ? (tmp[layer].challenges[data].rewardDisplay) : format(tmp[layer].challenges[data].rewardEffect)"></span></span>
</div>
`
})
Vue.component('upgrades', {
props: ['layer'],
template: `
<div v-if="tmp[layer].upgrades" class="upgTable">
<div v-for="row in tmp[layer].upgrades.rows" class="upgRow">
<div v-for="col in tmp[layer].upgrades.cols"><div v-if="tmp[layer].upgrades[row*10+col]!== undefined && tmp[layer].upgrades[row*10+col].unlocked" class="upgAlign">
<upgrade :layer = "layer" :data = "row*10+col" v-bind:style="tmp[layer].componentStyles.upgrade"></upgrade>
</div></div>
</div>
<br>
</div>
`
})
// data = id
Vue.component('upgrade', {
props: ['layer', 'data'],
template: `
<button v-if="tmp[layer].upgrades && tmp[layer].upgrades[data]!== undefined && tmp[layer].upgrades[data].unlocked" v-on:click="buyUpg(layer, data)" v-bind:class="{ [layer]: true, upg: true, bought: hasUpgrade(layer, data), locked: (!(canAffordUpgrade(layer, data))&&!hasUpgrade(layer, data)), can: (canAffordUpgrade(layer, data)&&!hasUpgrade(layer, data))}"
v-bind:style="[((!hasUpgrade(layer, data) && canAffordUpgrade(layer, data)) ? {'background-color': tmp[layer].color} : {}), tmp[layer].upgrades[data].style]">
<span v-if= "tmp[layer].upgrades[data].title"><h3 v-html="tmp[layer].upgrades[data].title"></h3><br></span>
<span v-html="tmp[layer].upgrades[data].description"></span>
<span v-if="tmp[layer].upgrades[data].effect"><br>Currently: <span v-html="(tmp[layer].upgrades[data].effectDisplay) ? (tmp[layer].upgrades[data].effectDisplay) : format(tmp[layer].upgrades[data].effect)"></span></span>
<br><br>Cost: {{ formatWhole(tmp[layer].upgrades[data].cost) }} {{(tmp[layer].upgrades[data].currencyDisplayName ? tmp[layer].upgrades[data].currencyDisplayName : tmp[layer].resource)}}
</button>
`
})
Vue.component('milestones', {
props: ['layer'],
template: `
<div v-if="tmp[layer].milestones">
<table>
<tr v-for="id in Object.keys(tmp[layer].milestones)"><div v-if="tmp[layer].milestones[id]!== undefined && tmp[layer].milestones[id].unlocked"
<milestone :layer = "layer" :data = "id" v-bind:style="tmp[layer].componentStyles.milestone"></milestone>
</tr></div>
</table>
<br>
</div>
`
})
// data = id
Vue.component('milestone', {
props: ['layer', 'data'],
template: `
<td v-if="tmp[layer].milestones && tmp[layer].milestones[data]!== undefined && milestoneShown(layer, data)" v-bind:style="[(!tmp[layer].milestones[data].unlocked) ? {'visibility': 'hidden'} : {}, tmp[layer].milestones[data].style]" v-bind:class="{milestone: !hasMilestone(layer, data), milestoneDone: hasMilestone(layer, data)}">
<h3 v-html="tmp[layer].milestones[data].requirementDescription"></h3><br>
<span v-html="tmp[layer].milestones[data].effectDescription"></span><br>
<span v-if="(tmp[layer].milestones[data].toggles)&&(hasMilestone(layer, data))" v-for="toggle in tmp[layer].milestones[data].toggles"><toggle :layer= "layer" :data= "toggle" v-bind:style="tmp[layer].componentStyles.toggle"></toggle>&nbsp;</span></td></tr>
`
})
Vue.component('toggle', {
props: ['layer', 'data'],
template: `
<button class="smallUpg can" v-bind:style="{'background-color': tmp[data[0]].color}" v-on:click="toggleAuto(data)">{{player[data[0]][data[1]]?"ON":"OFF"}}</button>
`
})
// data = function to return the text describing the reset before the amount gained (optional)
Vue.component('prestige-button', {
props: ['layer', 'data'],
template: `
<button v-if="(tmp[layer].type !== 'none')" v-bind:class="{ [layer]: true, reset: true, locked: !tmp[layer].canReset, can: tmp[layer].canReset}"
v-bind:style="[tmp[layer].canReset ? {'background-color': tmp[layer].color} : {}, tmp[layer].componentStyles['prestige-button']]"
v-html="tmp[layer].prestigeButtonText" v-on:click="doReset(layer)">
</button>
`
})
// Displays the main resource for the layer
Vue.component('main-display', {
props: ['layer'],
template: `
<div><span v-if="player[layer].points.lt('1e1000')">You have </span><h2 v-bind:style="{'color': tmp[layer].color, 'text-shadow': '0px 0px 10px' + tmp[layer].color}">{{formatWhole(player[layer].points)}}</h2> {{tmp[layer].resource}}<span v-if="tmp[layer].effectDescription">, {{tmp[layer].effectDescription}}</span><br><br></span>
`
})
// Displays the base resource for the layer, as well as the best and total values for the layer's currency, if tracked
Vue.component('resource-display', {
props: ['layer'],
template: `
<div style="margin-top: -13px">
<span v-if="tmp[layer].type=='normal' && tmp[layer].resetGain.lt(100) && player[layer].points.lt(1e3)"><br>You have {{formatWhole(tmp[layer].baseAmount)}} {{tmp[layer].baseResource}}</span>
<br><br>
<span v-if="player[layer].best != undefined">Your best {{tmp[layer].resource}} is {{formatWhole(player[layer].best)}}<br></span>
<span v-if="player[layer].total != undefined">You have made a total of {{formatWhole(player[layer].total)}} {{tmp[layer].resource}}<br></span>
</div>
`
})
// data = button size, in px
Vue.component('buyables', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].buyables" class="upgTable">
<respec-button v-if="tmp[layer].buyables.respec && !(tmp[layer].buyables.showRespec !== undefined && tmp[layer].buyables.showRespec == false)" :layer = "layer" v-bind:style="[{'margin-bottom': '12px'}, tmp[layer].componentStyles['respec-button']]"></respec-button>
<div v-for="row in tmp[layer].buyables.rows" class="upgRow">
<div v-for="col in tmp[layer].buyables.cols"><div v-if="tmp[layer].buyables[row*10+col]!== undefined && tmp[layer].buyables[row*10+col].unlocked" class="upgAlign" v-bind:style="{'margin-left': '7px', 'margin-right': '7px', 'height': (data ? data : 'inherit'),}">
<buyable :layer = "layer" :data = "row*10+col" :size = "data"></buyable>
</div></div>
<br>
</div>
</div>
`
})
// data = id of buyable
Vue.component('buyable', {
props: ['layer', 'data', 'size'],
template: `
<div v-if="tmp[layer].buyables && tmp[layer].buyables[data]!== undefined && tmp[layer].buyables[data].unlocked" style="display: grid">
<button v-bind:class="{ buyable: true, can: tmp[layer].buyables[data].canAfford, locked: !tmp[layer].buyables[data].canAfford}"
v-bind:style="[tmp[layer].buyables[data].canAfford ? {'background-color': tmp[layer].color} : {}, size ? {'height': size, 'width': size} : {}, tmp[layer].componentStyles.buyable, tmp[layer].buyables[data].style]"
v-on:click="buyBuyable(layer, data)">
<span v-if= "tmp[layer].buyables[data].title"><h2 v-html="tmp[layer].buyables[data].title"></h2><br></span>
<span v-bind:style="{'white-space': 'pre-line'}" v-html="tmp[layer].buyables[data].display"></span>
</button>
<br v-if="(tmp[layer].buyables[data].sellOne !== undefined && !(tmp[layer].buyables[data].canSellOne !== undefined && tmp[layer].buyables[data].canSellOne == false)) || (tmp[layer].buyables[data].sellAll && !(tmp[layer].buyables[data].canSellAll !== undefined && tmp[layer].buyables[data].canSellAll == false))">
<sell-one :layer="layer" :data="data" v-bind:style="tmp[layer].componentStyles['sell-one']" v-if="(tmp[layer].buyables[data].sellOne)&& !(tmp[layer].buyables[data].canSellOne !== undefined && tmp[layer].buyables[data].canSellOne == false)"></sell-one>
<sell-all :layer="layer" :data="data" v-bind:style="tmp[layer].componentStyles['sell-all']" v-if="(tmp[layer].buyables[data].sellAll)&& !(tmp[layer].buyables[data].canSellAll !== undefined && tmp[layer].buyables[data].canSellAll == false)"></sell-all>
</div>
`
})
Vue.component('respec-button', {
props: ['layer', 'data'],
template: `
<button v-if="tmp[layer].buyables && tmp[layer].buyables.respec && !(tmp[layer].buyables.showRespec !== undefined && tmp[layer].buyables.showRespec == false)" v-on:click="respecBuyables(layer)" v-bind:class="{ longUpg: true, can: player[layer].unlocked, locked: !player[layer].unlocked }">{{tmp[layer].buyables.respecText ? tmp[layer].buyables.respecText : "Respec"}}</button>
`
})
// data = button size, in px
Vue.component('clickables', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].clickables" class="upgTable">
<master-button v-if="tmp[layer].clickables.masterButtonPress && !(tmp[layer].clickables.showMasterButton !== undefined && tmp[layer].clickables.showMasterButton == false)" :layer = "layer" v-bind:style="[{'margin-bottom': '12px'}, tmp[layer].componentStyles['master-button']]"></master-button>
<div v-for="row in tmp[layer].clickables.rows" class="upgRow">
<div v-for="col in tmp[layer].clickables.cols"><div v-if="tmp[layer].clickables[row*10+col]!== undefined && tmp[layer].clickables[row*10+col].unlocked" class="upgAlign" v-bind:style="{'margin-left': '7px', 'margin-right': '7px', 'height': (data ? data : 'inherit'),}">
<clickable :layer = "layer" :data = "row*10+col" :size = "data" v-bind:style="tmp[layer].componentStyles.clickable"></clickable>
</div></div>
<br>
</div>
</div>
`
})
// data = id of clickable
Vue.component('clickable', {
props: ['layer', 'data', 'size'],
template: `
<button
v-if="tmp[layer].clickables && tmp[layer].clickables[data]!== undefined && tmp[layer].clickables[data].unlocked"
v-bind:class="{ upg: true, can: tmp[layer].clickables[data].canClick, locked: !tmp[layer].clickables[data].canClick}"
v-bind:style="[tmp[layer].clickables[data].canClick ? {'background-color': tmp[layer].color} : {}, size ? {'height': size, 'width': size} : {}, tmp[layer].clickables[data].style]"
v-on:click="clickClickable(layer, data)">
<span v-if= "tmp[layer].clickables[data].title"><h2 v-html="tmp[layer].clickables[data].title"></h2><br></span>
<span v-bind:style="{'white-space': 'pre-line'}" v-html="tmp[layer].clickables[data].display"></span>
</button>
`
})
Vue.component('master-button', {
props: ['layer', 'data'],
template: `
<button v-if="tmp[layer].clickables && tmp[layer].clickables.masterButtonPress && !(tmp[layer].clickables.showMasterButton !== undefined && tmp[layer].clickables.showMasterButton == false)" v-on:click="tmp[layer].clickables.masterButtonPress()" v-bind:class="{ longUpg: true, can: player[layer].unlocked, locked: !player[layer].unlocked }">{{tmp[layer].clickables.masterButtonText ? tmp[layer].clickables.masterButtonText : "Click me!"}}</button>
`
})
// data = button size, in px
Vue.component('microtabs', {
props: ['layer', 'data'],
computed: {
currentTab() {return player.subtabs[layer][data]}
},
template: `
<div v-if="tmp[layer].microtabs" :style="{'border-style': 'solid'}">
<div class="upgTable">
<tab-buttons :layer="layer" :data="tmp[layer].microtabs[data]" :name="data" v-bind:style="tmp[layer].componentStyles['tab-buttons']"></tab-buttons>
</div>
<column v-bind:style="tmp[layer].microtabs[data][player.subtabs[layer][data]].style" :layer="layer" :data="tmp[layer].microtabs[data][player.subtabs[layer][data]].content"></column>
</div>
`
})
// data = id of the bar
Vue.component('bar', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].bars && tmp[layer].bars[data].unlocked" v-bind:style="{'position': 'relative'}"><div v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].dims, {'display': 'table'}]">
<div class = "overlayTextContainer barBorder" v-bind:style="[tmp[layer].bars[data].borderStyle, tmp[layer].bars[data].dims]">
<span class = "overlayText" v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].textStyle]" v-html="tmp[layer].bars[data].display"></span>
</div>
<div class ="barBG barBorder" v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].baseStyle, tmp[layer].bars[data].borderStyle, tmp[layer].bars[data].dims]">
<div class ="fill" v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].fillStyle, tmp[layer].bars[data].fillDims]"></div>
</div>
</div></div>
`
})
Vue.component('achievements', {
props: ['layer'],
template: `
<div v-if="tmp[layer].achievements" class="upgTable">
<div v-for="row in tmp[layer].achievements.rows" class="upgRow">
<div v-for="col in tmp[layer].achievements.cols"><div v-if="tmp[layer].achievements[row*10+col]!== undefined && tmp[layer].achievements[row*10+col].unlocked" class="upgAlign">
<achievement :layer = "layer" :data = "row*10+col" v-bind:style="tmp[layer].componentStyles.achievement"></achievement>
</div></div>
</div>
<br>
</div>
`
})
// data = id
Vue.component('achievement', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].achievements && tmp[layer].achievements[data]!== undefined && tmp[layer].achievements[data].unlocked" v-bind:class="{ [layer]: true, achievement: true, locked: !hasAchievement(layer, data), bought: hasAchievement(layer, data)}"
v-bind:tooltip="
hasAchievement(layer, data) ? (tmp[layer].achievements[data].doneTooltip ? tmp[layer].achievements[data].doneTooltip : (tmp[layer].achievements[data].tooltip ? tmp[layer].achievements[data].tooltip : 'You did it!'))
: (tmp[layer].achievements[data].goalTooltip ? tmp[layer].achievements[data].goalTooltip : (tmp[layer].achievements[data].tooltip ? tmp[layer].achievements[data].tooltip : 'LOCKED'))
"
v-bind:style="[(!tmp[layer].achievements[data].unlocked) ? {'visibility': 'hidden'} : {}, tmp[layer].achievements[data].style,]">
<span v-if= "tmp[layer].achievements[data].name"><br><h3 v-html="tmp[layer].achievements[data].name"></h3><br></span>
</div>
`
})
// These are for buyables, data is the id of the corresponding buyable
Vue.component('sell-one', {
props: ['layer', 'data'],
template: `
<button v-if="tmp[layer].buyables && tmp[layer].buyables[data].sellOne && !(tmp[layer].buyables[data].canSellOne !== undefined && tmp[layer].buyables[data].canSellOne == false)" v-on:click="tmp[layer].buyables[data].sellOne()" v-bind:class="{ longUpg: true, can: player[layer].unlocked, locked: !player[layer].unlocked }">{{tmp[layer].buyables.sellOneText ? tmp[layer].buyables.sellOneText : "Sell One"}}</button>
`
})
Vue.component('sell-all', {
props: ['layer', 'data'],
template: `
<button v-if="tmp[layer].buyables && tmp[layer].buyables[data].sellAll && !(tmp[layer].buyables[data].canSellAll !== undefined && tmp[layer].buyables[data].canSellAll == false)" v-on:click="tmp[layer].buyables[data].sellAll()" v-bind:class="{ longUpg: true, can: player[layer].unlocked, locked: !player[layer].unlocked }">{{tmp[layer].buyables.sellAllText ? tmp[layer].buyables.sellAllText : "Sell All"}}</button>
`
})
// NOT FOR USE IN STANDARD TAB FORMATTING
Vue.component('tab-buttons', {
props: ['layer', 'data', 'name'],
template: `
<div class="upgRow">
<div v-for="tab in Object.keys(data)">
<button v-if="data[tab].unlocked == undefined || data[tab].unlocked" class="tabButton" v-bind:style="[{'border-color': tmp[layer].color}, tmp[layer].componentStyles['tab-button'], data[tab].buttonStyle]" v-on:click="player.subtabs[layer][name] = tab">{{tab}}</button>
</div>
</div>
`
})
Vue.component('layer-node', {
props: ['layer', 'abb', 'size'],
computed: {
shown: vm => nodeShown(vm.layer)
},
template: `
<button v-if="shown"
v-bind:id="layer"
v-on:click="function() {
showTab(layer)
}"
v-bind:tooltip="
player[layer].unlocked ? (tmp[layer].tooltip ? tmp[layer].tooltip : formatWhole(player[layer].points) + ' ' + tmp[layer].resource)
: (tmp[layer].tooltipLocked ? tmp[layer].tooltipLocked : 'Reach ' + formatWhole(tmp[layer].requires) + ' ' + tmp[layer].baseResource + ' to unlock (You have ' + formatWhole(tmp[layer].baseAmount) + ' ' + tmp[layer].baseResource + ')')
"
v-bind:class="{
treeNode: size != 'small',
smallNode: size == 'small',
[layer]: true,
ghost: tmp[layer].layerShown == 'ghost',
hidden: !tmp[layer].layerShown,
locked: !player[layer].unlocked && !tmp[layer].baseAmount.gte(tmp[layer].requires),
notify: tmp[layer].notify || (!player[layer].unlocked && tmp[layer].baseAmount.gte(tmp[layer].requires)),
can: player[layer].unlocked,
}"
v-bind:style="[layerunlocked(layer) ? {
'background-color': tmp[layer].color,
} : {}, tmp[layer].nodeStyle]">
{{abb}}
</button>
`,
watch: {
shown: function(a, b) {
needCanvasUpdate = true
}
}
})
app = new Vue({
el: "#app",
data: {
player,
tmp,
Decimal,
format,
formatWhole,
formatTime,
focused,
getThemeName,
layerunlocked,
doReset,
buyUpg,
startChallenge,
milestoneShown,
keepGoing,
VERSION,
LAYERS,
hotkeys
},
})
new ResizeObserver(_=>resizeCanvas()).observe(document.getElementById("fakeHead"));
}