1
0
Fork 0
mirror of https://github.com/Acamaeda/The-Modding-Tree.git synced 2024-11-22 00:21:32 +00:00

Merge branch 'dev3' into dev

This commit is contained in:
Acamaeda 2020-10-24 21:03:53 -04:00
commit 11d808ad5d
32 changed files with 4376 additions and 1263 deletions

27
2.0-format-changes.md Normal file
View file

@ -0,0 +1,27 @@
# 2.0 format changes
- Temp format is changed from `temp.something[layer]` to `temp[layer].something`, for consistency
- Challenges are now saved as an object with the amount of completions in each spot. (This will break saves.)
- `effectDisplay` in Challenges and Upgrades no longer takes an argument, and neither does `effect` for Buyables
- Buyable cost can take an argument for amount of buyables, but it needs to function if no argument is supplied (it should do the cost for the next purchase).
- Generation of Points now happens in the main game loop (not in a layer update function), enabled by `canGenPoints` in [game.js](js/game.js).
- Changed `fullLayerReset` to `layerDataReset`, which takes an array of names of values to keep
In addition, many names were changed, mostly expanding abbreviations:
All instances of:
- chall -> challenge
- unl -> unlocked
- upg -> upgrade (besides CSS)
- amt -> amount
- desc -> description
- resCeil -> roundUpCost
- order -> unlockOrder
- incr_order -> increaseUnlockOrder
Challenges:
- desc -> challengeDescription
- reward -> rewardDescription
- effect -> rewardEffect
- effectDisplay -> rewardDisplay
- active -> challengeActive

494
Old Code/gametest.js Normal file
View file

@ -0,0 +1,494 @@
var player;
var needCanvasUpdate = true;
var NaNalert = false;
var gameEnded = false;
let modInfo = {
name: "The Modding Tree",
id: "modbase",
pointsName: "points",
discordName: "",
discordLink: "",
offlineLimit: 1 // In hours
}
let VERSION = {
num: "1.3.5 maybe",
name: "Tabception... ception!",
tmtNum: "1.3.5 maybe",
tmtName: "Tabception... ception!"
}
// Determines if it should show points/sec
function showPointGen(){
return (tmp.pointGen.neq(new Decimal(0)))
}
// Calculate points/sec!
function getPointGen() {
if(!hasUpg("p", 11)) return new Decimal(0)
let gain = new Decimal(1)
if (hasUpg("p", 12)) gain = gain.times(upgEffect("p", 12))
if (hasUpg("p", 13)) gain = gain.times(upgEffect("p", 13))
if (hasUpg("p", 22)) gain = gain.times(upgEffect("p", 22))
if (player.b.unl) gain = gain.times(layers.b.effect())
if (player.g.unl) gain = gain.times(layers.g.effect().powerBoost)
if (hasUpg("e", 11)) gain = gain.times(layers.e.upgrades[11].effect())
if (player.e.unl && tmp.e.buyables) gain = gain.times(tmp.e.buyables[11].effect.second)
if (player.r.upgrades.includes(11)) gain = gain.times(layers.r.upgrades[11].effect())
if (player.d.buyables[12].gt(0)) gain = gain.times(layers.d.buyables[12].effect(player.d.buyables[12]).first)
if (!player.s.active) if (player.pr.buyables[13].gt(0)) gain = gain.times(layers.pr.buyables[13].effect().first)
if (player.d.banking == 1) gain = gain.sqrt()
if (player.d.banking == 2) gain = gain.add(1).log10()
return gain
}
// Function to determine if the player is in a challenge
function inChallenge(layer, id){
let challenge = player[layer].active
if (challenge==toNumber(id)) return true
if (layers[layer].challenges[challenge].countsAs)
return layers[layer].challenges[id].countsAs.includes(id)
}
function getResetGain(layer, useType = null) {
let type = useType
if (!useType) type = layers[layer].type
if (tmp[layer].gainExp.eq(0)) return new Decimal(0)
if (type=="static") {
if ((!layers[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(layers[layer].base).times(tmp[layer].gainExp).pow(Decimal.pow(layers[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(layers[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 = layers[layer].type
if (tmp[layer].gainExp.eq(0)) return new Decimal(1/0)
if (type=="static")
{
if (!layers[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(layers[layer].base, amt.pow(layers[layer].exponent).div(tmp[layer].gainExp)).times(tmp[layer].gainMult)
let cost = extraCost.times(tmp[layer].requires).max(tmp[layer].requires)
if (layers[layer].resCeil) 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(layers[layer].exponent).times(tmp[layer].requires).max(tmp[layer].requires))
if (layers[layer].resCeil) 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 layers[layer].upgrades){
if (!isNaN(id)){
if (canAffordUpg(layer, id) && !hasUpg(layer, id) && tmp[layer].upgrades[id].unl){
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)
else
return layers[layer].canReset()
}
function rowReset(row, layer) {
for (lr in ROW_LAYERS[row]){
if(layers[lr].doReset) {
player[lr].active = null // Exit challenges on any row reset on an equal or higher row
layers[lr].doReset(layer)
}
else
if(layers[layer].row > layers[lr].row) fullLayerReset(lr)
}
}
function fullLayerReset(layer) {
player[layer] = layers[layer].startData();
player[layer].upgrades = []
player[layer].milestones = []
player[layer].challenges = []
if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) {
if (player.subtabs[layer] == undefined) player.subtabs[layer] = {}
if (player.subtabs[layer].mainTabs == undefined) player.subtabs[layer].mainTabs = Object.keys(layers[layer].tabFormat)[0]
}
if (layers[layer].microtabs) {
if (player.subtabs[layer] == undefined) player.subtabs[layer] = {}
for (item in layers[layer].microtabs)
if (player.subtabs[layer][item] == undefined) player.subtabs[layer][item] = Object.keys(layers[layer].microtabs[item])[0]
}
resetBuyables(layer)
}
function resetBuyables(layer){
if (layers[layer].buyables)
player[layer].buyables = getStartBuyables(layer)
player[layer].spentOnBuyables = new Decimal(0)
}
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 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 = layers[layer].row
if (!force) {
if (!tmp[layer].canReset) return
let gain = tmp[layer].resetGain
if (layers[layer].type=="static") {
gain =(layers[layer].canBuyMax() ? gain : 1)
}
if (layers[layer].onPrestige)
layers[layer].onPrestige(gain)
addPoints(layer, gain)
updateMilestones(layer)
if (!player[layer].unl) {
player[layer].unl = true;
needCanvasUpdate = true;
if (layers[layer].incr_order){
lrs = layers[layer].incr_order
for (lr in lrs)
if (!player[lrs[lr]].unl) player[lrs[lr]].order++
}
}
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) : new Decimal(10))
for (let x = row; x >= 0; x--) rowReset(x, layer)
prevOnReset = undefined
updateTemp()
updateTemp()
}
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 \"" + (layers[layer].name ? layers[layer].name : layer) + "\" reset as well!")) return
layers[layer].buyables.respec()
updateBuyableTemp(layer)
}
function canAffordUpg(layer, id) {
if (!layers[layer].upgrades) return false
let upg = layers[layer].upgrades[id]
let cost = tmp[layer].upgrades[id].cost
return canAffordPurchase(layer, upg, cost)
}
function hasUpg(layer, id){
if (!layers[layer].upgrades) return false
return (player[layer].upgrades.includes(toNumber(id)) || player[layer].upgrades.includes(id.toString()))
}
function hasMilestone(layer, id) {
if (!layers[layer].milestones) return false
return (player[layer].milestones.includes(toNumber(id)) || player[layer].milestones.includes(id.toString()))
}
function hasChallenge(layer, id){
if (!layers[layer].challenges) return false
return (player[layer].challenges.includes(toNumber(id)) || player[layer].challenges.includes(id.toString()))
}
function buyablesOwned(layer, id){
return (player[layer].buyables[id])
}
function clickableState(layer, id){
return (player[layer].clickables[id])
}
function upgEffect(layer, id){
if (!layers[layer].upgrades) return {}
return (tmp[layer].upgrades[id].effect)
}
function challengeEffect(layer, id){
if (!layers[layer].challenge) return false
return (tmp[layer].challenges[id].effect)
}
function buyableEffect(layer, id){
if (!layers[layer].buyables) return false
return (tmp[layer].buyables[id].effect)
}
function canAffordPurchase(layer, thing, cost) {
if (thing.currencyInternalName){
let name = thing.currencyInternalName
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 buyUpg(layer, id) {
if (!player[layer].unl) return
if (!layers[layer].upgrades[id].unl()) return
if (player[layer].upgrades.includes(id)) return
let upg = layers[layer].upgrades[id]
let cost = tmp[layer].upgrades[id].cost
if (upg.currencyInternalName){
let name = upg.currencyInternalName
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].unl) return
if (!tmp[layer].buyables[id].unl) return
if (!tmp[layer].buyables[id].canAfford) return
if (!layers[layer].buyables[id].buyMax) return
layers[layer].buyables[id].buyMax()
updateBuyableTemp(layer)
}
function buyBuyable(layer, id) {
if (!player[layer].unl) return
if (!tmp[layer].buyables[id].unl) return
if (!tmp[layer].buyables[id].canAfford) return
layers[layer].buyables[id].buy()
updateBuyableTemp(layer)
}
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[layers[layer]].unl = false
if (player[layers[layer]].order) player[layers[layer]].order = 0
}
player.points = new Decimal(10)
updateTemp();
resizeCanvas();
}
function startChallenge(layer, x) {
let enter = false
if (!player[layer].unl) return
if (player[layer].active == x) {
completeChallenge(layer, x)
delete player[layer].active
} else {
enter = true
}
doReset(layer, true)
if(enter) player[layer].active = x
updateChallengeTemp(layer)
}
function canCompleteChallenge(layer, x)
{
if (x != player[layer].active) return
let challenge = layers[layer].challenges[x]
if (challenge.currencyInternalName){
let name = challenge.currencyInternalName
if (challenge.currencyLayer){
let lr = challenge.currencyLayer
return !(player[lr][name].lt(readData(challenge.goal)))
}
else {
return !(player[name].lt(challenge.cost))
}
}
else {
return !(player[layer].points.lt(challenge.cost))
}
}
function completeChallenge(layer, x) {
var x = player[layer].active
if (!x) return
if (! canCompleteChallenge(layer, x)) return
if (!player[layer].challenges.includes(x)) {
needCanvasUpdate = true
player[layer].challenges.push(x);
if (layers[layer].challenges[x].onComplete) layers[layer].challenges[x].onComplete()
}
delete player[layer].active
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 : "")
const ENDGAME = new Decimal("e280000000");
function gameLoop(diff) {
if (player.points.gte(ENDGAME) || gameEnded) gameEnded = 1
if (isNaN(diff)) diff = 0
if (gameEnded && !player.keepGoing) {
diff = 0
player.tab = "gameEnded"
}
if (player.devSpeed) diff *= player.devSpeed
addTime(diff)
for (layer in layers){
if (layers[layer].update) layers[layer].update(diff);
}
for (layer in layers){
if (layers[layer].automate) layers[layer].automate();
}
for (layer in layers){
if (layers[layer].milestones) updateMilestones(layer);
}
if (player.hasNaN&&!NaNalert) {
clearInterval(interval);
player.autosave = false;
NaNalert = true;
alert("We have detected a corruption in your save. Please visit https://discord.gg/wwQfgPa 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.offlineTime.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();
updateTemp();
gameLoop(diff)
ticking = false
}, 50)

1872
Old Code/test.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,3 @@
<<<<<<< Updated upstream
#The Modding Tree changelog:
=======
# The Modding Tree changelog:
- Fixed the "blank" component breaking if only specifying the height.
@ -101,24 +98,21 @@
### v1.3.5:
>>>>>>> Stashed changes
##v1.3.5
- Completely automated convertToDecimal, now you never have to worry about it again.
- Branches can be defined without a color id. But they can also use hex values for color ids!
- Created a tutorial for getting started with TMT and Github.
- Page title is now automatically taken from mod name.
### v1.3.4 - 10/8/20
##v1.3.4: 10/8/20
- Added "midsection" feature to add things to a tab's layout while still keeping the standard layout.
- Fix for being able to buy more buyables than you should.
##v1.3.3: - 10/7/20
### v1.3.3 - 10/7/20
- Fix for the "order of operations" issue in temp.
##v1.3.1: - 10/7/20
### v1.3.1 - 10/7/20
- Added custom CSS and tooltips for Layer Nodes.
- Added custom CSS for upgrades, buyables, milestones, and challenges, both individually and layer-wide.
@ -127,7 +121,7 @@
- Fixed importing saves, and issue with upgrades not appearing, and probably more.
- Optional "name" layer feature, used in confirmation messages.
##v1.3: Tabception... ception! - 10/7/20
## v1.3: Tabception... ception! - 10/7/20
- Added subtabs! And also a Micro-tab component to let you make smaller subtab-esque areas anywhere.
- Added a "custom" prestige formula type, and a number of features to support it.
@ -141,20 +135,20 @@
##v1.2.4 - 10/4/20
### v1.2.4 - 10/4/20
- Layers are now highlighted if you can buy an upgrade, and a new feature, shouldNotify,
lets you make it highlight other ways.
- Fixed bugs with hasUpg, hasChall, hasMilestone, and inChallenge.
- Changed the sample code to use the above functions for convenience.
##v1.2.3 - 10/3/20
### v1.2.3 - 10/3/20
- Added a row component, which displays a list of objects in a row.
- Added a column component, which displays a list of objects in a column (useful within a row).
- Changed blanks to have a customizable width and height.
#v1.2: This Changes Everything! - 10/3/20
## v1.2: This Changes Everything! - 10/3/20
- Many layer features can now be static values or functions. (This made some formats change,
which will break old things)
@ -167,16 +161,17 @@ which will break old things)
##v1.1.1
### v1.1.1:
- You can define hotkeys directly from layer config.
#v1.1: Enhanced Edition
## v1.1: Enhanced Edition
- Added "Buyables", which can function like Space Buildings or Enhancers.
- Custom CSS can now be used on any component! Make the third argument an object with CSS
parameters.
- Lots of minor good things.
#v1.0:
- First release.
## v1.0:
- First release.

View file

@ -191,4 +191,8 @@
</div>
</div>
</div>
<<<<<<< HEAD
</body>
=======
</body>
>>>>>>> dev3

View file

@ -2,10 +2,12 @@
The main way to add content is through creating layers. You can either add a layer directly in the layers object in layersSupportjs,
or declare it in another file and then do "`addLayer(layername, layerdata)`"
(good for breaking things up into smaller files). The existing layers are just examples and can be freely deleted.
You can use those as references and a base for your own layers.
You can also use them as references and a base for your own layers.
**You will also need to add layer nodes to the tree in the HTML, look for where it says "Modify the tree in the table below!"** While you're there, you can also edit the modInfo at the top to change the name for your mod and some other settings. A unique modId will prevent your mod's saves from conflicting with other mods.
The first thing you need to do is to edit the modInfo at the top of game.js to set your modID (a string). A
unique modId will prevent your mod's saves from conflicting with other mods.
Most of the time, you won't need to dive deep into the code to create things, but you still can if you really want to.
@ -15,18 +17,28 @@ The Modding Tree uses break_eternity.js to store large values. This means that m
and must be treated differently. For example, you have to use `new Decimal(x)` to create a Decimal value instead of a
plain number, and perform operations on them by calling functions. e.g, instead of `x = x + y`, use `x = x.add(y)`.
Almost all values can be either a constant value, or a dynamic value. Dynamic values are defined by putting a function
that returns what the value should be at any given time.
All display text can be basic HTML instead (But you can't use most Vue features there).
## Table of Contents:
- [Getting Started](getting-started.md): Getting your own copy of the code set up with Github Desktop.
- [Main mod info](main-mod-info.md): How to set up general things for your mod in mod.js.
- [Basic layer breakdown](basic-layer-breakdown.md): Breaking down the components of a layer with minimal features.
- [Layer features](layer-features.md): Explanations of all of the different properties that you can give a layer.
- [Upgrades](upgrades.md): How to create upgrades for a layer.
- [Milestones](milestones.md): How to create milestones for a layer.
- [Challenges](challenges.md): How to create challenges for a layer.
- [Buyables](buyables.md): Create rebuyable upgrades for your layer (with the option to make them respec-able).
Can be used to make Enhancers or Space Buildings, but they're flexible enough to do anything.
Can be used to make Enhancers or Space Buildings.
- [Clickables](clickables.md): A more generalized variant of buyables, for any kind of thing that is sometimes clickable.
Between these and Buyables, you can do just about anything.
- [Bars](bars.md): Display some information as a progress bar, gague, or similar. They are highly customizable,
and can be horizontal and vertical as well.
- [Custom Tab Layouts](custom-tab-layouts.md): An optional way to give your tabs a different layout.
You can even create entirely new components to use.
- [Subtabs and Microtabs](subtabs-and-microtabs.md): Create subtabs for your tabs, as well as "microtab" components that you can put inside the tabs.
- [Achievements](milestones.md): How to create achievements for a layer (or for the whole game).
- [Updating TMT](updating-tmt.md): Using Github Desktop to update your mod's version of TMT.

61
docs/achievements.md Normal file
View file

@ -0,0 +1,61 @@
# Achievements
Achievements are awarded to the player when they meet a certain goal, and give some benefit.
Currently, they are pretty basic, but additional features will be added later to help.
You can make global achievements by putting them in a side layer (make its row "side" instead of a number)
Useful functions for dealing with achievements and implementing their effects:
- hasAchievement(layer, id): determine if the player has the Achievement
- achievementEffect(layer, id): Returns the current effects of the achievement, if any
Achievements should be formatted like this:
```js
achievements: {
rows: # of rows
cols: # of columns
11: {
name: "Blah",
more features
}
etc
}
```
Each achievement should have an id where the first digit is the row and the second digit is the column.
Individual achievement can have these features:
- name: **optional**, displayed at the top of the achievement. The only visible text.
It can also be a function that returns updating text. Can use basic HTML.
- done(): A function returning a boolean to determine if the achievement should be awarded.
- tooltip: Default tooltip for the achievement, appears when it is hovered over. Should convey the goal and any reward for
completing the achievement. It can also be a function that returns updating text. Can use basic HTML.
- effect(): **optional**, A function that calculates and returns the current values of any bonuses from the achievement.
Can return a value or an object containing multiple values.
- unlocked(): **optional**, A function returning a bool to determine if the achievement is visible or not. Default is unlocked.
- onComplete() - **optional**, this function will be called when the achievement is completed.
- style: **Optional**, Applies CSS to this achievement, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the "key" which the achievement was stored under, for convenient access.
The achievement in the example's id is 11.
- goalTooltip: **optional, depracated** Appears when the achievement is hovered over and locked, overrides the basic tooltip.
This is to display the goal (or a hint). It can also be a function that returns updating text. Can use basic HTML.
- doneTooltip: **optional, depracated** Appears when the achievement is hovered over and completed, overrides the basic tooltip.
This can display what the player achieved (the goal), and the rewards, if any.
It can also be a function that returns updating text. Can use basic HTML.

39
docs/bars.md Normal file
View file

@ -0,0 +1,39 @@
# Buyables
Bars let you display information in a more direct way. It can be a progress bar, health bar, capacity gague, or anything else.
Bars are defined like other Big Features:
```js
bars: {
bigBar: {
display() {return "Blah"},
etc
}
etc
}
```
Features:
- direction: UP, DOWN, LEFT, or RIGHT (not Strings). Determines the direction that the bar is filled as it progresses.
RIGHT means from left to right.
- width, height: The size in pixels of the bar, but as Numbers (no "px" at the end)
- progress(): A function that returns the portion of the bar that is filled, from "empty" at 0 to "full" at 1.
(Nothing bad happens if the value goes out of these bounds, and it can be a number or Decimal).
- display(): **optional**, A function that returns text to be displayed on top of the bar, can use HTML.
- unlocked(): **optional**, A function returning a bool to determine if the buyable is visible or not. Default is unlocked.
- baseStyle, fillStyle, borderStyle, textStyle: **Optional**, Apply CSS to the unfilled portion, filled portion, border, and
display text on the bar, in the form of an object where the keys are CSS attributes, and the values are the
values for those attributes (both as strings).
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the "key" which the bar was stored under, for convenient access.
The bar in the example's id is "bigBar".

View file

@ -1,25 +1,23 @@
# Basic layer breakdown
This is a very minimal layer with minimal features. Most things will require additional features.
If you're curious about "() =>", it's a weird notation that lets you use either a value or function in the same slot,
and treats it like a function. You can use it to make actual functions, but it breaks things then.
```js
p: {
startData() { return { // startData is a function that returns default data for a layer.
unl: false, // You can add more variables here to add them to your layer.
unlocked: false, // You can add more variables here to add them to your layer.
points: new Decimal(0), // "points" is the internal name for the main resource of the layer.
// If you add non-standard Decimal variables, look at convertToDecimal
}},
color:() => "#FE0102", // The color for this layer, which affects many elements
color: "#FE0102", // The color for this layer, which affects many elements
resource: "prestige points", // The name of this layer's main prestige resource
row: 0, // The row this layer is on (0 is the first row)
baseResource: "points", // The name of the resource your prestige gain is based on
baseAmount() {return player.points}, // A function to return the current value of that resource
requires:() => new Decimal(200)}, // The amount of the base needed to gain 1 of the prestige currency.
requires: new Decimal(200), // The amount of the base needed to gain 1 of the prestige currency.
// Also the amount required to unlock the layer.
type: "normal", // Determines the formula used for calculating prestige currency.

View file

@ -3,8 +3,8 @@
Buyables are usually things that can be bought multiple times with scaling costs. If you set a respec function,
the player can reset the purchases to get their currency back.
However, if you're creative, you can use them for basically anything. "canAfford()" is effectively "canClick()" and "buy()" is effectively "onClick()".
The amount of a buyable owned is a Decimal.
You can get or set the amount of a buyable with getBuyableAmt(layer, id) and setBuyableAmt(layer, id, amt).
You can use buyableEffect(layer, id) to get the current effects of a buyable.
Buyables should be formatted like this:
@ -13,11 +13,13 @@ Buyables should be formatted like this:
buyables: {
rows: # of rows
cols: # of columns
respec() {}, **optional**, implement it to reset things and give back your currency.
Having this function makes a respec button appear
respecText: **optional**, text that appears on the respec button
respec() {}, //**optional**, implement it to reset things and give back your currency.
// Having this function makes a respec button appear
respecText:// **optional**, text that appears on the respec button
showRespecButton(){} //**optional**, a function determining whether or not to show the button. Defaults to true if absent.
sellOneText, sellAllText:// **optional**, text that appears on the "sell one" and "sell all" buttons respectively (if you are using them)
11: {
desc:() => "Blah",
display() {return "Blah"},
etc
}
etc
@ -29,24 +31,39 @@ Features:
- title: **optional**, displayed at the top in a larger font
It can also be a function that returns updating text.
- cost(x): cost for buying xth buyable, can be an object if there are multiple currencies
- cost(): cost for buying the next buyable. Can have an optional argument "x" to calculate the cost of the x+1th object,
but needs to use "current amount" as a default value for x. (x is a Decimal).
Can return an object if there are multiple currencies.
- effect(x): **optional**, A function that calculates and returns the current values of bonuses
for having x of this buyable. Can return a value or an object containing multiple values.
- effect(): **optional**, A function that calculates and returns the current values of bonuses
of this buyable. Can return a value or an object containing multiple values.
- display(): A function returning everything that should be displayed on the rebuyable after the title, likely
- display(): A function returning everything that should be displayed on the buyable after the title, likely
including the description, amount bought, cost, and current effect. Can use basic HTML.
- unl(): A function returning a bool to determine if the buyable is visible or not.
- unlocked(): **optional**, A function returning a bool to determine if the buyable is visible or not. Default is unlocked.
- canAfford(): A function returning a bool to determine if you can buy one of the buyables.
- buy(): A function that implements buying one of the buyable.
- buy(): A function that implements buying one of the buyable, including spending the currency.
- buyMax(): **optional**, A function that implements buying as many of the buyable as possible.
- style(): **Optional**, A function returning a CSS object, which affects this buyable.
- style: **Optional**, Applies CSS to this buyable, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the id for this buyable.
- id: **Assigned automagically**. It's the "key" which the buyable was stored under, for convenient access.
The buyable in the example's id is 11.
Sell One/Sell All:
Including a sellOne or sellAll function will cause an additional button to appear beneath the buyable.
They are functionally identical, but "sell one" appears above "sell all". You can also use them for other things.
sellOne/sellAll(): **optional**, Called when the button is pressed. The standard use would be to decrease/reset the amount of the buyable,
And possibly return some currency to the player.
canSellOne/canSellAll(): **optional**, booleans determining whether or not to show the buttons. If "canSellOne/All" is absent but
"sellOne/All" is present, the appropriate button will always show.

View file

@ -2,19 +2,20 @@
Useful functions for dealing with Challenges and implementing their effects:
- inChall(layer, id): determine if the player is in a given challenge (or another challenge on the same layer that counts as this one)
- hasChall(layer, id): determine if the player has completed the challenge
- inChallenge(layer, id): determine if the player is in a given challenge (or another challenge on the same layer that counts as this one)
- hasChallenge(layer, id): determine if the player has completed the challenge
- challengeCompletions(layer, id): determine how many times the player completed the challenge
- challEffect(layer, id): Returns the current effects of the challenge, if any
Challenges are stored in the following format:
```js
challs: {
challenges: {
rows: # of rows
cols: # of columns
11: {
name:() => "Ouch",
name: "Ouch",
etc
}
etc
@ -26,22 +27,22 @@ Individual Challenges can have these features:
- name: Name of the challenge, can be a string or a function. Can use basic HTML.
- desc: A description of what makes the challenge a challenge. *You will need to implement these elsewhere*
- challengeDescription: A description of what makes the challenge a challenge. *You will need to implement these elsewhere*
It can also be a function that returns updating text. Can use basic HTML.
- reward: A description of the reward's effect. *You will also have to implement the effect where it is applied.*
- rewardDescription: A description of the reward's effect. *You will also have to implement the effect where it is applied.*
It can also be a function that returns updating text. Can use basic HTML.
- effect(): **optional**, A function that calculates and returns the current values of any bonuses from the reward.
- rewardEffect(): **optional**, A function that calculates and returns the current values of any bonuses from the reward.
Can return a value or an object containing multiple values. Can use basic HTML.
- effectDisplay(effects): **optional**, A function that returns a display of the current effects of the reward with
- rewardDisplay(): **optional**, A function that returns a display of the current effects of the reward with
formatting. Default behavior is to just display the a number appropriately formatted.
- goal: A Decimal for the cost of the upgrade. By default, the goal is in basic Points.
- goal: A Decimal for the amount of currency required to beat the challenge. By default, the goal is in basic Points.
The goal can also be a function if its value changes.
- unl(): A function returning a bool to determine if the challenge is visible or not.
- unlocked(): **optional**, A function returning a bool to determine if the challenge is visible or not. Default is unlocked.
- onComplete() - **optional**, this function will be called when the challenge is completed when previously incomplete.
@ -52,10 +53,16 @@ By default, challenges use basic Points for the goal. You can change that using
- currencyDisplayName: **optional**, the name to display for the currency for the goal
- currencyInternalName: **optional**, the internal name for that currency
- currencyLayer: **optional**, the internal name of the layer that currency is stored in.
If it's part of a layer, omit.
If it's not in a layer, omit. If it's not stored directly in a layer, instead use the next feature.
- currencyLocation: **optional**, if your currency is stored in something inside a layer (e.g. a buyable's amount), you can access it this way.
This is a function returning the object in "player" that contains the value (like player[this.layer].buyables)
- style(): **Optional**, A function returning a CSS object, which affects this challenge.
- completionLimit: **optional**, the amount of times you can complete this challenge. Default is 1 completion.
- style: **Optional**, Applies CSS to this challenge, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the id for this challenge.
- id: **Assigned automagically**. It's the "key" which the challenge was stored under, for convenient access.
The challenge in the example's id is 11.

54
docs/clickables.md Normal file
View file

@ -0,0 +1,54 @@
# Clickables
Clickables are any kind of thing that you can click for an effect. They're a more generalized version of Buyables.
There are several differences between the two. One is that a buyable's saved data is its amount as a Decimal, while
Clickables store a "state" which can be a number or string, but not Decimal, array, or object).
Buyables have a number of extra features which you can see on their page.
Clickables also have a smaller default size.
You can get and set a clickable's state with getClickableState(layer, id) and setClickableState(layer, id, state).
You can use clickableEffect(layer, id) to get the current effects of a clickable.
Clickables should be formatted like this:
```js
clickables: {
rows: # of rows
cols: # of columns
masterButtonPress() // **optional** If this is present, an additional button will appear above the clickables.
// pressing it will call the function.
masterButtonText: "Press me!" // **optional** text to display on the Master Button
showMasterButton(){} //**optional**, a function determining whether or not to show the button. Defaults to true if absent.
11: {
display() {return "Blah"},
etc
}
etc
}
```
Features:
- title: **optional**, displayed at the top in a larger font
It can also be a function that returns updating text.
- effect(): **optional**, A function that calculates and returns the current values of bonuses
of this clickable. Can return a value or an object containing multiple values.
- display(): A function returning everything that should be displayed on the clickable after the title, likely
changing based on its state. Can use basic HTML.
- unlocked(): **optional**, A function returning a bool to determine if the clickable is visible or not. Default is unlocked.
- canClick(): A function returning a bool to determine if you can click the clickable.
- onClick(): A function that implements clicking one of the clickable.
- style: **Optional**, Applies CSS to this clickable, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar.
- id: **Assigned automagically**. It's the "key" which the clickable was stored under, for convenient access.
The clickable in the example's id is 11.

View file

@ -17,38 +17,50 @@ Custom tab layouts can be used to do basically anything in a tab window, especia
"milestones", "blank", "blank", "upgrades"]
```
It is a list of components, which can be either just a name, or an array with arguments. If it's an array, the first item is the name of the component, the second is the data passed into it, and the third (optional) is a CSS object,
which applies its style to the component.
It is a list of components, which can be either just a name, or an array with arguments. If it's an array,
the first item is the name of the component, the second is the data passed into it, and the third (optional)
applies a CSS style to it with a "CSS object", where the keys are CSS attributes.
These are the existing components, but you can create more in v.js:
- display-text: Displays some text. The argument is the text to display. It can also be a function that returns updating text.
- display-text: Displays some text (can use basic HTML). The argument is the text to display. It can also be a function that returns updating text.
- raw-html: Displays some HTML. The argument is the HTML as a string, or a function that returns updating HTML.
It doesn't work with many vue things.
- raw-html: Displays some basic HTML, can also be a function.
- blank: Adds empty space. The default dimensions are 8px x 17px. The argument changes the dimensions.
If it's a single value (e.g. "20px"), that determines the height.
If you have a pair of arguments, the first is width and the second is height.
- row: Display a list of components horizontally. The argument is an array of components in the tab layout format.
- column: Display a list of components vertically. The argument is an array of components in the tab layout format.
This is useful to display columns within a row.
- main-display: The text that displays the main currency for the layer and its effects.
- resource-display: The text that displays the currency that this layer is based on, as well as the best and/or total
values for this layer's prestige currency (if they are put in startData for this layer)
- prestige-button: The argument is a string that the prestige button should say before the amount of
currency you will gain. It can also be a function that returns updating text.
- upgrades, milestones, challs: Display the upgrades, milestones, and challenges for a layer, as appropriate.
- upgrades, milestones, challs, achievements: Display the upgrades, milestones, and challenges for a layer, as appropriate.
- buyables: Display all of the buyables for this layer, as appropriate. The argument optional, and is the size of the
boxes in pixels.
- buyables, clickables: Display all of the buyables/clickables for this layer, as appropriate. The argument optional,
and is the size of the boxes in pixels.
- microtabs: Display a set of subtabs for an area. The argument is the name of the set of microtabs in the "microtabs" feature.
- upgrade, milestone, chall, buyable: An individual upgrade, challenge, etc. The argument is the id.
This can be used if you want to have upgrades split up across multiple subtabs, for example.
- bar: Display a bar. The argument is the id of the bar to display.
- toggle: A toggle button that toggles a bool value. The data is a pair that identifies what bool to toggle, [layer, id]
- row: Display a list of components horizontally. The argument is an array of components in the tab layout format.
- column: Display a list of components vertically. The argument is an array of components in the tab layout format.
This is useful to display columns within a row.
The rest of the components are sub-components. They can be used just like other components, but are typically part of another component.
- upgrade, milestone, chall, buyable, clickable, achievement: An individual upgrade, challenge, etc. The argument is the id.
This can be used if you want to have upgrades split up across multiple subtabs, for example.
- respec-button, master-button: The respec and master buttons for buyables and clickables, respectively.
- sell-one, sell-all: The "sell one" and "sell all" for buyables, respectively. The argument is the id of the buyable.

View file

@ -17,7 +17,7 @@ The benefits of using Github:
- It lets you collaborate with other people, if you want to.
# Getting set up with Github and The Modding Tree:
## Getting set up with Github and The Modding Tree:
1. Install [Github Desktop](https://desktop.github.com/) and [Visual Studio Code](https://code.visualstudio.com/).
@ -36,17 +36,18 @@ The benefits of using Github:
6. Select that you're using it for your own purposes, and click continue. It will download the files and handle everything.
# Using your repository
## Using your repository
1. Click on "show in finder" to the right, and then open index.html. This will let you view and
test your project!
2. To edit your project, click "open in VSCode" in Github Desktop.
3. Open index.html, and look at the top part where it says "modInfo". On the lines below that, change
the mod's name to whatever you want, and change the id as well. (The id is important, it determines where the game looks for your saves. It should be different from other mods, and shouldn't be changed once set.)
3. Open mod.js in VSCode, and look at the top part where it says "modInfo". On the lines below that, change
the mod's name to whatever you want, and change the id as well. (It can be any string value, and it's used to determine
where the savefile is. Make it something that's probably unique, and don't change it again later.)
4. Reload index.html. The title on the tab, as well as on the info page, will now be the new ones!
4. Save game.js, and then reload index.html. The title on the tab, as well as on the info page, will now be the new ones!
5. Go back to Github Desktop. It's time to save your changes into the git system by making a "commit".

View file

@ -3,12 +3,14 @@
This is a more comprehensive list of established features to add to layers.
You can add more freely, if you want to have other functions or values associated with your layer. These have special functionality, though.
You can make almost any value dynamic by using a function in its place, including all display strings and styling/color features.
Key:
- No label: This is required and the game will crash if it isn't included.
- **sometimes required**: This is may be required, depending on other things in the layer.
- **optional**: You can leave this out if you don't intend to use that feature for the layer.
# Layer Definition features
## Layer Definition features
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
to access the save value. It makes copying code to new layers easier. It is also assigned to all upgrades and buyables and such.
@ -19,16 +21,20 @@ Key:
Any nonstandard Decimal variables need to be added to convertToDecimal as well.
Standard values:
Required:
unl: a bool determining if this layer is unlocked or not
unlocked: a bool determining if this layer is unlocked or not
points: a Decimal, the main currency for the layer
Optional:
total: A Decimal, tracks total amount of main prestige currency
best: A Decimal, tracks highest amount of main prestige currency
order: used to keep track of relevant layers unlocked before this one.
unlockOrder: used to keep track of relevant layers unlocked before this one.
- color: A color associated with this layer, used in many places. (A string in hex format with a #)
- row: The row of the layer, starting at 0.
- row: The row of the layer, starting at 0. This affects where the node appears on the tree, and which resets affect the layer.
Using "side" instead of a number will cause the layer to appear off to the side as a smaller node (useful for achievements
and statistics). Side layers are not affected by resets unless you add a doReset to them.
- resource: Name of the main currency you gain by resetting on this layer.
@ -41,17 +47,19 @@ Key:
If the text stays constant, it can just be a string.
- layerShown(): A function returning a bool which determines if this layer's node should be visible on the tree.
It can also return "ghost", which will hide the layer, but its node will still take up space in the tree.
- hotkeys: **optional**, An array containing information on any hotkeys associated with this layer:
```js
hotkeys: [
{key: "p", // What the hotkey button is. Use uppercase if it's combined with shift, or "ctrl+x" if ctrl is.
desc: "p: reset your points for prestige points", // The description of the hotkey used in the How To Play
onPress(){if (player.p.unl) doReset("p")}}, // This function is called when the hotkey is pressed.
onPress(){if (player.p.unlocked) doReset("p")}}, // This function is called when the hotkey is pressed.
],
```
- style(): **optional**, a function returning a CSS object containing any CSS that should affect this layer's whole tab.
- style: **optional**, a "CSS object" where the keys are CSS attributes ,containing any CSS that should affect this
layer's entire tab.
- tabFormat: **optional**, use this if you want to add extra things to your tab or change the layout. [See here for more info.](custom-tab-layouts.md)
@ -59,7 +67,7 @@ Key:
standard tab layout. (cannot do subtabs)
# Big features (all optional)
## Big features (all optional)
- upgrades: A grid of one-time purchases which can have unique upgrade conditions, currency costs, and bonuses.
[Explanations are in a separate file.](upgrades.md)
@ -71,22 +79,23 @@ Key:
they recieve a bonus.
[Explanations are in a separate file.](challenges.md)
- buyables: Effectively upgrades that can be bought multiple times, and are optionally respeccable.
- buyables: Effectively upgrades that can be bought multiple times, and are optionally respeccable. Many uses.
[Explanations are in a separate file.](buyables.md)
- clickables: Extremely versatile and generalized buttons which can only be clicked sometimes.
[Explanations are in a separate file.](clickables.md)
- microtabs: An area that functions like a set of subtabs, with buttons at the top changing the content within. (Advanced)
[Explanations are in a separate file.](subtabs-and-microtabs.md)
- bars: Display some information as a progress bar, gague, or similar. They are highly customizable, and can be vertical as well.
[Explanations are in a separate file.](bars.md)
# Prestige formula features
- achievements: Kind of like milestones, but with a different display style and some other differences. Extra features are on the way at a later date!
[Explanations are in a separate file.](achievements.md)
- baseResource: The name of the resource that determines how much of the main currency you gain on reset.
- baseAmount(): A function that gets the current value of the base resource.
- requires: A Decimal, the amount of the base needed to gain 1 of the prestige currency.
Also the amount required to unlock the layer.
You can instead make this a function, to make it harder if another layer was unlocked first (based on "order").
## Prestige formula features
- type: Determines which prestige formula you use.
"normal": The amount of currency you gain is independent of its current amount (like Prestige).
@ -94,41 +103,67 @@ Key:
"static": The cost is dependent on your total after reset.
formula before bonuses is based on `base^(x^exponent)`
"custom": You can define everything, from the calculations to the text on the button, yourself. (See more at the bottom)
"none": This layer does not prestige, and therefore does not need any of the other features in this section.
- baseResource: The name of the resource that determines how much of the main currency you gain on reset.
- baseAmount(): A function that gets the current value of the base resource.
- requires: A Decimal, the amount of the base needed to gain 1 of the prestige currency.
Also the amount required to unlock the layer.
You can instead make this a function, to make it harder if another layer was unlocked first (based on unlockOrder).
- exponent: Used as described above.
- base: **sometimes required**, required for "static" layers, used as described above.
- resCeil: **optional**, a bool, which is true if the resource cost needs to be rounded up.
- roundUpCost: **optional**, a bool, which is true if the resource cost needs to be rounded up.
(use if the base resource is a "static" currency.)
- canBuyMax(): **sometimes required**, required for static layers, function used to determine if buying max is permitted.
- gainMult(), gainExp(): Functions that calculate the multiplier and exponent on resource gain from upgrades
- gainMult(), gainExp(): **optional**, Functions that calculate the multiplier and exponent on resource gain from upgrades
and boosts and such. Plug in any bonuses here.
- onPrestige(gain): **optional**, A function that triggers when this layer prestiges, just before you gain the currency.
Can be used to have secondary resource gain on prestige, or to recalculate things or whatnot.
- resetDesc: **optional**, use this to replace "Reset for " on the Prestige button with something else.
# Tree/node features
- prestigeButtonText(): **Sometimes required**, Use this to make the entirety of the text a Prestige button contains. Only required for custom layers,
but usable by all types.
## Tree/node features
- symbol: **optional**, the text that appears on this layer's node. Default is the layer id with the first letter capitalized
- position: **optional**, Determines the horizontal position of the layer in its row. By default, it uses the layer id,
and layers are sorted in alphabetical order.
- branches: **optional**, an array of layer ids. On a tree, a line will appear from this layer to all of the layers
in the list. Alternatively, an entry in the array can be a pair consisting of the layer id and a color
in the list. Alternatively, an entry in the array can be a 2-element array consisting of the layer id and a color
value. The color value can either be a string with a hex color code, or a number from 1-3 (theme-affected colors)
- nodeStyle(): **optional**, a function returning a CSS object, styles this layer's node on the tree
- nodeStyle: **optional**, a CSS object, where the keys are CSS attributes, which styles this layer's node on the tree
- tooltip() / tooltipLocked(): **optional** Functions that return text, which is the tooltip for the node when the layer
is unlocked or locked, respectively. By default the tooltips behave the same as in the original Prestige Tree.
# Other features
## Other features
- doReset(resettingLayer): **optional**, is triggered when a layer on a row greater than or equal to this one does a reset.
If you use it, you can choose what to keep via milestones and such.
Without it, the default is to reset everything on the row, but only
if it was triggered by a layer in a higher row.
The default behavior is to reset everything on the row, but only if it was triggered by a layer in a higher row.
(doReset is always called for side layers, but for these the default behavior is to reset nothing.)
If you want to keep things, determine what to keep based on the resettingLayer, milestones, and such, then call
resetLayerData(layer, keep), where layer is this layer, and keep is an array of the names of things to keep.
It can include things like "points", "best", "total" (for this layer's prestige currency), "upgrades",
any unique variables like "generatorPower", etc.
If you want to only keep specific upgrades or something like that, save them in a separate variable, then
call layerDataReset, and then set player[layer].upgrades to the saved upgrades.
- update(diff): **optional**, this function is called every game tick. Use it for any passive resource production or
time-based things. diff is the time since the last tick.
@ -138,16 +173,12 @@ Key:
- automate(): **optional**, this function is called every game tick, after production. Use it to activate any
autobuyers or auto-resets or similar on this layer, if appropriate.
- updateTemp(): **optional**, this function is called every game tick. use it to update anything in the "tmp" object.
You don't really need it. tmp is used as a way to store calculated values so it doesn't repeat
calculations.
- resetsNothing: **optional**, returns true if this layer shouldn't trigger any resets when you prestige.
- resetsNothing(): **optional**, returns true if this layer shouldn't trigger any resets when you prestige.
- incr_order: **optional**, an array of layer ids. When this layer is unlocked for the first time, the "order" value
- increaseUnlockOrder: **optional**, an array of layer ids. When this layer is unlocked for the first time, the unlockOrder value
for any not-yet-unlocked layers in this list increases. This can be used to make them harder to unlock.
- should_notify: **optional**, a function to return true if this layer should be highlighted in the tree.
- shouldNotify: **optional**, a function to return true if this layer should be highlighted in the tree.
The layer will automatically be highlighted if you can buy an upgrade whether you have this or not.
- componentStyles: **optional**, An object that contains a set of functions returning CSS objects.
@ -155,27 +186,23 @@ Key:
```js
componentStyles: {
"chall"() {return {'height': '200px'}},
"challenge"() {return {'height': '200px'}},
"prestige-button"() {return {'color': '#AA66AA'}},
},
```
# Custom Prestige type only
(No effect otherwise)
## Custom Prestige type
- prestigeButtonText(): **Only for custom prestige type**, Function that returns the entirety of the text that should
be displayed on the prestige button.You can use HTML as well!
- getResetGain(): **Only for custom prestige type**, Returns how many points you should get if you reset now. You can call
- getResetGain(): **For custom prestige type**, Returns how many points you should get if you reset now. You can call
getResetGain(this.layer, useType = "static") or similar to calculate what your gain would be under another
prestige type (provided you have all of the required features in the layer.)
- getNextAt(canMax=false): **Only for custom prestige type**, Returns how many of the base currency you need to get to
- getNextAt(canMax=false): **For custom prestige type**, Returns how many of the base currency you need to get to
the next point. canMax is an optional variable used with Static-ish layers to differentiate between if
it's looking for the first point you can reset at, or the requirement for any gain at all.
(Supporting both is good). You can also call getNextAt(this.layer, canMax=false, useType = "static")
or similar to calculate what your next at would be under another prestige type (provided you have
all of the required features in the layer.)
- canReset(): **Only for custom prestige type**, return true only if you have the resources required to do a prestige here.
- canReset(): **For custom prestige type**, return true only if you have the resources required to do a prestige here.

62
docs/main-mod-info.md Normal file
View file

@ -0,0 +1,62 @@
# mod.js
All of the code and data that you're likely to edit is here in mod.js! Everything in mod.js
will not be altered by updates, besides the addition of new things.
Here's a breakdown of what's in it:
- modInfo is where most of the basic configuration for the mod is. It contains:
- name: The name of your mod. (a string)
- id: The id for your mod, a unique string that is used to determine savefile location. Setting it is important!
- author: The name of the author, displayed in the info tab.
- pointsName: This changes what is displayed instead of "points" for the main currency. (It does not affect it in the code.)
- discordName, discordLink: If you have a Discord server or other discussion place, you can add a link to it.
"discordName" is the text on the link, and "discordLink" is the url of an invite.
If you're using a Discord invite, please make sure it's set to never expire.
- changelogLink: You can use this to set a link to a page where your changelog for the game is displayed.
- offlineLimit: The maximum amount of offline time that the player can accumulate, in hours. Any extra time is lost. (a number)
This is useful because most of these mods are fast-paced enough that too much offline time ruins the balance,
such as the time in between updates.
That is why I suggest developers disable offline time on their own savefile.
- initialStartPoints: A Decimal for the amount of points a new player should start with.
- VERSION is used to describe the current version of your mod. It contains:
num: The mod's version number, displayed at the top right of the tree tab.
name: The version's name, displayed alongside the number in the info tab.
- doNotCallTheseFunctionsEveryTick is very important. TMT calls every function anywhere in "layers" every tick to store the result,
unless specifically told not to. Functions that have are used to do an action need to be identified. "Official" functions
(those in the documentation) are all fine, but if you make any new ones, add their names to this array.
```js
// (The ones here are examples, all official functions are already taken care of)
var doNotCallTheseFunctionsEveryTick = ["doReset", "buy", "onPurchase", "blowUpEverything"]
```
- getStartPoints(): A function to determine the amount of points the player starts with after a reset. (returns a Decimal value)
- canGenPoints(): A function returning a boolean for if points should be generated.
Use this if you want an upgrade to unlock generating points.
- getPointGen(): A function that calculates your points per second. Anything that affects your point gain should go into the calculation here.
- addedPlayerData(): A function that returns any non-layer-related data that you want to be added to the save data and "player" object.
```js
function addedPlayerData() { return {
weather: "Yes",
happiness: new Decimal(72),
}}
```
- displayThings: An array of functions used to display extra things at the top of the tree tab. Each function returns a string, which
is a line to display (with basic HTML support). If a function returns nothing, nothing is displayed (and it doesn't take up a line).
- isEndgame(): A function to determine if the player has reached the end of the game, at which point the "you win!" screen appears.
Less important things beyond this point!
- maxTickLength(): Returns the maximum tick length, in milliseconds. Only really useful if you have something that reduces over time,
which long ticks mess up (usually a challenge).

View file

@ -1,11 +1,11 @@
#Milestones
# Milestones
Milestones should be formatted like this:
Milestones are awarded to the player when they meet a certain goal, and give some benefit. Milestones should be formatted like this:
```js
milestones: {
0: {
requirementDesc:() => "123 waffles",
requirementDesc: "123 waffles",
}
etc
}
@ -21,7 +21,7 @@ Milestone features:
- effectDesc: A string describing the reward for having the milestone. *You will have to implement the reward elsewhere.*
It can also be a function that returns updating text. Can use basic HTML.
- done(): A function returning a boolean to determine if the milestone has been fulfilled.
- done(): A function returning a boolean to determine if the milestone should be awarded.
- toggles: *optional*, Creates toggle buttons that appear on the milestone when it is unlocked.
The toggles can toggle a given boolean value in a layer.
@ -31,10 +31,13 @@ Milestone features:
**Tip:** Toggles are not de-set if the milestone becomes locked! In this case, you should also check if the player has the milestone.
- style(): **Optional**, A function returning a CSS object, which affects this milestone.
- style: **Optional**, Applies CSS to this milestone, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- unl(): A function returning a boolean to determine if the milestone should be shown. If absent, it is always shown.
- unlocked(): **Optional** A function returning a boolean to determine if the milestone should be shown.
If absent, it is always shown.
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the id for this milestone.
- id: **Assigned automagically**. It's the "key" which the milestone was stored under, for convenient access.
The milestone in the example's id is 0.

View file

@ -44,9 +44,11 @@ Normal subtabs and microtab subtabs both use the same features:
- content: The tab layout code for the subtab, in [the tab layout format](custom-tab-layouts.md)
- style(: **Optional**, A function returning a CSS object, which affects the CSS when in that subtab.
- style: **Optional**, Applies CSS to the whole subtab when switched to, in the form of an "CSS Object",
where the keys are CSS attributes, and the values are the values for those attributes (both as strings)
- buttonStyle(): **Optional**, A function returning a CSS object, which affects the appearance of the button for that subtab.
- buttonStyle: **Optional**, A CSS object, which affects the appearance of the button for that subtab.
- unl(): **Optional**, a function to determine if the button for this subtab should be visible. By default, a subtab is always unlocked.
- unlocked(): **Optional**, a function to determine if the button for this subtab should be visible.
By default, a subtab is always unlocked.
(You can't use the "this" keyword in this function.)

View file

@ -1,21 +1,21 @@
# Upgrades
Upgrades are stored in the following format:
Useful functions for dealing with Upgrades and implementing their effects:
- hasUpg(layer, id): determine if the player has the upgrade
- ugpEffect(layer, id): Returns the current effects of the upgrade, if any
- hasUpgrade(layer, id): determine if the player has the upgrade
- upgradeEffect(layer, id): Returns the current effects of the upgrade, if any
- buyUpgrade(layer, id): Buys an upgrade directly (if affordable)
Hint: Basic point gain is calculated in game.js's "getPointGain".
Hint: Basic point gain is calculated in mod.js's "getPointGen".
Upgrades are stored in the following format:
```js
upgrades: {
rows: # of rows
cols: # of columns
11: {
desc:() => "Blah",
description: "Blah",
more features
}
etc
@ -28,30 +28,34 @@ Individual upgrades can have these features:
- title: **optional**, displayed at the top in a larger font
It can also be a function that returns updating text. Can use basic HTML.
- desc: A description of the upgrade's effect. *You will also have to implement the effect where it is applied.*
- description: A description of the upgrade's effect. *You will also have to implement the effect where it is applied.*
It can also be a function that returns updating text. Can use basic HTML.
- effect(): **optional**, A function that calculates and returns the current values of any bonuses from the upgrade.
Can return a value or an object containing multiple values.
- effectDisplay(effects): **optional**, A function that returns a display of the current effects of the upgrade with
- effectDisplay(): **optional**, A function that returns a display of the current effects of the upgrade with
formatting. Default behavior is to just display the a number appropriately formatted. Can use basic HTML.
- cost: A Decimal for the cost of the upgrade. By default, upgrades cost the main prestige currency for the layer.
- unl(): A function returning a bool to determine if the upgrade is visible or not.
- unlocked(): **optional**, A function returning a bool to determine if the upgrade is visible or not. Default is unlocked.
- onPurchase() - **optional**, this function will be called when the upgrade is purchased.
Good for upgrades like "makes this layer act like it was unlocked first".
By default, upgrades use the main prestige currency for the layer. You can include these to change them:
By default, upgrades use the main prestige currency for the layer. You can include these to change them (but it needs to be a Decimal):
- currencyDisplayName: **optional**, the name to display for the currency for the upgrade
- currencyInternalName: **optional**, the internal name for that currency
- currencyLayer: **optional**, the internal name of the layer that currency is stored in.
If it's not in a layer (like Points), omit.
If it's not in a layer (like Points), omit. If it's not stored directly in a layer, instead use the next feature.
- currencyLocation: **optional**, if your currency is stored in something inside a layer (e.g. a buyable's amount), you can access it this way.
This is a function returning the object in "player" that contains the value (like player[this.layer].buyables)
- style(): **Optional**, A function returning a CSS object, which affects this upgrade.
- style: **Optional**, Applies CSS to this upgrade, in the form of an object where the keys are CSS attributes,
and the values are the values for those attributes (both as strings)
- layer: **Assigned automagically**. It's the same value as the name of this layer, so you can do player[this.layer].points or similar
- id: **Assigned automagically**. It's the id for this upgrade.
- id: **Assigned automagically**. It's the "key" which the upgrade was stored under, for convenient access.
The upgrade in the example's id is 11.

View file

@ -1,23 +1,16 @@
<!DOCTYPE html>
<head>
<script>
const modInfo = {
name: "The Modding Tree",
id: "modbase",
pointsName: "points",
offlineLimit: 1 // In hours
}
</script>
<link rel="stylesheet" type="text/css" href="style.css" />
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
<script type="text/javascript" src="js/break_eternity.js"></script>
<script type="text/javascript" src="js/layerSupport.js"></script>
<script type="text/javascript" src="js/layers.js"></script>
<script type="text/javascript" src="js/mod.js"></script>
<script type="text/javascript" src="js/temp.js"></script>
<script type="text/javascript" src="js/saves.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/game.js"></script>
<script type="text/javascript" src="js/utils.js"></script>
<script type="text/javascript" src="js/v.js"></script>
<script type="text/javascript" src="js/canvas.js"></script>
@ -32,14 +25,15 @@
<br>
<h2>{{modInfo.name}} {{VERSION.withoutName}}</h2><br><br>
<h3>Congratulations! You have reached the end and beaten this game, but for now...</h3><br>
<h3>Please check the Discord to see there are new content updates!</h3><br><br>
<h3>Please check the Discord to see if there are new content updates!</h3><br><br>
<div v-if="!player.timePlayedReset">It took you {{formatTime(player.timePlayed)}} to beat the game.</div>
<div v-if="player.timePlayedReset">Make sure that you record the time in your stream or else your speedrun won't count!</div>
<br>
<button class="longUpg can" onclick="hardReset(true)">Play Again</button>&nbsp;&nbsp;&nbsp;&nbsp;<button class="longUpg can" onclick="keepGoing()">Keep Going</button>
<br><br><br>
<a class="link" href="https://discord.gg/F3xveHV" target="_blank">The Modding Tree Discord</a><br>
<a class="link" href="http://discord.gg/wwQfgPa" target="_blank">Main Prestige Tree Discord</a><br>
<span v-if="modInfo.discordLink"><a class="link" v-bind:href="modInfo.discordLink" target="_blank">{{modInfo.discordName}}</a><br></span>
<a class="link" href="https://discord.gg/F3xveHV" target="_blank" v-bind:style="modInfo.discordLink ? {'font-size': '16px'} : {}">The Modding Tree Discord</a><br>
<a class="link" href="http://discord.gg/wwQfgPa" target="_blank" v-bind:style="{'font-size': '16px'}">Main Prestige Tree server</a><br>
<br><br>
If you would like to speedrun this, press Play Again and record your attempt, then submit on the Discord Server in the channel #speedrun-submissions.
<br><br><br>
@ -48,47 +42,6 @@
</div>
<div v-if="player.tab=='changelog'" class="col right">
<button class="back" onclick="showTab('tree')"></button><br><br><br>
<h3>v1.3.5</h3>
<ul>
<li>The system now handles convertToDecimal for everything automatically!</li>
<li>Created a tutorial for getting started with TMT and Github.</li>
<li>Branches can be defined without a color id. But they can also use hex values for color ids!</li>
<li>Page title is now automatically taken from mod name.</li>
</ul>
<h3>v1.3.4</h3>
<ul>
<li>Added "midsection" feature to add things to a tab's layout while still keeping the standard layout.</li>
<li>Fix for being able to buy more buyables than you should.</li>
</ul><br>
<h3>v1.3.3</h3>
<ul>
<li>Fix for the "order of operations" issue in temp.</li>
</ul><br>
<h3>v1.3.1</h3>
<ul>
<li>Added custom CSS and tooltips for Layer Nodes.</li>
<li>Added custom CSS for upgrades, buyables, milestones, and challenges, both individually and layer-wide.</li>
<li>You can now use HTML in most display text!</li>
<li>You can now make milestones unlockable and not display immediately.</li>
<li>Fixed importing saves, and issue with upgrades not appearing, and probably more.</li>
<li>Optional "name" layer feature, used in confirmation messages.</li>
<li></li>
</ul><br>
<h3>v1.3: Tabception... ception!</h3>
<ul>
<li>Added subtabs! And also a Micro-tab component to let you make smaller subtab-esque areas anywhere.</li>
<li>Added a "custom" prestige formula type, and a number of features to support it.</li>
<li>Added points/sec display (can be disabled, automatically disabled if getPointGen is 0).</li>
<li>Added h-line, v-line and image-display components, plus components for individual upgrades, challenges, and milestones.</li>
<li>Added upgEffect, buyableEffect, and challEffect functions.</li>
<li>Added "hide completed challenges" setting.</li>
<li>Moved old changelogs to a separate place.</li>
<li>Fixed hasMilestone and incr_order.</li>
<li>Static layers now show the currency amount needed for the next one if you can buy max.</li>
<li></li>
</ul><br>
<a href="https://github.com/Acamaeda/The-Modding-Tree/blob/master/changelog.md" target="_blank" class="link" >Full history</a><br>
</div>
<div v-if="player.tab=='info'" class="col right">
@ -96,22 +49,27 @@
<h2>{{modInfo.name}}</h2>
<br>
<h3>{{VERSION.withName}}</h3>
<span v-if="modInfo.author">
<br>
Made by {{modInfo.author}}
</span>
<br>
The Modding Tree by Acamaeda
The Modding Tree {{TMT_VERSION.tmtNum}} by Acamaeda
<br>
The Prestige Tree made by Jacorb and Aarex
<br>
Original idea by papyrus (on discord)
<br><br>
<div class="link" onclick="showTab('changelog')">Changelog</div><br>
<a class="link" href="https://discord.gg/F3xveHV" target="_blank">The Modding Tree Discord</a><br>
<a class="link" href="http://discord.gg/wwQfgPa" target="_blank">Main Prestige Tree server</a><br>
<a v-bind:href="modInfo.changelogLink" target="_blank" class="link" >Changelog</a><br>
<span v-if="modInfo.discordLink"><a class="link" v-bind:href="modInfo.discordLink" target="_blank">{{modInfo.discordName}}</a><br></span>
<a class="link" href="https://discord.gg/F3xveHV" target="_blank" v-bind:style="modInfo.discordLink ? {'font-size': '16px'} : {}">The Modding Tree Discord</a><br>
<a class="link" href="http://discord.gg/wwQfgPa" target="_blank" v-bind:style="{'font-size': '16px'}">Main Prestige Tree server</a><br>
<br>
Note by Jacorb: If anyone wishes to make a mod of this game, that is perfectly fine with me, just make sure to name it something different (ex: Prestige Tree NG+) and to let me know on <a href="https://discord.gg/wwQfgPa" target="_blank">my discord</a>.
<br><br>
Time Played: {{ formatTime(player.timePlayed) }}<br><br>
<h3>Hotkeys</h3><br>
<span v-for="key in hotkeys" v-if="player[key.layer].unl"><br>{{key.desc}}</span>
<span v-for="key in hotkeys" v-if="player[key.layer].unlocked"><br>{{key.description}}</span>
</div>
<div v-if="player.tab=='options'" class="col right">
<button class="back" onclick="showTab('tree')"></button><br>
@ -132,23 +90,11 @@
<td><button class="opt" onclick="toggleOpt('hqTree')">High-Quality Tree: {{ player.hqTree?"ON":"OFF" }}</button></td>
</tr>
<tr>
<td><button class="opt" onclick="toggleOpt('hideChalls')">Completed Challenges: {{ player.hideChalls?"HIDDEN":"SHOWN" }}</button></td>
<td><button class="opt" onclick="toggleOpt('hideChallenges')">Completed Challenges: {{ player.hideChallenges?"HIDDEN":"SHOWN" }}</button></td>
<!-- <td><button class="opt" onclick="toggleOpt('oldStyle')">Style: {{ player.oldStyle?"v1.0":"NEW" }}</button></td>-->
</tr>
</table>
</div>
<<<<<<< Updated upstream
<div id="treeTab" v-if="player.tab!='gameEnded'" onscroll="resizeCanvas()" v-bind:class="{ fullWidth: player.tab == 'tree', col: player.tab != 'tree', left: player.tab != 'tree'}">
<div id="version" onclick="showTab('changelog')">{{VERSION.withoutName}}</div>
<img id="optionWheel" v-if="player.tab!='options'" src="options_wheel.png" onclick="showTab('options')"></img>
<div id="info" v-if="player.tab!='info'" onclick="showTab('info')"><br>i</div>
<img id="discord" onclick="window.open('https://discord.gg/wwQfgPa','mywindow')" src="discord.png" target="_blank"></img>
<span v-if="player.devSpeed && player.devSpeed != 1">
<br>Dev Speed: {{format(player.devSpeed)}}x<br>
</span>
<span v-if="player.offTime !== undefined">
<br>Offline Time: {{formatTime(player.offTime.remain)}}<br>
=======
<div id="treeOverlay" class="treeOverlay" v-if="player.tab!='gameEnded'" onscroll="resizeCanvas()" v-bind:class="{ fullWidth: player.tab == 'tree', col: player.tab != 'tree', left: player.tab != 'tree'}">
<div id="version" class="overlayThing" style="margin-right: 13px">{{VERSION.withoutName}}</div>
<img id="optionWheel" class="overlayThing" v-if="player.tab!='options'" src="options_wheel.png" onclick="showTab('options')"></img>
@ -209,46 +155,9 @@
<td v-if="player.tab=='tree'&& someLayerUnlocked(row) && row != 0" class="left"><br><br><img class="remove" src="remove.png" onclick="resetRow(row)"></img></td>
<td v-for="node in TREE_LAYERS[row]"><layer-node :layer='node.layer' :abb='tmp[node.layer].symbol'></layer-node></td>
<table><button class="treeNode hidden"></button></td></table>
>>>>>>> Stashed changes
</span>
<span v-if="false && !player.keepGoing">
<br>Reach {{formatWhole(ENDGAME)}} to beat the game!<br>
</span>
<br>
<span v-if="player.points.lt('1e1000')">You have </span>
<h2 id="points">{{format(player.points)}}</h2>
<span v-if="player.points.lt('1e1e6')"> {{modInfo.pointsName}}</span>
<br>
<span v-if="showPointGen()">({{format(getPointGen())}}/sec)</span>
<br><br><br><br>
<!-- *************************** Modify the tree in the table below! *************************** -->
<table>
<td><layer-node layer='c' abb='C'></layer-node></td>
</table><table>
<td><button class="treeNode hidden"></button></td>
</table><table>
<td v-if="player.tab=='tree'&&(player.c.unl||player.c.unl)" class="left"><br><br><img class="remove" src="remove.png" onclick="resetRow(1)"></img></td>
<td><layer-node layer='f' abb='F'></layer-node></td>
</table><table>
<td><button class="treeNode hidden"></button></td>
</table>
<canvas id="treeCanvas" class="canvas"></canvas>
</div>
<<<<<<< Updated upstream
<div v-for="layer in LAYERS">
<div v-if="player.tab==layer" v-bind:class="'col right'" v-bind:style="tmp.style[layer] ? tmp.style[layer] : {}">
<button class="back" onclick="showTab('tree')"></button><br><br><br>
<div v-if="!layers[layer].tabFormat">
<main-display v-bind:style="tmp.componentStyles[layer]['main-display']" :layer="layer"></main-display>
<prestige-button v-bind:style="tmp.componentStyles[layer]['prestige-button']" :layer="layer"></prestige-button>
<span v-if="layers[layer].type=='normal' && tmp.resetGain[layer].lt(100) && player[layer].points.lt(1e3)"><br>You have {{formatWhole(tmp.layerAmt[layer])}} {{layers[layer].baseResource}}</span>
<br><br>
<span v-if="player[layer].best != undefined">Your best {{layers[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)}} {{layers[layer].resource}}<br></span>
<milestones v-bind:style="tmp.componentStyles[layer].milestones" :layer="layer"></milestones>
<div v-if="Array.isArray(layers[layer].midsection)">
<column :layer="layer" :data="layers[layer].midsection"></column>
=======
<div v-for="layer in LAYERS" >
<div v-if="player.tab==layer" v-bind:class="'col right fast tab'" v-bind:style="[tmp[layer].style ? tmp[layer].style : {}, (tmp[layer].tabFormat && !Array.isArray(tmp[layer].tabFormat)) ? tmp[layer].tabFormat[player.subtabs[layer].mainTabs].style : {}]">
<button class="back" onclick="showTab('tree')"></button><br><br><br>
@ -261,31 +170,25 @@
<milestones v-bind:style="tmp[layer].componentStyles.milestones" :layer="layer"></milestones>
<div v-if="Array.isArray(tmp[layer].midsection)">
<column :layer="layer" :data="tmp[layer].midsection"></column>
>>>>>>> Stashed changes
</div>
<buyables v-bind:style="tmp.componentStyles[layer].buyables" :layer="layer"></buyables>
<upgrades v-bind:style="tmp.componentStyles[layer]['upgrades']" :layer="layer"></upgrades>
<challs v-bind:style="tmp.componentStyles[layer]['challs']" :layer="layer"></challs>
<clickables v-bind:style="tmp[layer].componentStyles['clickables']" :layer="layer"></clickables>
<buyables v-bind:style="tmp[layer].componentStyles.buyables" :layer="layer"></buyables>
<upgrades v-bind:style="tmp[layer].componentStyles['upgrades']" :layer="layer"></upgrades>
<challenges v-bind:style="tmp[layer].componentStyles['challenges']" :layer="layer"></challenges>
<br><br>
</div>
<<<<<<< Updated upstream
<div v-if="layers[layer].tabFormat">
<div v-if="Array.isArray(layers[layer].tabFormat)">
<column :layer="layer" :data="layers[layer].tabFormat"></column>
=======
<div v-if="tmp[layer].tabFormat">
<div v-if="Array.isArray(tmp[layer].tabFormat)">
<column :layer="layer" :data="tmp[layer].tabFormat"></column>
>>>>>>> Stashed changes
</div>
<div v-else v-bind:style="[{'margin-top': '-50px'}, readData(layers[layer].tabFormat[player.subtabs[layer].mainTabs].style)]">
<div v-else v-bind:style="[{'margin-top': '-50px'}]">
<div class="upgTable" v-bind:style="{'padding-top': '25px', 'margin-bottom': '24px'}">
<tab-buttons v-bind:style="tmp.componentStyles[layer]['tab-buttons']" :layer="layer" :data="layers[layer].tabFormat" :name="'mainTabs'"></tab-buttons>
<tab-buttons v-bind:style="tmp[layer].componentStyles['tab-buttons']" :layer="layer" :data="tmp[layer].tabFormat" :name="'mainTabs'"></tab-buttons>
</div>
<column :layer="layer" :data="layers[layer].tabFormat[player.subtabs[layer].mainTabs].content"></column>
<column :layer="layer" :data="tmp[layer].tabFormat[player.subtabs[layer].mainTabs].content"></column>
</div>
</div>
</div>
</div>
</div>
</body>
</body>

516
js/Demo/demoLayers.js Normal file
View file

@ -0,0 +1,516 @@
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)
},
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: [
["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
addLayer("a", {
startData() { return {
unlocked: true,
points: new Decimal(0),
}},
color: "yellow",
resource: "achievement power",
type: "none",
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",
},
)

66
js/Demo/demoMod.js Normal file
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
}

View file

@ -40,10 +40,10 @@ function drawTree() {
if (!retrieveCanvasData()) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (layer in layers){
if (layers[layer].layerShown() && layers[layer].branches){
for (branch in layers[layer].branches)
if (tmp[layer].layerShown && tmp[layer].branches){
for (branch in tmp[layer].branches)
{
drawTreeBranch(layer, layers[layer].branches[branch])
drawTreeBranch(layer, tmp[layer].branches[branch])
}
}
}

View file

@ -1,52 +1,27 @@
var player;
var tmp = {};
var needCanvasUpdate = true;
var NaNalert = false;
var gameEnded = false;
let VERSION = {
num: "1.3.5",
name: "Tabception... ception!"
}
// Determines if it should show points/sec
function showPointGen(){
return (tmp.pointGen.neq(new Decimal(0)))
}
// Calculate points/sec!
function getPointGen() {
if(!hasUpg("c", 11))
return new Decimal(0)
let gain = new Decimal(1)
if (hasUpg("c", 12)) gain = gain.times(upgEffect("c", 12))
return gain
}
// Function to determine if the player is in a challenge
function inChallenge(layer, id){
let chall = player[layer].active
if (chall==toNumber(id)) return true
if (layers[layer].challs[chall].countsAs)
return layers[layer].challs[id].countsAs.includes(id)
// 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 = layers[layer].type
if (tmp.gainExp[layer].eq(0)) return new Decimal(0)
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 ((!layers[layer].canBuyMax()) || tmp.layerAmt[layer].lt(tmp.layerReqs[layer])) return new Decimal(1)
let gain = tmp.layerAmt[layer].div(tmp.layerReqs[layer]).div(tmp.gainMults[layer]).max(1).log(layers[layer].base).times(tmp.gainExp[layer]).pow(Decimal.pow(layers[layer].exponent, -1))
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.layerAmt[layer].lt(tmp.layerReqs[layer])) return new Decimal(0)
let gain = tmp.layerAmt[layer].div(tmp.layerReqs[layer]).pow(layers[layer].exponent).times(tmp.gainMults[layer]).pow(tmp.gainExp[layer])
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"){
@ -58,22 +33,26 @@ function getResetGain(layer, useType = null) {
function getNextAt(layer, canMax=false, useType = null) {
let type = useType
if (!useType) type = layers[layer].type
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 (tmp.gainExp[layer].eq(0)) return new Decimal(1/0)
if (type=="static")
{
if (!layers[layer].canBuyMax()) canMax = false
let amt = player[layer].points.plus((canMax&&tmp.layerAmt[layer].gte(tmp.nextAt[layer]))?tmp.resetGain[layer]:0)
let extraCost = Decimal.pow(layers[layer].base, amt.pow(layers[layer].exponent).div(tmp.gainExp[layer])).times(tmp.gainMults[layer])
let cost = extraCost.times(tmp.layerReqs[layer]).max(tmp.layerReqs[layer])
if (layers[layer].resCeil) cost = cost.ceil()
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.resetGain[layer].add(1)
let next = tmp[layer].resetGain.add(1)
if (next.gte("e1e7")) next = next.div("e5e6").pow(2)
next = next.root(tmp.gainExp[layer]).div(tmp.gainMults[layer]).root(layers[layer].exponent).times(tmp.layerReqs[layer]).max(tmp.layerReqs[layer])
if (layers[layer].resCeil) next = next.ceil()
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)
@ -83,9 +62,9 @@ function getNextAt(layer, canMax=false, useType = null) {
// Return true if the layer should be highlighted. By default checks for upgrades only.
function shouldNotify(layer){
for (id in layers[layer].upgrades){
for (id in tmp[layer].upgrades){
if (!isNaN(id)){
if (canAffordUpg(layer, id) && !hasUpg(layer, id) && tmp.upgrades[layer][id].unl){
if (canAffordUpgrade(layer, id) && !hasUpgrade(layer, id) && tmp[layer].upgrades[id].unlocked){
return true
}
}
@ -98,33 +77,48 @@ function shouldNotify(layer){
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].active = null // Exit challenges on any row reset on an equal or higher row
player[lr].activeChallenge = null // Exit challenges on any row reset on an equal or higher row
layers[lr].doReset(layer)
}
else
if(layers[layer].row > layers[lr].row) fullLayerReset(lr)
if(tmp[layer].row > tmp[lr].row && row !== "side") layerDataReset(lr)
}
}
function fullLayerReset(layer) {
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]]
}
player[layer] = layers[layer].startData();
player[layer].upgrades = []
player[layer].milestones = []
player[layer].challs = []
if (layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)) {
if (player.subtabs[layer] == undefined) player.subtabs[layer] = {}
if (player.subtabs[layer].mainTabs == undefined) player.subtabs[layer].mainTabs = Object.keys(layers[layer].tabFormat)[0]
}
if (layers[layer].microtabs) {
if (player.subtabs[layer] == undefined) player.subtabs[layer] = {}
for (item in layers[layer].microtabs)
if (player.subtabs[layer][item] == undefined) player.subtabs[layer][item] = Object.keys(layers[layer].microtabs[item])[0]
}
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){
@ -133,15 +127,6 @@ function resetBuyables(layer){
player[layer].spentOnBuyables = new Decimal(0)
}
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 addPoints(layer, gain) {
player[layer].points = player[layer].points.add(gain).max(0)
@ -150,22 +135,22 @@ function addPoints(layer, gain) {
}
function generatePoints(layer, diff) {
addPoints(layer, tmp.resetGain[layer].times(diff))
addPoints(layer, tmp[layer].resetGain.times(diff))
}
var prevOnReset
function doReset(layer, force=false) {
let row = layers[layer].row
let row = tmp[layer].row
if (!force) {
if (tmp.layerAmt[layer].lt(tmp.layerReqs[layer])) return;
let gain = tmp.resetGain[layer]
if (layers[layer].type=="static") {
if (tmp.layerAmt[layer].lt(tmp.nextAt[layer])) return;
gain =(layers[layer].canBuyMax() ? gain : 1)
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 (layers[layer].type=="custom") {
if (!tmp.canReset[layer]) return;
if (tmp[layer].type=="custom") {
if (!tmp[layer].canReset) return;
}
if (layers[layer].onPrestige)
@ -173,139 +158,40 @@ function doReset(layer, force=false) {
addPoints(layer, gain)
updateMilestones(layer)
updateAchievements(layer)
if (!player[layer].unl) {
player[layer].unl = true;
if (!player[layer].unlocked) {
player[layer].unlocked = true;
needCanvasUpdate = true;
if (layers[layer].incr_order){
lrs = layers[layer].incr_order
if (tmp[layer].increaseUnlockOrder){
lrs = tmp[layer].increaseUnlockOrder
for (lr in lrs)
if (!player[lrs[lr]].unl) player[lrs[lr]].order++
if (!player[lrs[lr]].unlocked) player[lrs[lr]].unlockOrder++
}
}
tmp.layerAmt[layer] = new Decimal(0) // quick fix
tmp[layer].baseAmount = new Decimal(0) // quick fix
}
if (layers[layer].resetsNothing && layers[layer].resetsNothing()) return
if (tmp[layer].resetsNothing) return
for (layerResetting in layers) {
if (row >= layers[layerResetting].row && (!force || layerResetting != layer)) completeChall(layerResetting)
if (row >= layers[layerResetting].row && (!force || layerResetting != layer)) completeChallenge(layerResetting)
}
prevOnReset = {...player} //Deep Copy
player.points = (row == 0 ? new Decimal(0) : new Decimal(10))
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 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 \"" + (layers[layer].name ? layers[layer].name : layer) + "\" reset as well!")) return
layers[layer].buyables.respec()
updateBuyableTemp(layer)
}
function canAffordUpg(layer, id) {
let upg = layers[layer].upgrades[id]
let cost = tmp.upgrades[layer][id].cost
return canAffordPurchase(layer, upg, cost)
}
function hasUpg(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 hasChall(layer, id){
return (player[layer].challs.includes(toNumber(id)) || player[layer].challs.includes(id.toString()))
}
function upgEffect(layer, id){
return (tmp.upgrades[layer][id].effect)
}
function challEffect(layer, id){
return (tmp.challs[layer][id].effect)
}
function buyableEffect(layer, id){
return (tmp.buyables[layer][id].effect)
}
function canAffordPurchase(layer, thing, cost) {
if (thing.currencyInternalName){
let name = thing.currencyInternalName
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 buyUpg(layer, id) {
if (!player[layer].unl) return
if (!layers[layer].upgrades[id].unl()) return
if (player[layer].upgrades.includes(id)) return
let upg = layers[layer].upgrades[id]
let cost = tmp.upgrades[layer][id].cost
if (upg.currencyInternalName){
let name = upg.currencyInternalName
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].unl) return
if (!tmp.buyables[layer][id].unl) return
if (!tmp.buyables[layer][id].canAfford) return
if (!layers[layer].buyables[id].buyMax) return
layers[layer].buyables[id].buyMax()
updateBuyableTemp(layer)
}
function buyBuyable(layer, id) {
if (!player[layer].unl) return
if (!tmp.buyables[layer][id].unl) return
if (!tmp.buyables[layer][id].canAfford) return
layers[layer].buyables[id].buy()
updateBuyableTemp(layer)
}
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]
@ -314,72 +200,77 @@ function resetRow(row) {
rowReset(row+1, post_layers[0])
doReset(pre_layers[0], true)
for (let layer in layers) {
player[layers[layer]].unl = false
if (player[layers[layer]].order) player[layers[layer]].order = 0
player[layer].unlocked = false
if (player[layer].unlockOrder) player[layer].unlockOrder = 0
}
player.points = new Decimal(10)
player.points = getStartPoints()
updateTemp();
resizeCanvas();
}
function startChall(layer, x) {
function startChallenge(layer, x) {
let enter = false
if (!player[layer].unl) return
if (player[layer].active == x) {
completeChall(layer, x)
delete player[layer].active
if (!player[layer].unlocked) return
if (player[layer].activeChallenge == x) {
completeChallenge(layer, x)
delete player[layer].activeChallenge
} else {
enter = true
}
doReset(layer, true)
if(enter) player[layer].active = x
if(enter) player[layer].activeChallenge = x
updateChallTemp(layer)
updateChallengeTemp(layer)
}
function canCompleteChall(layer, x)
function canCompleteChallenge(layer, x)
{
if (x != player[layer].active) return
if (x != player[layer].activeChallenge) return
let chall = layers[layer].challs[x]
let challenge = tmp[layer].challenges[x]
if (chall.currencyInternalName){
let name = chall.currencyInternalName
if (chall.currencyLayer){
let lr = chall.currencyLayer
return !(player[lr][name].lt(readData(chall.goal)))
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(chall.cost))
return !(player[name].lt(challenge.cost))
}
}
else {
return !(player[layer].points.lt(chall.cost))
return !(player[layer].points.lt(challenge.cost))
}
}
function completeChall(layer, x) {
var x = player[layer].active
function completeChallenge(layer, x) {
var x = player[layer].activeChallenge
if (!x) return
if (! canCompleteChall(layer, x)) return
if (!player[layer].challs.includes(x)) {
needCanvasUpdate = true
player[layer].challs.push(x);
if (layers[layer].challs[x].onComplete) layers[layer].challs[x].onComplete()
if (! canCompleteChallenge(layer, x)){
delete player[layer].activeChallenge
return
}
delete player[layer].active
updateChallTemp(layer)
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 : "")
const ENDGAME = new Decimal("e280000000");
function gameLoop(diff) {
if (player.points.gte(ENDGAME) || gameEnded) gameEnded = 1
if (isEndgame() || gameEnded) gameEnded = 1
if (isNaN(diff)) diff = 0
if (gameEnded && !player.keepGoing) {
@ -388,18 +279,44 @@ function gameLoop(diff) {
}
if (player.devSpeed) diff *= player.devSpeed
addTime(diff)
let limit = maxTickLength()
if(diff > limit)
diff = limit
for (layer in layers){
if (layers[layer].update) layers[layer].update(diff);
addTime(diff)
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 (layer in layers){
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].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) {
@ -407,7 +324,7 @@ function gameLoop(diff) {
player.autosave = false;
NaNalert = true;
alert("We have detected a corruption in your save. Please visit https://discord.gg/wwQfgPa for help.")
alert("We have detected a corruption in your save. Please visit one of the discords in the info panel for help.")
}
}
@ -428,7 +345,7 @@ var interval = setInterval(function() {
let now = Date.now()
let diff = (now - player.time) / 1e3
if (player.offTime !== undefined) {
if (player.offTime.remain > modInfo.offlineLimit * 3600000) player.offlineTime.remain = modInfo.offlineLimit * 3600000
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

View file

@ -8,6 +8,8 @@ var LAYERS = Object.keys(layers);
var hotkeys = {};
var maxRow = 0;
function updateHotkeys()
{
hotkeys = {};
@ -23,10 +25,14 @@ function updateHotkeys()
}
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){
@ -34,6 +40,8 @@ function updateLayers(){
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
}
}
}
@ -42,14 +50,31 @@ function updateLayers(){
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].challs){
for (thing in layers[layer].challs){
if (layers[layer].achievements){
for (thing in layers[layer].achievements){
if (!isNaN(thing)){
layers[layer].challs[thing].id = thing
layers[layer].challs[thing].layer = layer
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
}
}
}
@ -59,24 +84,66 @@ function updateLayers(){
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].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)
row = layers[layer].row
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
updateLayers()
}
// If data is a function, return the result of calling it. Otherwise, return the data.
@ -85,4 +152,18 @@ function readData(data, args=null){
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

View file

@ -1,320 +1,28 @@
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.
addLayer("p", {
name: "prestige", // This is optional, only used in a few places, If absent it just uses the layer id.
symbol: "P", // 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 {
unl: true,
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
color: "#4BDC13",
requires: new Decimal(10), // Can be a function that takes requirement increases into account
resource: "prestige points", // Name of prestige currency
baseResource: "points", // 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))
resCeil: 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 (hasUpg(this.layer, 166)) mult = mult.times(2) // These upgrades don't exist
if (hasUpg(this.layer, 120)) mult = mult.times(upgEffect(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)
},
milestones: {
0: {requirementDesc:() => "3 Lollipops",
done() {return player[this.layer].best.gte(3)}, // Used to determine when to give the milestone
effectDesc:() => "Unlock the next milestone",
},
1: {requirementDesc:() => "4 Lollipops",
unl() {return hasMilestone(this.layer, 0)},
done() {return player[this.layer].best.gte(4)},
effectDesc:() => "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'
}},
},
},
challs: {
rows: 1,
cols: 1,
11: {
name:() => "Fun",
desc:() => "Makes the game 0% harder",
unl() { 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
effect() {
let ret = player[this.layer].points.add(1).tetrate(0.02)
return ret;
},
effectDisplay(x) { return format(x)+"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.
reward:() => "Says hi",
onComplete() {console.log("hiii")} // Called when you complete the challenge
},
},
upgrades: {
rows: 1,
cols: 3,
11: {
title:() => "Generator of Genericness",
desc:() => "Gain 1 Point every second.",
cost:() => new Decimal(1),
unl() { return player[this.layer].unl }, // The upgrade is only visible when this is true
},
12: {
desc:() => "Candy generation is faster based on your unspent Lollipops.",
cost:() => new Decimal(1),
unl() { return (hasUpg(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(fx) { return format(fx)+"x" }, // Add formatting to the effect
},
13: {
desc:() => "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
currencyLayer: "", // Leave empty if not in a layer "e.g. points"
unl() { return (hasUpg(this.layer, 12))},
onPurchase() { // This function triggers when the upgrade is purchased
player[this.layer].order = 0
},
style() {
if (hasUpg(this.layer, this.id)) return {
'background-color': '#1111dd'
}
else if (!canAffordUpg(this.layer, this.id)) {
return {
'background-color': '#dd1111'
}
} // Otherwise use the default
},
},
},
buyables: {
rows: 1,
cols: 1,
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) { // 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) { // 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.buyables[this.layer][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)
},
unl() { return player[this.layer].unl },
canAfford() {
return player[this.layer].points.gte(tmp.buyables[this.layer][this.id].cost)},
buy() {
cost = tmp.buyables[this.layer][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
},
},
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) fullLayerReset(this.layer) // This is actually the default behavior
},
layerShown() {return true}, // Condition for when layer appears on the tree
update(diff) {
if (player[this.layer].upgrades.includes(11)) player.points = player.points.add(tmp.pointGen.times(diff)).max(0)
}, // Do any gameloop things (e.g. resource generation) inherent to this layer
automate() {
}, // Do any automation inherent to this layer if appropriate
updateTemp() {
}, // Do any necessary temp updating, not that important usually
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", desc: "C: reset for lollipops or whatever", onPress(){if (player[this.layer].unl) doReset(this.layer)}},
{key: "ctrl+c" + this.layer, desc: "Ctrl+c: respec things", onPress(){if (player[this.layer].unl) respecBuyables(this.layer)}},
{key: "p", description: "Reset for prestige points", onPress(){if (canReset(this.layer)) doReset(this.layer)}},
],
incr_order: [], // 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
}
},
// 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", function() {return "Melt your points into "}],
["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", "challs"],
},
thingies: {
style() {return {'background-color': '#222222'}},
buttonStyle() {return {'border-color': 'orange'}},
content:[
["buyables", "150px"], "blank",
["row", [
["toggle", ["c", "beep"]], ["blank", ["30px", "10px"]], // Width, height
["display-text", function() {return "Beep"}], "blank", ["v-line", "200px"],
["column", [
["prestige-button", function() {return "Be redundant for "}, {'width': '150px', 'height': '30px'}],
["prestige-button", function() {return "Be redundant for "}, {'width': '150px', 'height': '30px'}],
]],
], {'width': '600px', 'height': '350px', 'background-color': 'green', 'border-style': 'solid'}],
"blank",
["display-image", "discord.png"],],
},
illuminati: {
unl() {return (hasUpg("c", 13))},
content:[
["raw-html", function() {return "<h1> C O N F I R M E D </h1>"}],
["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: {
"chall"() {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)
}
})
// This layer is mostly minimal but it uses a custom prestige type
addLayer("f", {
startData() { return {
unl: false,
points: new Decimal(0),
boop: false,
}},
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,
resCeil: true,
canBuyMax:() => true,
gainMult() {
return new Decimal(1)
},
gainExp() {
return new Decimal(1)
},
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.nextAt[layer]) + " candies)"
if (this.canBuyMax()) return "Hi! I'm a <u>weird dinosaur</u> and I'll give you <b>" + formatWhole(tmp.resetGain[this.layer]) + "</b> Farm Points in exchange for all of your candies and lollipops! (You'll get another one at " + formatWhole(tmp.nextAtDisp[layer]) + " candies)"
},
getResetGain() {
return getResetGain(this.layer, useType = "static")
},
getNextAt(canMax=false) { //
return getNextAt(this.layer, canMax, useType = "static")
},
canReset() {
return tmp.layerAmt[this.layer].gte(tmp.nextAt[this.layer])
},
},
)
layerShown(){return true},
})

61
js/mod.js Normal file
View file

@ -0,0 +1,61 @@
let modInfo = {
name: "The ??? Tree",
id: "mymod",
author: "nobody",
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: "0.0",
name: "Literally nothing",
}
// 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"]
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 = new Decimal(1)
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 {
}}
// Display extra things at the top of the page
var displayThings = [
]
// 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
}

View file

@ -1,3 +0,0 @@
const SAVES = {
PRE_SUPER_BOOSTERS: "eyJ0YWIiOiJvcHRpb25zIiwidGltZSI6MTU5ODE5ODI4NjQ4OCwiYXV0b3NhdmUiOnRydWUsInBvaW50cyI6IjMuMzYwMTkyNjc5ODczNDgzZTQxNDMiLCJwIjp7InVubCI6dHJ1ZSwicG9pbnRzIjoiMi4xMjI1NzI5ODA3NjQzMTQ2ZTI3NjMiLCJ1cGdyYWRlcyI6WzExLDEyLDIxLDIyLDEzLDIzXSwiYmVzdCI6IjUxMDIyNjAwNiJ9LCJiIjp7InVubCI6dHJ1ZSwicG9pbnRzIjoiMTgwIiwidXBncmFkZXMiOlsxMSwxMiwxMywyMiwyMSwyM10sImJlc3QiOiIxODAiLCJhdXRvIjp0cnVlfSwiZyI6eyJ1bmwiOnRydWUsInBvaW50cyI6IjEzOSIsInBvd2VyIjoiMi4zNDMxNTg3NTczMjc0ODVlMzM5IiwidXBncmFkZXMiOlsxMSwxMiwxMywxNCwxNSwyMiwyMSwyMywyNCwyNV0sImJlc3QiOiIxMzkiLCJhdXRvIjp0cnVlfSwiZSI6eyJ1bmwiOnRydWUsInBvaW50cyI6IjEuNzAxNTAzNTMzMDU4MTA3NmU4NiIsImJlc3QiOiIxLjcwMTUwMzUzMzA1ODEwNzZlODYiLCJlbmhhbmNlcnMiOiIzMyIsInVwZ3JhZGVzIjpbMTEsMTIsMTMsMTQsMTUsMjEsMjMsMjIsMjQsMjVdLCJvcmRlciI6MH0sInQiOnsidW5sIjp0cnVlLCJvcmRlciI6MCwicG9pbnRzIjoiMTYiLCJiZXN0IjoiMTYiLCJlbmVyZ3kiOiIxLjA4MDMzODY3NDA5Mjg3NmU5NCIsImV4dENhcHN1bGVzIjoiMjYiLCJ1cGdyYWRlcyI6WzExLDEyLDEzLDE0LDIxLDIyLDIzLDI0LDMxLDMyLDMzXX0sInMiOnsidW5sIjp0cnVlLCJvcmRlciI6MCwicG9pbnRzIjoiMTYiLCJiZXN0IjoiMTYiLCJzcGVudCI6IjY4IiwiYnVpbGRpbmdzIjp7IjEiOiIxNyIsIjIiOiIyNiIsIjMiOiIxMyIsIjQiOiI4IiwiNSI6IjQifSwidXBncmFkZXMiOlsxMSwxMiwxMywxNCwyMSwyMywyNCwzMSwyMiwzMiwzMywzNF19LCJzYiI6eyJ1bmwiOmZhbHNlLCJvcmRlciI6MCwicG9pbnRzIjoiMCIsImJlc3QiOiIwIiwidXBncmFkZXMiOltdfSwidmVyc2lvblR5cGUiOiJhbHBoYSIsInZlcnNpb24iOjEwfQ==",
}

View file

@ -1,312 +1,158 @@
function setupTemp(){
if (!tmp.challActive) {tmp.challActive = {}}
if (!tmp.challs) tmp.challs = {}
for (layer in layers) {
if(layers[layer].challs !== undefined){
tmp.challActive[layer] = {}
setupChallTemp(layer)
}
}
var tmp = {}
if (!tmp.upgrades) tmp.upgrades = {}
for (layer in layers) {
if(layers[layer].upgrades !== undefined){
setupUpgradeTemp(layer)
}
// 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].prestigeButtonText = {}
setupBarStyles(layer)
}
if (!tmp.milestones) tmp.milestones = {}
for (layer in layers) {
if(layers[layer].milestones !== undefined){
setupMilestoneTemp(layer)
}
function setupTempData(layerData, tmpData) {
for (item in layerData){
if (layerData[item] == null) {
tmpData[item] = null
}
}
if (!tmp.buyables) tmp.buyables = {}
for (layer in layers) if (layers[layer].buyables) {
if(layers[layer].buyables !== undefined){
setupBuyableTemp(layer)
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.genPoints == undefined) tmp.genPoints = false
if (tmp === undefined)
setupTemp()
updateTempData(layers, tmp)
if (!tmp.layerReqs) tmp.layerReqs = {}
for (layer in layers) tmp.layerReqs[layer] = layers[layer].requires()
if (!tmp.layerEffs) tmp.layerEffs = {}
for (layer in layers) if (layers[layer].effect) tmp.layerEffs[layer] = layers[layer].effect()
if (!tmp.gainMults) tmp.gainMults = {}
if (!tmp.gainExp) tmp.gainExp = {}
if (!tmp.resetGain) tmp.resetGain = {}
if (!tmp.nextAt) tmp.nextAt = {}
if (!tmp.layerAmt) tmp.layerAmt = {}
if (!tmp.layerColor) tmp.layerColor = {}
if (!tmp.layerShown) tmp.layerShown = {}
if (!tmp.effectDescription) tmp.effectDescription = {}
if (!tmp.style) tmp.style = {}
if (!tmp.nodeStyle) tmp.nodeStyle = {}
if (!tmp.notify) tmp.notify = {}
if (!tmp.nextAtDisp) tmp.nextAtDisp = {}
if (!tmp.prestigeButtonText) tmp.prestigeButtonText = {}
if (!tmp.canReset) tmp.canReset = {}
if (!tmp.tooltips) tmp.tooltips = {}
if (!tmp.tooltipsLocked) tmp.tooltipsLocked = {}
for (layer in layers) {
if (layers[layer].color) tmp.layerColor[layer] = layers[layer].color()
if (layers[layer].style) tmp.style[layer] = layers[layer].style()
tmp.layerShown[layer] = layers[layer].layerShown()
tmp.layerAmt[layer] = layers[layer].baseAmount()
tmp.gainMults[layer] = layers[layer].gainMult()
tmp.gainExp[layer] = layers[layer].gainExp()
tmp.resetGain[layer] = getResetGain(layer)
tmp.nextAt[layer] = getNextAt(layer)
tmp.notify[layer] = shouldNotify(layer)
tmp.nextAtDisp[layer] = getNextAt(layer, true)
if (layers[layer].effectDescription) tmp.effectDescription[layer] = layers[layer].effectDescription()
if (layers[layer].canReset) tmp.canReset[layer] = layers[layer].canReset()
if (layers[layer].prestigeButtonText) tmp.prestigeButtonText[layer] = layers[layer].prestigeButtonText()
if (layers[layer].tooltip) tmp.tooltips[layer] = layers[layer].tooltip()
if (layers[layer].tooltipLocked) tmp.tooltipsLocked[layer] = layers[layer].tooltipLocked()
if (layers[layer].nodeStyle) tmp.nodeStyle[layer] = layers[layer].nodeStyle()
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].prestigeButtonText = prestigeButtonText(layer)
constructBarStyles(layer)
}
tmp.pointGen = getPointGen()
for (layer in layers){
if (layers[layer].updateTemp) layers[layer].updateTemp()
tmp.displayThings = []
for (thing in displayThings){
let text = displayThings[thing]
if (isFunction(text)) text = text()
tmp.displayThings.push(text)
}
if (!tmp.componentStyles) tmp.componentStyles = {}
for (layer in layers) if (layers[layer].componentStyles) {
if(layers[layer].componentStyles !== undefined){
tmp.componentStyles[layer] = {}
for (item in layers[layer].componentStyles)
tmp.componentStyles[layer][item] = layers[layer].componentStyles[item]()
}
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)
}
if (!tmp.microtabs) tmp.microtabs = {}
for (layer in layers) {
if (!tmp.microtabs[layer]) tmp.microtabs[layer] = {}
if (layers[layer].microtabs) {
if(layers[layer].microtabs !== undefined){
updateMicrotabTemp(layer)
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 + '%)'
}
}
}
if(layers[layer].tabFormat && !Array.isArray(layers[layer].tabFormat)){
let data2 = layers[layer].tabFormat
let set = "mainTabs"
if (!tmp.microtabs[layer][set]) tmp.microtabs[layer][set] = {}
for (tab in data2) {
if (!tmp.microtabs[layer][set][tab])
tmp.microtabs[layer][set][tab] = {}
if(data2[tab].style)
tmp.microtabs[layer][set][tab].style = data2[tab].style()
if(data2[tab].buttonStyle)
tmp.microtabs[layer][set][tab].buttonStyle = data2[tab].buttonStyle()
}
}
}
if (!tmp.challActive) {tmp.challActive = {}}
if (!tmp.challs) tmp.challs = {}
for (layer in layers) {
if(layers[layer].challs !== undefined){
tmp.challActive[layer] = {}
updateChallTemp(layer)
}
}
if (!tmp.upgrades) tmp.upgrades = {}
for (layer in layers) {
if(layers[layer].upgrades !== undefined){
updateUpgradeTemp(layer)
}
}
if (!tmp.milestones) tmp.milestones = {}
for (layer in layers) {
if(layers[layer].milestones !== undefined){
updateMilestoneTemp(layer)
}
}
if (!tmp.buyables) tmp.buyables = {}
for (layer in layers) if (layers[layer].buyables) {
if(layers[layer].buyables !== undefined){
updateBuyableTemp(layer)
}
}
}
function updateChallTemp(layer) {
if (player[layer] === undefined) return
if (!tmp.challs[layer]) tmp.challs[layer] = {}
let data = tmp.challActive[layer]
let data2 = layers[layer].challs
let customActive = data2.active !== undefined
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
tmp.challs[layer][id] = {}
if (customActive ? data2.active(id) : player[layer].active == id) data[id] = 1
else delete data[id]
tmp.challs[layer][id].unl = data2[id].unl()
if(data2[id].name) tmp.challs[layer][id].name = data2[id].name()
if(data2[id].desc) tmp.challs[layer][id].desc = data2[id].desc()
if(data2[id].reward) tmp.challs[layer][id].reward = data2[id].reward()
if(data2[id].effect) tmp.challs[layer][id].effect = data2[id].effect()
if(data2[id].effectDisplay) tmp.challs[layer][id].effectDisplay = data2[id].effectDisplay(tmp.challs[layer][id].effect)
tmp.challs[layer][id].goal = data2[id].goal()
if(data2[id].style) tmp.challs[layer][id].style = data2[id].style()
}
}
}
function updateUpgradeTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.upgrades[layer]) tmp.upgrades[layer] = {}
let data2 = layers[layer].upgrades
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
tmp.upgrades[layer][id] = {}
tmp.upgrades[layer][id].unl = data2[id].unl()
if(data2[id].effect) tmp.upgrades[layer][id].effect = data2[id].effect()
tmp.upgrades[layer][id].cost = data2[id].cost()
if(data2[id].effectDisplay) tmp.upgrades[layer][id].effectDisplay = data2[id].effectDisplay(tmp.upgrades[layer][id].effect)
if(data2[id].desc) tmp.upgrades[layer][id].desc = data2[id].desc()
if(data2[id].title) tmp.upgrades[layer][id].title = data2[id].title()
if(data2[id].style) tmp.upgrades[layer][id].style = data2[id].style()
}
}
}
function updateMilestoneTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.milestones[layer]) tmp.milestones[layer] = {}
let data2 = layers[layer].milestones
for (id in data2) {
tmp.milestones[layer][id] = {}
if(data2[id].unl) tmp.milestones[layer][id].unl = data2[id].unl()
tmp.milestones[layer][id].done = data2[id].done()
if(data2[id].requirementDesc) tmp.milestones[layer][id].requirementDesc = data2[id].requirementDesc()
if(data2[id].effectDesc) tmp.milestones[layer][id].effectDesc = data2[id].effectDesc()
if(data2[id].style) tmp.milestones[layer][id].style = data2[id].style()
}
}
function updateBuyableTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.buyables[layer]) tmp.buyables[layer] = {}
let data2 = layers[layer].buyables
if(data2.respecText) tmp.buyables[layer].respecText = data2.respecText()
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
let amt = player[layer].buyables[id]
tmp.buyables[layer][id] = {}
tmp.buyables[layer][id].unl = data2[id].unl()
if(data2[id].effect) tmp.buyables[layer][id].effect = data2[id].effect(amt)
tmp.buyables[layer][id].cost = data2[id].cost(amt)
tmp.buyables[layer][id].canAfford = data2[id].canAfford()
if(data2[id].title) tmp.buyables[layer][id].title = data2[id].title()
if(data2[id].display) tmp.buyables[layer][id].display = data2[id].display()
if(data2[id].style) tmp.buyables[layer][id].style = data2[id].style()
}
}
}
function updateMicrotabTemp(layer) {
if (layers[layer] === undefined) return
let data2 = layers[layer].microtabs
for (set in data2) {
if (!tmp.microtabs[layer][set]) tmp.microtabs[layer][set] = {}
for (tab in data2[set]) {
if (!tmp.microtabs[layer][set][tab])
tmp.microtabs[layer][set][tab] = {}
if(data2[set][tab].style)
tmp.microtabs[layer][set][tab].style = data2[set][tab].style()
if(data2[set][tab].buttonStyle)
tmp.microtabs[layer][set][tab].buttonStyle = data2[set][tab].buttonStyle()
}
}
}
function setupChallTemp(layer) {
if (player[layer] === undefined) return
if (!tmp.challs[layer]) tmp.challs[layer] = {}
let data = tmp.challActive[layer]
let data2 = layers[layer].challs
let customActive = data2.active !== undefined
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
tmp.challs[layer][id] = {}
if(data2[id].effect) tmp.challs[layer][id].effect = {}
if(data2[id].style) tmp.challs[layer][id].style = {}
}
}
}
function setupUpgradeTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.upgrades[layer]) tmp.upgrades[layer] = {}
let data2 = layers[layer].upgrades
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
tmp.upgrades[layer][id] = {}
if(data2[id].effect) tmp.upgrades[layer][id].effect = {}
if(data2[id].style) tmp.upgrades[layer][id].style = {}
}
}
}
function setupMilestoneTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.milestones[layer]) tmp.milestones[layer] = {}
let data2 = layers[layer].milestones
for (id in data2) {
tmp.milestones[layer][id] = {}
if(data2[id].style) tmp.milestones[layer][id].style = {}
}
}
function setupBuyableTemp(layer) {
if (layers[layer] === undefined) return
if (!tmp.buyables[layer]) tmp.buyables[layer] = {}
let data2 = layers[layer].buyables
for (let row = 1; row <= data2.rows; row++) {
for (let col = 1; col <= data2.cols; col++) {
let id = row * 10 + col
let amt = player[layer].buyables[id]
tmp.buyables[layer][id] = {}
if(data2[id].effect) tmp.buyables[layer][id].effect = {}
tmp.buyables[layer][id].cost = {}
if(data2[id].style) tmp.buyables[layer][id].style = {}
}
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 = {}
}
}

View file

@ -3,7 +3,11 @@
function exponentialFormat(num, precision) {
let e = num.log10().floor()
let m = num.div(Decimal.pow(10, e))
return m.toStringWithDecimalPlaces(3)+"e"+e.toStringWithDecimalPlaces(0)
if(m.toStringWithDecimalPlaces(precision) == 10) {
m = new Decimal(1)
e = e.add(1)
}
return m.toStringWithDecimalPlaces(precision)+"e"+e.toStringWithDecimalPlaces(0)
}
function commaFormat(num, precision) {
@ -12,6 +16,13 @@ function commaFormat(num, precision) {
return num.toStringWithDecimalPlaces(precision).replace(/\B(?=(\d{3})+(?!\d))/g, ",")
}
function regularFormat(num, precision) {
if (num === null || num === undefined) return "NaN"
if (num.mag < 0.001) return (0).toFixed(precision)
return num.toStringWithDecimalPlaces(precision)
}
function fixValue(x, y = 0) {
return x || new Decimal(y)
}
@ -34,13 +45,15 @@ function format(decimal, precision=2) {
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 (Math.floor(decimal.mantissa + 0.01) + ("e"+formatWhole(decimal.log10())))
} 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 commaFormat(decimal, precision)
else return regularFormat(decimal, precision)
}
function formatWhole(decimal) {
decimal = new Decimal(decimal)
if (decimal.gte(1e9)) return format(decimal, 2)
return format(decimal, 0)
}
@ -78,21 +91,29 @@ function startPlayerBase() {
timePlayed: 0,
keepGoing: false,
hasNaN: false,
hideChalls: false,
points: new Decimal(10),
hideChallenges: false,
points: modInfo.initialStartPoints,
subtabs: {},
}
}
function getStartPlayer() {
playerdata = startPlayerBase()
if (addedPlayerData) {
extradata = addedPlayerData()
for (thing in extradata)
playerdata[thing] = extradata[thing]
}
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].challs = []
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]
@ -106,6 +127,37 @@ function getStartPlayer() {
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)
@ -114,6 +166,15 @@ function fixSave() {
{
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]
}
}
}
@ -136,7 +197,7 @@ function fixData(defaultData, newData) {
newData[item] = new Decimal(newData[item])
}
else if ((!!defaultData[item]) && (defaultData[item].constructor === Object)) {
if (newData[item] === undefined)
if (newData[item] === undefined || (defaultData[item].constructor !== Object))
newData[item] = defaultData[item]
else
fixData(defaultData[item], newData[item])
@ -163,6 +224,8 @@ function load() {
versionCheck();
changeTheme();
changeTreeQuality();
updateLayers()
setupTemp();
updateTemp();
updateTemp();
@ -310,11 +373,180 @@ function milestoneShown(layer, id) {
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
if (!tmp[layer].buyables[id].unlocked) return
if (!tmp[layer].buyables[id].canAfford) return
if (!layers[layer].buyables[id].buyMax) return
layers[layer].buyables[id].buyMax()
updateBuyableTemp(layer)
}
function buyBuyable(layer, id) {
if (!player[layer].unlocked) return
if (!tmp[layer].buyables[id].unlocked) return
if (!tmp[layer].buyables[id].canAfford) return
layers[layer].buyables[id].buy()
updateBuyableTemp(layer)
}
function clickClickable(layer, id) {
if (!player[layer].unlocked) return
if (!tmp[layer].clickables[id].unlocked) return
if (!tmp[layer].clickables[id].canClick) return
layers[layer].clickables[id].onClick()
updateClickableTemp(layer)
}
// 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[id].countsAs.includes(id)
}
// ************ Misc ************
var onTreeTab = true
function showTab(name) {
if (LAYERS.includes(name) && !layerUnl(name)) return
if (LAYERS.includes(name) && !layerunlocked(name)) return
var toTreeTab = name == "tree"
player.tab = name
@ -328,22 +560,22 @@ function showTab(name) {
}
function notifyLayer(name) {
if (player.tab == name || !layerUnl(name)) return
if (player.tab == name || !layerunlocked(name)) return
player.notify[name] = 1
}
function nodeShown(layer) {
if (tmp.layerShown[layer]) return true
if (tmp[layer].layerShown) return true
switch(layer) {
case "idk":
return player.l.unl
return player.l.unlocked
break;
}
return false
}
function layerUnl(layer) {
return LAYERS.includes(layer) && (player[layer].unl || (tmp.layerAmt[layer].gte(tmp.layerReqs[layer]) && layers[layer].layerShown()))
function layerunlocked(layer) {
return LAYERS.includes(layer) && (player[layer].unlocked || (tmp[layer].baseAmount.gte(tmp[layer].requires) && tmp[layer].layerShown))
}
function keepGoing() {
@ -364,6 +596,15 @@ function updateMilestones(layer){
}
}
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
@ -396,9 +637,9 @@ document.onkeydown = function(e) {
let key = e.key
if (ctrlDown) key = "ctrl+" + key
if (onFocused) return
if (ctrlDown && key != "-" && key != "_" && key != "+" && key != "=" && key != "r" && key != "R" && key != "F5") e.preventDefault()
if (ctrlDown && hotkeys[key]) e.preventDefault()
if(hotkeys[key]){
if (player[hotkeys[key].layer].unl)
if (player[hotkeys[key].layer].unlocked)
hotkeys[key].onPress()
}
}
@ -408,4 +649,25 @@ function focused(x) {
onFocused = x
}
document.title = modInfo.name
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

217
js/v.js
View file

@ -5,26 +5,26 @@ function loadVue() {
Vue.component('display-text', {
props: ['layer', 'data'],
template: `
<span v-html="readData(data)"></span>
<span class="instant" v-html="data"></span>
`
})
// data = a function returning html content, with some limited functionality
// data = a function returning the content (actually HTML)
Vue.component('raw-html', {
props: ['layer', 'data'],
template: `
<span v-html="readData(data)"></span>
`
})
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>
<div v-if="!data" v-bind:style="{'width': '8px', 'height': '17px'}"></div>
<div v-else-if="Array.isArray(data)" v-bind:style="{'width': data[0], 'height': data[1]}"></div>
<div v-else v-bind:style="{'width': '8px', 'height': 'data'}"><br></div>
<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>
`
})
@ -33,7 +33,7 @@ function loadVue() {
Vue.component('display-image', {
props: ['layer', 'data'],
template: `
<img v-bind:src= "readData(data)" v-bind:alt= "readData(data)">
<img class="instant" v-bind:src= "data" v-bind:alt= "data">
`
})
@ -41,12 +41,12 @@ function loadVue() {
Vue.component('row', {
props: ['layer', 'data'],
template: `
<div class="upgTable">
<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.componentStyles[layer][item]"></div>
<div v-else-if="item.length==3" v-bind:style="[tmp.componentStyles[layer][item], (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.componentStyles[layer][item]"></div>
<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>
@ -57,12 +57,12 @@ function loadVue() {
Vue.component('column', {
props: ['layer', 'data'],
template: `
<div class="upgTable">
<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.componentStyles[layer][item]"></div>
<div v-else-if="item.length==3" v-bind:style="[tmp.componentStyles[layer][item], (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.componentStyles[layer][item]"></div>
<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>
@ -74,7 +74,7 @@ function loadVue() {
Vue.component('h-line', {
props: ['layer', 'data'],
template:`
<hr v-bind:style="data ? {'width': data} : {}" class="hl">
<hr class="instant" v-bind:style="data ? {'width': data} : {}" class="hl">
`
})
@ -82,17 +82,17 @@ function loadVue() {
Vue.component('v-line', {
props: ['layer', 'data'],
template: `
<div v-bind:style="data ? {'height': data} : {}" class="vl2"></div>
<div class="instant" v-bind:style="data ? {'height': data} : {}" class="vl2"></div>
`
})
Vue.component('challs', {
Vue.component('challenges', {
props: ['layer'],
template: `
<div v-if="layers[layer].challs" class="upgTable">
<div v-for="row in layers[layer].challs.rows" class="upgRow">
<div v-for="col in layers[layer].challs.cols">
<chall :layer = "layer" :data = "row*10+col" v-bind:style="tmp.componentStyles[layer].chall"></chall>
<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>
@ -100,16 +100,16 @@ function loadVue() {
})
// data = id
Vue.component('chall', {
Vue.component('challenge', {
props: ['layer', 'data'],
template: `
<div v-if="layers[layer].challs && tmp.challs[layer][data].unl && !(player.hideChalls && hasChall(layer, [data]))" v-bind:class="{hChall: true, done: player[layer].challs.includes(data), canComplete: tmp.challActive[layer][data]&&!player[layer].challs.includes(data)&&canCompleteChall(layer, data)}">
<br><h3 v-html="tmp.challs[layer][data].name"></h3><br><br>
<button v-bind:class="{ longUpg: true, can: true, [layer]: true }" v-bind:style="{'background-color': tmp.layerColor[layer]}" v-on:click="startChall(layer, data)">{{player[layer].active==(data)?(canCompleteChall(layer, data)?"Finish":"Exit Early"):(player[layer].challs.includes(data)?"Completed":"Start")}}</button><br><br>
<span v-html="tmp.challs[layer][data].desc"></span><br>
Goal: {{format(tmp.challs[layer][data].goal)}} {{layers[layer].challs[data].currencyDisplayName ? layers[layer].challs[data].currencyDisplayName : "points"}}<br>
Reward: <span v-html="tmp.challs[layer][data].reward"></span><br>
<span v-if="tmp.challs[layer][data].effect!==undefined">Currently: <span v-html="(tmp.challs[layer][data].effectDisplay) ? (tmp.challs[layer][data].effectDisplay) : format(tmp.challs[layer][data].effect)"></span></span>
<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)}">
<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)">{{player[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>
`
})
@ -117,18 +117,11 @@ function loadVue() {
Vue.component('upgrades', {
props: ['layer'],
template: `
<div v-if="layers[layer].upgrades" class="upgTable">
<<<<<<< Updated upstream
<div v-for="row in layers[layer].upgrades.rows" class="upgRow">
<div v-for="col in layers[layer].upgrades.cols" class="upgAlign">
<upgrade :layer = "layer" :data = "row*10+col" v-bind:style="tmp.componentStyles[layer].upgrade"></upgrade>
</div>
=======
<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>
>>>>>>> Stashed changes
</div>
<br>
</div>
@ -139,21 +132,12 @@ function loadVue() {
Vue.component('upgrade', {
props: ['layer', 'data'],
template: `
<<<<<<< Updated upstream
<button v-if="layers[layer].upgrades && tmp.upgrades[layer][data].unl" v-on:click="buyUpg(layer, data)" v-bind:class="{ [layer]: true, upg: true, bought: player[layer].upgrades.includes(data), locked: (!(canAffordUpg(layer, data))&&!player[layer].upgrades.includes(data)), can: (canAffordUpg(layer, data)&&!player[layer].upgrades.includes(data))}"
v-bind:style="[((!hasUpg(layer, data) && canAffordUpg(layer, data)) ? {'background-color': tmp.layerColor[layer]} : {}), tmp.upgrades[layer][data].style]">
<span v-if= "tmp.upgrades[layer][data].title"><h3 v-html="tmp.upgrades[layer][data].title"></h3><br></span>
<span v-html="tmp.upgrades[layer][data].desc"></span>
<span v-if="tmp.upgrades[layer][data].effect"><br>Currently: <span v-html="(tmp.upgrades[layer][data].effectDisplay) ? (tmp.upgrades[layer][data].effectDisplay) : format(tmp.upgrades[layer][data].effect)"></span></span>
<br><br>Cost: {{ formatWhole(tmp.upgrades[layer][data].cost) }} {{(layers[layer].upgrades[data].currencyDisplayName ? layers[layer].upgrades[data].currencyDisplayName : layers[layer].resource)}}
=======
<button v-if="layers[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))}"
<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)}}
>>>>>>> Stashed changes
</button>
`
})
@ -161,17 +145,11 @@ function loadVue() {
Vue.component('milestones', {
props: ['layer'],
template: `
<div v-if="layers[layer].milestones">
<div v-if="tmp[layer].milestones">
<table>
<<<<<<< Updated upstream
<tr v-for="id in Object.keys(layers[layer].milestones)">
<milestone :layer = "layer" :data = "id" v-bind:style="tmp.componentStyles[layer].milestone"></milestone>
</tr>
=======
<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>
>>>>>>> Stashed changes
</table>
<br>
</div>
@ -182,24 +160,17 @@ function loadVue() {
Vue.component('milestone', {
props: ['layer', 'data'],
template: `
<<<<<<< Updated upstream
<td v-if="layers[layer].milestones && milestoneShown(layer, data)" v-bind:style="[(layers[layer].milestones[data].unl && !tmp.milestones[layer][data].unl) ? {'visibility': 'hidden'} : {}, tmp.milestones[layer][data].style]" v-bind:class="{milestone: !player[layer].milestones.includes(data), milestoneDone: player[layer].milestones.includes(data)}">
<h3 v-html="tmp.milestones[layer][data].requirementDesc"></h3><br>
<span v-html="tmp.milestones[layer][data].effectDesc"></span><br>
<span v-if="(layers[layer].milestones[data].toggles)&&(player[layer].milestones.includes(data))" v-for="toggle in layers[layer].milestones[data].toggles"><toggle :layer= "layer" :data= "toggle" v-bind:style="tmp.componentStyles[layer].toggle"></toggle>&nbsp;</span></td></tr>
=======
<td v-if="layers[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)}">
<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>
>>>>>>> Stashed changes
`
})
Vue.component('toggle', {
props: ['layer', 'data'],
template: `
<button class="smallUpg can" v-bind:style="{'background-color': tmp.layerColor[data[0]]}" v-on:click="toggleAuto(data)">{{player[data[0]][data[1]]?"ON":"OFF"}}</button>
<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>
`
})
@ -207,14 +178,10 @@ function loadVue() {
Vue.component('prestige-button', {
props: ['layer', 'data'],
template: `
<span>
<button v-if="layers[layer].type=='normal'" v-bind:class="{ [layer]: true, reset: true, locked: tmp.layerAmt[layer].lt(tmp.layerReqs[layer]), can: tmp.layerAmt[layer].gte(tmp.layerReqs[layer]) }"
v-bind:style="[tmp.layerAmt[layer].gte(tmp.layerReqs[layer]) ? {'background-color': tmp.layerColor[layer]} : {}, tmp.componentStyles[layer]['prestige-button']]" v-on:click="doReset(layer)"><span v-if="player[layer].points.lt(1e3)">{{data ? data() : "Reset for "}}</span>+<b>{{formatWhole(tmp.resetGain[layer])}}</b> {{layers[layer].resource}}<span v-if="tmp.resetGain[layer].lt(100) && player[layer].points.lt(1e3)"><br><br>Next at {{ (layers[layer].resCeil ? formatWhole(tmp.nextAt[layer]) : format(tmp.nextAt[layer])) }} {{ layers[layer].baseResource }}</span></button>
<button v-if="layers[layer].type=='static'" v-bind:class="{ [layer]: true, reset: true, locked: tmp.layerAmt[layer].lt(tmp.nextAt[layer]), can: tmp.layerAmt[layer].gte(tmp.nextAt[layer]) }"
v-bind:style="[tmp.layerAmt[layer].gte(tmp.nextAt[layer]) ? {'background-color': tmp.layerColor[layer]} : {}, tmp.componentStyles[layer]['prestige-button']]" v-on:click="doReset(layer)"><span v-if="player[layer].points.lt(10)">{{data ? data() : "Reset for "}}</span>+<b>{{formatWhole(tmp.resetGain[layer])}}</b> {{layers[layer].resource}}<br><br><span v-if="player[layer].points.lt(10)">{{(tmp.layerAmt[layer].gte(tmp.nextAt[layer])&&layers[layer].canBuyMax && layers[layer].canBuyMax())?"Next":"Req"}}: {{formatWhole(tmp.layerAmt[layer])}} / </span>{{(layers[layer].resCeil ? formatWhole(tmp.nextAtDisp[layer]) : format(tmp.nextAtDisp[layer]))}} {{ layers[layer].baseResource }}</button>
<button v-if="layers[layer].type=='custom'" v-bind:class="{ [layer]: true, reset: true, locked: !tmp.canReset[layer], can: tmp.canReset[layer] }"
v-bind:style="[tmp.canReset[layer] ? {'background-color': tmp.layerColor[layer]} : {}, tmp.componentStyles[layer]['prestige-button']]" v-on:click="doReset(layer)" v-html="tmp.prestigeButtonText[layer]"></button>
</span>
<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>
`
})
@ -223,7 +190,20 @@ function loadVue() {
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.layerColor[layer], 'text-shadow': '0px 0px 10px' + tmp.layerColor[layer]}">{{formatWhole(player[layer].points)}}</h2> {{layers[layer].resource}}<span v-if="layers[layer].effectDescription">, {{tmp.effectDescription[layer]}}</span><br><br></span>
<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>
`
})
@ -231,40 +211,23 @@ function loadVue() {
Vue.component('buyables', {
props: ['layer', 'data'],
template: `
<div v-if="layers[layer].buyables" class="upgTable">
<<<<<<< Updated upstream
<button v-if="layers[layer].buyables.respec" v-on:click="respecBuyables(layer)" v-bind:class="{ longUpg: true, can: player[layer].unl, locked: !player[layer].unl }">{{layers[layer].buyables.respecText ? tmp.buyables[layer].respecText : "Respec"}}</button><br>
<div v-for="row in layers[layer].buyables.rows" class="upgRow">
<div v-for="col in layers[layer].buyables.cols" class="upgAlign" v-bind:style="{'margin-left': '7px', 'margin-right': '7px', 'height': (data ? data : '200px'),}">
<buyable :layer = "layer" :data = "row*10+col" :size = "data" v-bind:style="tmp.componentStyles[layer].buyable"></buyable>
</div>
=======
<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>
>>>>>>> Stashed changes
<br>
</div>
</div>
`
`
})
// data = id of buyable
Vue.component('buyable', {
props: ['layer', 'data', 'size'],
template: `
<<<<<<< Updated upstream
<button
v-if="layers[layer].buyables && tmp.buyables[layer][data].unl"
v-bind:class="{ buyable: true, can: tmp.buyables[layer][data].canAfford, locked: !tmp.buyables[layer][data].canAfford}"
v-bind:style="[tmp.buyables[layer][data].canAfford ? {'background-color': tmp.layerColor[layer]} : {}, {'height': (size ? size : 'inherit'), 'width': (size ? size : '200px')}, tmp.buyables[layer][data].style]"
v-on:click="buyBuyable(layer, data)">
<span v-if= "layers[layer].buyables[data].title"><h2 v-html="tmp.buyables[layer][data].title"></h2><br></span>
<span v-bind:style="{'white-space': 'pre-line'}" v-html="tmp.buyables[layer][data].display"></span>
=======
<div v-if="layers[layer].buyables && tmp[layer].buyables[data]!== undefined && tmp[layer].buyables[data].unlocked" style="display: grid">
<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)">
@ -281,7 +244,7 @@ function loadVue() {
Vue.component('respec-button', {
props: ['layer', 'data'],
template: `
<button v-if="layers[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>
<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>
`
})
@ -289,7 +252,7 @@ function loadVue() {
Vue.component('clickables', {
props: ['layer', 'data'],
template: `
<div v-if="layers[layer].clickables" class="upgTable">
<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'),}">
@ -306,19 +269,16 @@ function loadVue() {
props: ['layer', 'data', 'size'],
template: `
<button
v-if="layers[layer].clickables && tmp[layer].clickables[data]!== undefined && tmp[layer].clickables[data].unlocked"
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>
>>>>>>> Stashed changes
</button>
`
})
<<<<<<< Updated upstream
=======
Vue.component('master-button', {
props: ['layer', 'data'],
template: `
@ -326,7 +286,6 @@ function loadVue() {
`
})
>>>>>>> Stashed changes
// data = button size, in px
Vue.component('microtabs', {
props: ['layer', 'data'],
@ -334,18 +293,16 @@ function loadVue() {
currentTab() {return player.subtabs[layer][data]}
},
template: `
<div v-if="layers[layer].microtabs" :style="{'border-style': 'solid'}">
<div v-if="tmp[layer].microtabs" :style="{'border-style': 'solid'}">
<div class="upgTable">
<tab-buttons :layer="layer" :data="layers[layer].microtabs[data]" :name="data" v-bind:style="tmp.componentStyles[layer]['tab-buttons']"></tab-buttons>
<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.microtabs[layer][data][player.subtabs[layer][data]].style" :layer="layer" :data="layers[layer].microtabs[data][player.subtabs[layer][data]].content"></column>
<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>
`
})
<<<<<<< Updated upstream
=======
// data = id of the bar
Vue.component('bar', {
props: ['layer', 'data'],
@ -365,7 +322,7 @@ function loadVue() {
Vue.component('achievements', {
props: ['layer'],
template: `
<div v-if="layers[layer].achievements" class="upgTable">
<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>
@ -380,7 +337,7 @@ function loadVue() {
Vue.component('achievement', {
props: ['layer', 'data'],
template: `
<div v-if="layers[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)}"
<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'))
@ -397,31 +354,30 @@ function loadVue() {
Vue.component('sell-one', {
props: ['layer', 'data'],
template: `
<button v-if="layers[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>
<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="layers[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>
<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>
`
})
>>>>>>> Stashed changes
// 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].unl || data[tab].unl()" class="tabButton" v-bind:style="[{'border-color': tmp.layerColor[layer]}, tmp.componentStyles[layer]['tab-button'], tmp.microtabs[layer][name][tab].buttonStyle]" v-on:click="player.subtabs[layer][name] = tab">{{tab}}</button>
<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'],
props: ['layer', 'abb', 'size'],
template: `
<button v-if="nodeShown(layer)"
v-bind:id="layer"
@ -429,20 +385,22 @@ function loadVue() {
showTab(layer)
}"
v-bind:tooltip="
player[layer].unl ? (layers[layer].tooltip ? tmp.tooltips[layer] : formatWhole(player[layer].points) + ' ' + layers[layer].resource)
: (layers[layer].tooltipLocked ? tmp.tooltipsLocked[layer] : 'Reach ' + formatWhole(tmp.layerReqs[layer]) + ' ' + layers[layer].baseResource + ' to unlock (You have ' + formatWhole(tmp.layerAmt[layer]) + ' ' + layers[layer].baseResource + ')')
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: true,
treeNode: size != 'small',
smallNode: size == 'small',
[layer]: true,
hidden: !tmp.layerShown[layer],
locked: !player[layer].unl && !tmp.layerAmt[layer].gte(tmp.layerReqs[layer]),
notify: tmp.notify[layer],
can: layerUnl(layer),
ghost: tmp[layer].layerShown == 'ghost',
hidden: !tmp[layer].layerShown,
locked: !player[layer].unlocked && !tmp[layer].baseAmount.gte(tmp[layer].requires),
notify: tmp[layer].notify,
can: player[layer].unlocked,
}"
v-bind:style="[layerUnl(layer) ? {
'background-color': tmp.layerColor[layer],
} : {}, tmp.nodeStyle[layer]]">
v-bind:style="[layerunlocked(layer) ? {
'background-color': tmp[layer].color,
} : {}, tmp[layer].nodeStyle]">
{{abb}}
</button>
`
@ -460,14 +418,13 @@ function loadVue() {
formatTime,
focused,
getThemeName,
layerUnl,
layerunlocked,
doReset,
buyUpg,
startChall,
startChallenge,
milestoneShown,
keepGoing,
VERSION,
ENDGAME,
LAYERS,
hotkeys
},

156
style.css
View file

@ -49,12 +49,10 @@ td {
}
.upgAlign {
height: 120px;
vertical-align: 0
}
.bigUpgAlign {
height: 200px;
vertical-align: 0
}
@ -67,7 +65,7 @@ h1, h2, h3, b, input {
height: 100px;
width: 100px;
border: var(--hqProperty1);
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
border-radius: 50%;
box-shadow: var(--hqProperty2a), var(--hqProperty2b);
font-size: 40px;
@ -76,6 +74,20 @@ h1, h2, h3, b, input {
text-shadow: var(--hqProperty3);
}
.smallNode {
height: 60px;
width: 60px;
border: var(--hqProperty1);
border-color: rgba(0, 0, 0, 0.125);
border-radius: 50%;
box-shadow: var(--hqProperty2a), var(--hqProperty2b);
font-size: 30px;
font-family: "Lucida Console", "Courier New", monospace;
color: rgba(0, 0, 0, 0.5);
text-shadow: var(--hqProperty3);
}
.locked {
background-color: #bf8f8f;
cursor: not-allowed;
@ -92,7 +104,7 @@ h1, h2, h3, b, input {
.treeNode.notify {
transform: scale(1.1, 1.1);
border-color: rgba(255, 0, 0, 0.125) rgba(255, 0, 0, 0.25) rgba(255, 0, 0, 0.25) rgba(255, 0, 0, 0.125);
border-color: rgba(0, 0, 0, 0.125);
box-shadow: var(--hqProperty2a), 0px 0px 20px #ff0000;
z-index: 3
}
@ -124,9 +136,45 @@ h1, h2, h3, b, input {
text-shadow: 0px 0px 7px var(--color);
}
.barBase {
overflow: hidden;
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
display:table
}
.barBorder {
border: 2px solid;
border-radius: 10%;
border-color: var(--color);
overflow: hidden;
-webkit-mask-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAA5JREFUeNpiYGBgAAgwAAAEAAGbA+oJAAAAAElFTkSuQmCC);
margin:0
}
.overlayTextContainer {
z-index: 3;
border-radius: 10%;
vertical-align: middle;
display: flex;
justify-content: center;
align-items: left;
position: absolute;
}
.fill {
background-color: var(--color);
z-index:2;
position: absolute;
overflow: hidden;
margin-left: -0.5px;
}
.overlayText {
z-index: 6;
}
.tabButton {
background-color: transparent;
border: none;
color: var(--color);
font-size: 20px;
cursor: pointer;
@ -134,7 +182,7 @@ h1, h2, h3, b, input {
margin: 5px 5px 5px 5px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
}
@ -148,7 +196,7 @@ h1, h2, h3, b, input {
width: 180px;
border-radius: 25%;
border: 4px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
}
.upg {
@ -156,16 +204,31 @@ h1, h2, h3, b, input {
width: 120px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
font-size: 10px;
}
.achievement {
height: 90px;
width: 90px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(0, 0, 0, 0.125);
font-size: 10px;
color: white;
text-shadow: 0px 0px 2px #000000;
}
.achievement:hover {
box-shadow: 0px 0px 10px var(--points);
z-index: 7;
}
.buyable {
height: 200px;
width: 200px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
font-size: 10px;
}
@ -174,7 +237,7 @@ h1, h2, h3, b, input {
width: 200px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
}
.longUpg {
@ -183,7 +246,7 @@ h1, h2, h3, b, input {
background: var(--points);
border-radius: 50%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
font-size: 10px;
}
@ -192,7 +255,7 @@ h1, h2, h3, b, input {
width: 40px;
border-radius: 25%;
border: 2px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
}
#points {
@ -238,11 +301,24 @@ h1, h2, h3, b, input {
left: 4px;
width: 40px;
height: 40px;
cursor: pointer
}
#discord:hover {
transform: scale(1.2, 1.2);
#discord img {
width: 100%;
height: 100%;
}
#discord-links {
position: absolute;
top: 0;
padding-top: 44px;
left: -244px;
width: 200px;
transition: left .3s ease;
}
#discord:hover #discord-links {
left: -4px;
}
#version {
@ -256,7 +332,7 @@ h1, h2, h3, b, input {
}
#version:hover {
transform: scale(1.2, 1.2);
transform: scale(1.1, 1.1);
right: 4.8px;
}
@ -272,8 +348,6 @@ a {
color: #41f5f5;
text-decoration-line: none;
cursor: pointer;
width: 100%;
min-width: 100%;
font-family: "Lucida Console", "Courier New", monospace;
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: #02f2f2;
@ -319,7 +393,7 @@ a {
background-color: #bf8f8f;
border: 4px solid;
border-radius: 4px;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
color: rgba(0, 0, 0, 0.5);
}
@ -329,7 +403,7 @@ a {
background-color: #77bf5f;
border: 4px solid;
border-radius: 4px;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
color: rgba(0, 0, 0, 0.5);
}
@ -361,6 +435,14 @@ a {
transition-duration: 0s
}
.instant {
transition-duration: 0s !important
}
.fast {
transition:color none
}
.col.right {
top: 0;
right: 0;
@ -394,10 +476,10 @@ ul {
list-style-type: none;
}
.hChall {
.hChallenge {
background-color: #bf8f8f;
border: 4px solid;
border-color: rgba(255, 255, 255, 0.125) rgba(0, 0, 0, 0.25) rgba(0, 0, 0, 0.25) rgba(255, 255, 255, 0.125);
border-color: rgba(0, 0, 0, 0.125);
color: rgba(0, 0, 0, 0.5);
width: 300px;
height: 300px;
@ -405,11 +487,11 @@ ul {
border-radius: 33.33%;
}
.hChall.done {
.hChallenge.done {
background-color: #77bf5f;
}
.hChall.canComplete {
.hChallenge.canComplete {
background-color: #ffbf00;
}
@ -488,4 +570,30 @@ ul {
display: flex;
flex-direction: column;
justify-content: center;
}
.treeOverlay {
z-index: 200000;
pointer-events:none;
overflow:hidden;
}
.overlayThing {
pointer-events:auto;
background-color: var(--background);
}
.sideLayers {
pointer-events:auto;
position: absolute;
right: 55px;
top: 65px;
}
button > * {
pointer-events:none;
}
.ghost {
visibility: hidden
}