1
0
Fork 0
mirror of https://github.com/thepaperpilot/The-Modding-Tree.git synced 2025-03-14 10:01:47 +00:00

Merge remote-tracking branch 'upstream/master' into kronos

This commit is contained in:
thepaperpilot 2021-05-11 23:22:34 -05:00
commit fd249f5b4a
20 changed files with 679 additions and 179 deletions

View file

@ -1,9 +1,44 @@
# The Modding Tree changelog:
# v2.5.3 - 5/8/21
- Improved performance of tab formats and bars.
- Respec confirmation settings are now kept on resets.
- Improved compatibility with older browsers.
- Fixed missing pixel on vertical bars.
# v2.5.2.1 - 5/7/21
- Fixed microtabs making layers highlight incorrectly.
# v2.5.2 - 5/7/21
- Added glowColor for subtabs.
- Improved the display for extremely small numbers.
- Fixed issues in the buyable docs.
# v2.5.1 - 5/7/21
- Fixed dynamic things in tabFormat not updating.
## v2.5: Dreams Really Do Come True - 5/7/21
- Optimizations, hopefully a significant amount.
- Added OOM/s point gen display at high values (thanks to Ducdat!)
- Only one tab will display if the window is not wide enough (also thanks to Ducdat!)
- Holding down a buyable's button now buys it continuously.
- New milestone setting will also show the most recently unlocked milestone. (Also renamed all settings to be clearer)
- Added an onHold feature for clickables.
- Layer nodes will be highlighted even if the player is on the same tab.
- Added customizable node glowColor.
- Added buyable purchaseLimit.
- Amount is automatically supplied to buyable cost and effect functions.
- Locked (not yet visible) milestones no longer take up space. Also fixed hidden milestones taking a tiny bit of space.
- Re-centered respec buttons.
- Force-displayed tooltips are not hidden by resets.
- Added formatting support for very small numbers. Disabled in most places by default because rounding errors might cause issues. Access it with formatSmall, or enable it globally by adding "allowSmall: true" to modInfo.
# v2.4.1 - 4/29/21
- A number of minor fixes, many thanks to thepaperpilot.
- The respec confirmation checkbox is now part of the respec-button component.
- Added a few missing changes to the 2.4 changelog (the two at the bottom)
(This also fixes the checkbox appearing when there is no respec button)
- Added a few undocumented changes to the 2.4 changelog (the two at the bottom)
## v2.4: Rationalized Edition - 4/29/21
- Completely reworked tooltips. Shift-click a node to force its tooltip to stay displayed. (And hopefully finally fixed flickering!)

122
demo.html Normal file
View file

@ -0,0 +1,122 @@
<!DOCTYPE html>
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
<link rel="stylesheet" type="text/css" href="popup.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 src="js/technical/break_eternity.js"></script>
<script src="js/technical/layerSupport.js"></script>
<script src="js/demo/demoTree.js"></script>
<script src="js/demo/demoLayers.js"></script>
<script src="js/demo/demoMod.js"></script>
<script src="js/technical/temp.js"></script>
<script src="js/technical/displays.js"></script>
<script src="js/game.js"></script>
<script src="js/utils.js"></script>
<script src="js/technical/systemComponents.js"></script>
<script src="js/components.js"></script>
<script src="js/technical/canvas.js"></script>
<script src="js/utils/NumberFormating.js"></script>
<script src="js/utils/options.js"></script>
<script src="js/utils/save.js"></script>
<script src="js/utils/themes.js"></script>
</head>
<body onload="load()">
<div id="app">
<canvas id="treeCanvas" class="canvas" v-if="!(gameEnded && !player.keepGoing)"></canvas>
<div v-if="false" id="loadingSection" class="fullWidth">
<h1>Loading... (If this takes too long it means there was a serious error!)←</h1>
</div>
<div class="vl" v-if="player.navTab !== 'none' && tmp.other.splitScreen && player.tab!='none' && !(gameEnded && !player.keepGoing)"></div>
<div v-if="(gameEnded && !player.keepGoing)" class="fullWidth">
<br>
<h2>{{modInfo.name}} {{VERSION.withoutName}}</h2><br><br>
<h3 v-html="modInfo.winText"></h3><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>
<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>
<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>
</div>
<div id="treeOverlay" v-if="!(gameEnded && !player.keepGoing) && (player.tab === 'none' || tmp.other.splitScreen || !readData(layoutInfo.showTree))" class="treeOverlay" onscroll="resizeCanvas()"
v-bind:class="{
fullWidth: (player.tab == 'none' || player.navTab == 'none'),
col: (player.tab !== 'none' && player.navTab !== 'none'),
left: (player.tab !== 'none' && player.navTab !== 'none')}"
:style="{'margin-top': !readData(layoutInfo.showTree) && player.tab == 'info-tab' ? '50px' : ''}">
<div id="version" onclick="showTab('changelog-tab')" class="overlayThing" style="margin-right: 13px">
{{VERSION.withoutName}}</div>
<button
v-if="player.navTab == 'none' && (tmp[player.tab].row == 'side' || tmp[player.tab].row == 'otherside')"
class="other-back overlayThing" onclick="goBack()">←</button>
<img id="optionWheel" class="overlayThing" v-if="player.tab!='options-tab'" src="options_wheel.png"
onclick="showTab('options-tab')"></img>
<div id="info" v-if="player.tab!='info-tab'" class="overlayThing" onclick="showTab('info-tab')"><br>i</div>
<div id="discord" class="overlayThing">
<img onclick="window.open((modInfo.discordLink ? modInfo.discordLink : 'https://discord.gg/F3xveHV'),'mywindow')"
src="discord.png" target="_blank"></img>
<ul id="discord-links">
<li v-if="modInfo.discordLink"><a class="link" v-bind:href="modInfo.discordLink"
target="_blank">{{modInfo.discordName}}</a><br></li>
<li><a class="link" href="https://discord.gg/F3xveHV" target="_blank"
v-bind:style="modInfo.discordLink ? {'font-size': '16px'} : {}">The Modding Tree
Discord</a><br></li>
<li><a class="link" href="http://discord.gg/wwQfgPa" target="_blank"
v-bind:style="{'font-size': '16px'}">Main Prestige Tree server</a></li>
</ul>
</div>
<overlay-head v-if="!(gameEnded && !player.keepGoing)"></overlay-head>
<div class="sideLayers">
<div v-for="(node, index) in OTHER_LAYERS['side']">
<tree-node :layer='node' :abb='tmp[node].symbol' :size="'small'" :key="'side' + index"></tree-node>
</div>
</div>
</div>
<div v-if="!(gameEnded && !player.keepGoing) && (player.tab === 'none' || tmp.other.splitScreen)" id="treeTab" style="z-index: 0" onscroll="resizeCanvas()"
v-bind:class="{ fullWidth: (player.tab == 'none' || player.navTab == 'none'), col: (player.tab !== 'none' && player.navTab !== 'none'), left: (player.tab !== 'none' && player.navTab !== 'none')}">
<br><br><br><br>
<overlay-head id="fakeHead" style="visibility: hidden;">
</overlay-head>
<layer-tab :layer="player.navTab == 'none' ? player.tab : player.navTab" :key="'left'"></layer-tab>
</div>
<!-- Popups -->
<div class="popup-container">
<transition-group name="fade">
<div v-for="popup,index in activePopups" class="popup" v-bind:class="popup.type"
v-bind:key="'p' + popup.id" v-on:click="() => {activePopups.splice(index, 1)}" v-bind:style="popup.color ? {'background-color': popup.color} : {}">
<h3>{{popup.title}}</h3><br>
<h2 v-html="popup.message"></h2>
</div>
</transition-group>
</div>
<div v-if="player.navTab !== 'none' && player.tab !== 'none' && !(gameEnded && !player.keepGoing)" onscroll="resizeCanvas()" style="background-color:var(--background)" v-bind:class="{ fullWidth: player.navTab == 'none' || !tmp.other.splitScreen || !readData(layoutInfo.showTree), col: player.navTab != 'none', right: player.navTab != 'none', fast: true, tab: true}">
<div v-for="layer in LAYERS">
<div v-if="player.tab==layer">
<layer-tab :layer="layer" :back="'none'" :spacing="'50px'" :key="'left'"></layer-tab>
</div>
</div>
</div>
</div>
</body>

View file

@ -15,12 +15,12 @@ Buyables should be formatted like this:
```js
buyables: {
11: {
cost(x) { return new Decimal(1).mul(x || getBuyableAmt(this.layer, this.id)) },
cost(x) { return new Decimal(1).mul(x) },
display() { return "Blah" },
canAfford() { return player[this.layer].points.gte(this.cost()) },
buy() {
player[this.layer].points = player[this.layer].points.sub(this.cost())
setBuyableAmount(this.layer, this.id, getBuyableAmt(this.layer, this.id).add(1))
setBuyableAmount(this.layer, this.id, getBuyableAmount(this.layer, this.id).add(1))
},
etc
},
@ -32,9 +32,11 @@ Features:
- title: **optional**. displayed at the top in a larger font. It can also be a function that returns updating text.
- 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.
- cost(): cost for buying the next buyable. Can have an optional argument "x" to calculate the cost of the x+1th purchase. (x is a `Decimal`).
Can return an object if there are multiple currencies.
- 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.
- effect(): **optional**. A function that calculates and returns the current values of bonuses of this buyable. Can have an optional argument "x" to calculate the effect of having x of the buyable..
Can return a value or an object containing multiple values.
- 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.
@ -47,7 +49,9 @@ Features:
- buyMax(): **optional**. A function that implements buying as many of the buyable as possible.
- 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).
- purchaseLimit: **optional**. The limit on how many of the buyable can be bought. The default is no limit.
- 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 buyable was stored under, for convenient access. The buyable in the example's id is 11.
@ -64,7 +68,7 @@ Including a `sellOne` or `sellAll` function will cause an additional button to a
To add a respec button, or something similar, add the respecBuyables function to the main buyables object (not individual buyables).
You can use these features along with it:
- respecBuyables(): **optional**. This is called when the button is pressed (after a toggleable confirmation message).
- respec(): **optional**. This is called when the button is pressed (after a toggleable confirmation message).
- respecText: **optional**. Text to display on the respec Button.

View file

@ -38,6 +38,8 @@ Features:
- onClick(): A function that implements clicking one of the clickable.
- onHold(): **optional** A function that is called 20x/sec when the button is held for at least 0.25 seconds.
- 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.

View file

@ -155,6 +155,8 @@ You can make almost any value dynamic by using a function in its place, includin
- 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.
- glowColor: **optional**. The color that this layer will be highlighted if it should notify. The default is red. You can use this if you want several different notification types!
- componentStyles: **optional**. An object that contains a set of functions returning CSS objects. Each of these will be applied to any components on the layer with the type of its id. Example:
```js

View file

@ -52,5 +52,8 @@ Normal subtabs and microtab subtabs both use the same features:
- shouldNotify()/prestigeNotify(): **optional**, if true, the tab button will be highlighted to notify the player that there is something there.
- glowColor: **optional**, specifies the color that the subtab glows. If this subtab is causing the main layer to node glow
(and it would't otherwise) the node also glows this color. Is NOT overridden by embedding a layer.
- embedLayer: **SIGNIFICANT**, the id of another layer. If you have this, it will override "content", "style" and "shouldNotify",
instead displaying the entire layer in the subtab.

View file

@ -21,6 +21,7 @@
<script type="text/javascript" src="js/tree.js"></script>
<script type="text/javascript" src="js/mod.js"></script>
<script type="text/javascript" src="js/technical/temp.js"></script>
<script type="text/javascript" src="js/technical/displays.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/technical/systemComponents.js"></script>
@ -67,7 +68,7 @@
<div v-if="false" id="loadingSection" class="fullWidth">
<h1>Loading...<br>(If this takes too long it means there was a serious error!)</h1>
</div>
<div class="vl" v-if="player.navTab!== 'none' &&player.tab!=='none'&&!(gameEnded && !player.keepGoing)"></div>
<div class="vl" v-if="player.navTab !== 'none' && tmp.other.splitScreen && player.tab!='none' && !(gameEnded && !player.keepGoing)"></div>
<div v-if="(gameEnded && !player.keepGoing)" class="fullWidth">
<br>
<h2>{{modInfo.name}} {{VERSION.withoutName}}</h2><br><br>
@ -87,8 +88,15 @@
<br><br>
</div>
<div id="treeOverlay" v-if="!(gameEnded && !player.keepGoing)" class="treeOverlay" onscroll="resizeCanvas()"
v-bind:class="{ fullWidth: (player.tab === 'none' || player.navTab === 'none'), col: (player.tab !== 'none' && player.navTab !== 'none'), left: (player.tab !== 'none' && player.navTab !== 'none')}">
<div id="treeOverlay" v-if="!(gameEnded && !player.keepGoing) && (player.tab === 'none' || tmp.other.splitScreen || !readData(layoutInfo.showTree))" class="treeOverlay" onscroll="resizeCanvas()"
v-bind:class="{
fullWidth: (player.tab === 'none' || player.navTab === 'none'),
col: (player.tab !== 'none' && player.navTab !== 'none'),
left: (player.tab !== 'none' && player.navTab !== 'none')
}"
:style="{
'margin-top': !readData(layoutInfo.showTree) && player.tab == 'info-tab' ? '50px' : ''
}">
<button v-if= "player.navTab === 'none' && (tmp[player.tab].row === 'side' || tmp[player.tab].row === 'otherside')" class="other-back overlayThing" onclick="goBack()"></button>
<overlay-head v-if="!(gameEnded && !player.keepGoing)"></overlay-head>
<div class="sideLayers">
@ -98,7 +106,7 @@
</div>
</div>
<perfect-scrollbar v-if="!(gameEnded && !player.keepGoing)" style="height: calc(100% - 70px)" id="treeTab" v-bind:style="{'z-index': (tmp.scrolled ? '1' : '5000')}" onscroll="resizeCanvas()"
<perfect-scrollbar v-if="!(gameEnded && !player.keepGoing) && (player.tab === 'none' || tmp.other.splitScreen)" style="height: calc(100% - 70px); z-index: 0;" id="treeTab" onscroll="resizeCanvas()"
v-bind:class="{ fullWidth: (player.tab === 'none' || player.navTab === 'none'), left: (player.tab !== 'none' && player.navTab !== 'none')}">
<br>
<overlay-head id="fakeHead" style="visibility: hidden;">
@ -119,7 +127,7 @@
</transition-group>
</div>
<perfect-scrollbar v-if="player.navTab !== 'none' && player.tab !== 'none' && !(gameEnded && !player.keepGoing)" onscroll="resizeCanvas()" v-bind:class="{ fullWidth: player.navTab === 'none', right: player.navTab !== 'none', fast: true, tab: true}">
<perfect-scrollbar v-if="player.navTab !== 'none' && player.tab !== 'none' && !(gameEnded && !player.keepGoing)" onscroll="resizeCanvas()" style="background-color:var(--background)" v-bind:class="{ fullWidth: player.navTab === 'none' || !tmp.other.splitScreen || !readData(layoutInfo.showTree), right: player.navTab !== 'none', fast: true, tab: true}">
<div v-for="layer in LAYERS" >
<div v-if="player.tab===layer" >
<layer-tab :layer="layer" :back="'none'" :spacing="'50px'" :key="'left'"></layer-tab>
@ -127,4 +135,4 @@
</div>
</perfect-scrollbar>
</div>
</body>
</body>

View file

@ -158,12 +158,12 @@ addLayer("c", {
respecMessage: "Are you sure? Respeccing these doesn't accomplish much.",
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
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=player[this.layer].buyables[this.id]) { // Effects of owning x of the items, x is a decimal
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))
@ -175,7 +175,7 @@ addLayer("c", {
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\
Amount: " + player[this.layer].buyables[this.id] + "/4\n\
Adds + " + format(data.effect.first) + " things and multiplies stuff by " + format(data.effect.second)
},
unlocked() { return player[this.layer].unlocked },
@ -189,6 +189,7 @@ addLayer("c", {
},
buyMax() {}, // You'll have to handle this yourself if you want
style: {'height':'222px'},
purchaseLimit: new Decimal(4),
sellOne() {
let amount = getBuyableAmount(this.layer, this.id)
if (amount.lte(0)) return // Only sell one if there is at least one
@ -220,6 +221,8 @@ addLayer("c", {
content: ["upgrades", ["display-text", function() {return "confirmed"}]]
},
second: {
embedLayer: "f",
content: [["upgrade", 11],
["row", [["upgrade", 11], "blank", "blank", ["upgrade", 11],]],
@ -301,6 +304,8 @@ addLayer("c", {
function() {return 'I have ' + format(player.points) + ' ' + player.c.thingy + ' points!'},
{"color": "red", "font-size": "32px", "font-family": "Comic Sans MS"}],
"h-line", "milestones", "blank", "upgrades", "challenges"],
glowColor: "blue",
},
thingies: {
prestigeNotify: true,
@ -354,13 +359,14 @@ addLayer("c", {
'color': '#3325CC',
'text-decoration': 'underline'
}},
glowColor: "orange", // If the node is highlighted, it will be this color (default is red)
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 += "<br>" + formatWhole(player[this.layer].buyables[11]) + " Exhancers"
if (player[this.layer].buyables[11].gt(0)) tooltip += "<br><i>" + formatWhole(player[this.layer].buyables[11]) + " Exhancers</i>"
return tooltip
},
shouldNotify() { // Optional, layer will be highlighted on the tree if true.
@ -405,7 +411,7 @@ addLayer("f", {
// 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 points and lollipops! (At least " + formatWhole(tmp[this.layer].nextAt) + " points)"
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 points and lollipops! (You'll get another one at " + formatWhole(tmp[layer].nextAtDisp) + " points)"
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 points and lollipops! (You'll get another one at " + formatWhole(tmp[this.layer].nextAtDisp) + " points)"
},
getResetGain() {
return getResetGain(this.layer, useType = "static")
@ -450,9 +456,11 @@ addLayer("f", {
default:
player[this.layer].clickables[this.id] = "Start"
break;
}
},
onHold(){
console.log("Clickkkkk...")
},
style() {
switch(getClickableState(this.layer, this.id)){
case "Start":

View file

@ -11,8 +11,8 @@ let modInfo = {
// Set your version in num and name
let VERSION = {
num: "2.4.1",
name: "Rationalized Edition",
num: "2.5.3",
name: "Dreams Really Do Come True",
}
let changelog = `<h1>Changelog:</h1><br>

View file

@ -144,10 +144,11 @@ function loadVue() {
Vue.component('challenge', {
props: ['layer', 'data'],
template: `
<div v-if="tmp[layer].challenges && tmp[layer].challenges[data]!== undefined && tmp[layer].challenges[data].unlocked && !(player.hideChallenges && maxedChallenge(layer, [data]))" v-bind:class="{hChallenge: true, done: tmp[layer].challenges[data].defaultStyle === 'done', canComplete:tmp[layer].challenges[data].defaultStyle === 'canComplete', locked: tmp[layer].challenges[data].defaultStyle === 'locked'}" v-bind:style="tmp[layer].challenges[data].style">
<div v-if="tmp[layer].challenges && tmp[layer].challenges[data]!== undefined && tmp[layer].challenges[data].unlocked && !(player.hideChallenges && maxedChallenge(layer, [data]))"
v-bind:class="['hChallenge', challengeStyle(layer, data)]" v-bind:style="tmp[layer].challenges[data].style">
<br><h3 v-html="tmp[layer].challenges[data].name"></h3><br><br>
<button v-bind:class="{ longUpg: true, can: true, [layer]: true }" v-bind:style="{'background-color': tmp[layer].color}" v-on:click="startChallenge(layer, data)">{{tmp[layer].challenges[data].buttonText}}</button><br><br>
<span v-if="tmp[layer].challenges[data].fullDisplay" v-html="tmp[layer].challenges[data].fullDisplay"></span>
<button v-bind:class="{ longUpg: true, can: true, [layer]: true }" v-bind:style="{'background-color': tmp[layer].color}" v-on:click="startChallenge(layer, data)">{{challengeButtonText(layer, data)}}</button><br><br>
<span v-if="layers[layer].challenges[data].fullDisplay" v-html="run(layers[layer].challenges[data].fullDisplay, layers[layer].challenges[data])"></span>
<span v-else>
<span v-html="tmp[layer].challenges[data].challengeDescription"></span><br>
Goal: <span v-if="tmp[layer].challenges[data].goalDescription"
@ -155,7 +156,7 @@ function loadVue() {
v-else>{{ format(tmp[layer].challenges[data].goal) }}
{{ tmp[layer].challenges[data].currencyDisplayName ? tmp[layer].challenges[data].currencyDisplayName : "points" }}</span><br>
Reward: <span v-html="tmp[layer].challenges[data].rewardDescription"></span><br>
<span v-if="tmp[layer].challenges[data].rewardDisplay!==undefined">Currently: <span v-html="(tmp[layer].challenges[data].rewardDisplay) ? (tmp[layer].challenges[data].rewardDisplay) : format(tmp[layer].challenges[data].rewardEffect)"></span></span>
<span v-if="layers[layer].challenges[data].rewardDisplay!==undefined">Currently: <span v-html="(tmp[layer].challenges[data].rewardDisplay) ? (run(layers[layer].challenges[data].rewardDisplay, layers[layer].challenges[data])) : format(tmp[layer].challenges[data].rewardEffect)"></span></span>
</span>
</div>
`
@ -187,7 +188,7 @@ function loadVue() {
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].fullDisplay" v-html="tmp[layer].upgrades[data].fullDisplay"></span>
<span v-if="layers[layer].upgrades[data].fullDisplay" v-html="run(layers[layer].upgrades[data].fullDisplay, layers[layer].upgrades[data])"></span>
<span v-else>
<span v-if="tmp[layer].upgrades[data].title">
<!--suppress HtmlUnknownTag -->
@ -238,12 +239,12 @@ function loadVue() {
Vue.component("milestone", {
props: ["layer", "data"],
template: `
<td v-if="tmp[layer].milestones && tmp[layer].milestones[data]!== undefined && milestoneShown(layer, data)"
v-bind:style="[(!tmp[layer].milestones[data].unlocked) ? {'visibility': 'hidden'} : {}, tmp[layer].milestones[data].style]"
<td v-if="tmp[layer].milestones && tmp[layer].milestones[data]!== undefined && milestoneShown(layer, data) && tmp[layer].milestones[data].unlocked"
v-bind:style="[tmp[layer].milestones[data].style]"
v-bind:class="{milestone: !hasMilestone(layer, data), milestoneDone: hasMilestone(layer, data)}">
<h3 v-html="tmp[layer].milestones[data].title"></h3><br>
<b v-html="tmp[layer].milestones[data].requirementDescription"></b><br>
<span v-html="tmp[layer].milestones[data].effectDescription"></span><br>
<span v-html="run(layers[layer].milestones[data].effectDescription, layers[layer].milestones[data])"></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"/>&nbsp;</span>
@ -267,7 +268,7 @@ function loadVue() {
<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)">
v-html="prestigeButtonText(layer)" v-on:click="doReset(layer)">
</button>
`
@ -287,7 +288,7 @@ function loadVue() {
<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">, <span v-html="tmp[layer].effectDescription"></span></span>
v-if="layers[layer].effectDescription">, <span v-html="run(layers[layer].effectDescription, layers[layer])"></span></span>
</div>
`
});
@ -327,17 +328,33 @@ function loadVue() {
props: ['layer', 'data', 'size'],
template: `
<div v-if="tmp[layer].buyables && tmp[layer].buyables[data]!== undefined && tmp[layer].buyables[data].unlocked" style="display: grid">
<button v-bind:class="{ buyable: true, can: tmp[layer].buyables[data].canAfford, locked: !tmp[layer].buyables[data].canAfford}"
v-bind:style="[tmp[layer].buyables[data].canAfford ? {'background-color': tmp[layer].buyables[data].color || tmp[layer].color} : {}, size ? {'height': size, 'width': size} : {}, tmp[layer].componentStyles.buyable, tmp[layer].buyables[data].style]"
v-on:click="buyBuyable(layer, data)">
<button v-bind:class="{ buyable: true, can: tmp[layer].buyables[data].canBuy, locked: !tmp[layer].buyables[data].canAfford, bought: player[layer].buyables[data].gte(tmp[layer].buyables[data].purchaseLimit)}"
v-bind:style="[tmp[layer].buyables[data].canBuy ? {'background-color': tmp[layer].buyables[data].color || tmp[layer].color} : {}, size ? {'height': size, 'width': size} : {}, tmp[layer].componentStyles.buyable, tmp[layer].buyables[data].style]"
v-on:click="buyBuyable(layer, data)" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start" @touchend="stop" @touchcancel="stop">
<span v-if= "tmp[layer].buyables[data].title"><h2 v-html="tmp[layer].buyables[data].title"></h2><br></span>
<span v-bind:style="{'white-space': 'pre-line'}" v-html="tmp[layer].buyables[data].display"></span>
<span v-bind:style="{'white-space': 'pre-line'}" v-html="run(layers[layer].buyables[data].display, layers[layer].buyables[data])"></span>
</button>
<br v-if="(tmp[layer].buyables[data].sellOne !== undefined && !(tmp[layer].buyables[data].canSellOne !== undefined && tmp[layer].buyables[data].canSellOne == false)) || (tmp[layer].buyables[data].sellAll && !(tmp[layer].buyables[data].canSellAll !== undefined && tmp[layer].buyables[data].canSellAll == false))">
<sell-one :layer="layer" :data="data" v-bind:style="tmp[layer].componentStyles['sell-one']" v-if="(tmp[layer].buyables[data].sellOne)&& !(tmp[layer].buyables[data].canSellOne !== undefined && tmp[layer].buyables[data].canSellOne == false)"></sell-one>
<sell-all :layer="layer" :data="data" v-bind:style="tmp[layer].componentStyles['sell-all']" v-if="(tmp[layer].buyables[data].sellAll)&& !(tmp[layer].buyables[data].canSellAll !== undefined && tmp[layer].buyables[data].canSellAll == false)"></sell-all>
</div>
`
`,
data() { return { interval: false, time: 0,}},
methods: {
start() {
if (!this.interval) {
this.interval = setInterval((function() {
if(this.time >= 5)
buyBuyable(this.layer, this.data)
this.time = this.time+1
}).bind(this), 50)}
},
stop() {
clearInterval(this.interval)
this.interval = false
this.time = 0
}
},
})
Vue.component("respec-button", {
@ -345,7 +362,7 @@ function loadVue() {
template: `
<div>
<div class="tooltipBox"><input type="checkbox" v-model="player[layer].noRespecConfirm" ><tooltip v-bind:text="'Disable respec confirmation'"></tooltip></div>
<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>
<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 }" style="margin-right: 18px">{{tmp[layer].buyables.respecText ? tmp[layer].buyables.respecText : "Respec"}}</button>
</div>
`
})
@ -446,20 +463,23 @@ function loadVue() {
// data = id of the bar
Vue.component("bar", {
props: ["layer", "data"],
computed: {
style() {return constructBarStyle(this.layer, this.data)}
},
template: `
<div v-if="tmp[layer].bars && tmp[layer].bars[data].unlocked !== false"
v-bind:style="{'position': 'relative'}">
<div
v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].dims, {'display': 'table', 'borderRadius': '10px', 'boxShadow': '0 0 10px 2px var(--shadowColor), inset 0 0 10px 4px var(--innerShadowColor)'}]">
<div class="overlayTextContainer barBorder"
v-bind:style="[tmp[layer].bars[data].borderStyle, tmp[layer].bars[data].dims]">
v-bind:style="[tmp[layer].bars[data].borderStyle, style.dims]">
<span class="overlayText" v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].textStyle]"
v-html="tmp[layer].bars[data].display"></span>
v-html="run(layers[layer].bars[data].display, layers[layer].bars[data])"></span>
</div>
<div class="barBG barBorder"
v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].baseStyle, tmp[layer].bars[data].borderStyle, tmp[layer].bars[data].dims]">
<div class="fill"
v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].fillStyle, tmp[layer].bars[data].fillDims]"></div>
v-bind:style="[tmp[layer].bars[data].style, tmp[layer].bars[data].fillStyle, style.fillDims]"></div>
</div>
</div>
</div>
@ -491,7 +511,7 @@ function loadVue() {
props: ["layer", "data"],
template: `
<div v-if="tmp[layer].achievements && tmp[layer].achievements[data]!== undefined && tmp[layer].achievements[data].unlocked" v-bind:class="{ [layer]: true, achievement: true, tooltipBox:true, locked: !hasAchievement(layer, data), bought: hasAchievement(layer, data)}"
v-bind:style="tmp[layer].achievements[data].computedStyle">
v-bind:style="achievementStyle(layer, data)">
<tooltip :text="
(tmp[layer].achievements[data].tooltip == '') ? false : 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'))
@ -614,9 +634,13 @@ function loadVue() {
maxedChallenge,
inChallenge,
canAffordUpgrade,
canBuyBuyable,
canCompleteChallenge,
subtabShouldNotify,
subtabResetNotify,
challengeStyle,
challengeButtonText,
constructBarStyle,
VERSION,
LAYERS,
hotkeys,

View file

@ -5,8 +5,8 @@ let gameEnded = false;
// Don't change this
const TMT_VERSION = {
tmtNum: "2.4.1",
tmtName: "Rationalized Edition"
tmtNum: "2.5.3",
tmtName: "Dreams Really Do Come True"
}
function getResetGain(layer, useType = null) {
@ -117,9 +117,13 @@ function shouldNotify(layer) {
return true;
}
if (tmp[layer].shouldNotify)
return true
if (isPlainObject(tmp[layer].tabFormat)) {
for (let subtab in tmp[layer].tabFormat) {
if (subtabShouldNotify(layer, "mainTabs", subtab)) {
tmp[layer].trueGlowColor = tmp[layer].tabFormat[subtab].glowColor
return true;
}
}
@ -128,12 +132,13 @@ function shouldNotify(layer) {
for (let family in tmp[layer].microtabs) {
for (let subtab in tmp[layer].microtabs[family]) {
if (subtabShouldNotify(layer, family, subtab)) {
tmp[layer].trueGlowColor = tmp[layer].microtabs[family][subtab].glowColor
return true;
}
}
}
return tmp[layer].shouldNotify;
return false;
}
@ -162,7 +167,7 @@ function rowReset(row, layer) {
}
function layerDataReset(layer, keep = []) {
let storedData = {unlocked: player[layer].unlocked}; // Always keep unlocked
let storedData = {unlocked: player[layer].unlocked, forceTooltip: player[layer].forceTooltip, noRespecConfirm: player[layer].noRespecConfirm} // Always keep these
for (let thing in keep) {
if (player[layer][keep[thing]] !== undefined) {
@ -508,6 +513,9 @@ const interval = setInterval(function () {
}
tmp.scrolled = document.getElementById("treeTab")?.scrollTop > 30;
updateTemp();
updateOomps(diff);
updateWidth()
updateTabFormats()
gameLoop(diff);
fixNaNs();
adjustPopupTime(0.05);

194
js/technical/displays.js Normal file
View file

@ -0,0 +1,194 @@
function prestigeButtonText(layer) {
if (layers[layer].prestigeButtonText !== undefined)
return run(layers[layer].prestigeButtonText(), layers[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}` : ""}`
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}
`
if (tmp[layer].type == "none")
return ""
return "You need prestige button text"
}
function constructNodeStyle(layer){
let style = []
if ((tmp[layer].isLayer && layerunlocked(layer)) || (!tmp[layer].isLayer && tmp[layer].canClick))
style.push({'background-color': tmp[layer].color})
if (tmp[layer].image !== undefined)
style.push({'background-image': 'url("' + tmp[layer].image + '")'})
if(tmp[layer].notify && player[layer].unlocked)
style.push({'box-shadow': 'var(--hqProperty2a), 0 0 20px ' + tmp[layer].trueGlowColor})
style.push(tmp[layer].nodeStyle)
return style
}
function challengeStyle(layer, id) {
if (player[layer].activeChallenge == id && canCompleteChallenge(layer, id)) return "canComplete"
else if (hasChallenge(layer, id)) return "done"
return "locked"
}
function challengeButtonText(layer, id) {
return (player[layer].activeChallenge==(id)?(canCompleteChallenge(layer, id)?"Finish":"Exit Early"):(hasChallenge(layer, id)?"Completed":"Start"))
}
function achievementStyle(layer, id){
ach = tmp[layer].achievements[id]
let style = []
if (ach.image){
style.push({'background-image': 'url("' + ach.image + '")'})
}
if (!ach.unlocked) style.push({'visibility': 'hidden'})
style.push(ach.style)
return style
}
function updateWidth() {
var screenWidth = window.innerWidth
var splitScreen = screenWidth >= 1024
if (player.splitMode === "disabled") splitScreen = false
if (player.splitMode === "enabled") splitScreen = true
tmp.other.screenWidth = screenWidth
tmp.other.splitScreen = splitScreen
tmp.other.lastPoints = player.points
}
function updateOomps(diff)
{
tmp.other.oompsMag = 0
if (player.points.lte(new Decimal(1e100))) return
var pp = new Decimal(player.points);
var lp = tmp.other.lastPoints || new Decimal(0);
if (pp.gt(lp)) {
if (pp.gte("10^^8")) {
pp = pp.slog(1e10)
lp = lp.slog(1e10)
tmp.other.oomps = pp.sub(lp).div(diff)
tmp.other.oompsMag = -1;
} else {
while (pp.div(lp).log(10).div(diff).gte("100") && tmp.other.oompsMag <= 5 && lp.gt(0)) {
pp = pp.log(10)
lp = lp.log(10)
tmp.other.oomps = pp.sub(lp).div(diff)
tmp.other.oompsMag++;
}
}
}
}
function constructBarStyle(layer, id) {
let bar = tmp[layer].bars[id]
let style = {}
if (bar.progress instanceof Decimal)
bar.progress = bar.progress.toNumber()
bar.progress = (1 -Math.min(Math.max(bar.progress, 0), 1)) * 100
style.dims = {'width': bar.width + "px", 'height': bar.height + "px"}
let dir = bar.direction
style.fillDims = {'width': (bar.width + 0.5) + "px", 'height': (bar.height + 0.5) + "px"}
switch(bar.direction) {
case UP:
style.fillDims['clip-path'] = 'inset(' + bar.progress + '% 0% 0% 0%)'
style.fillDims.width = bar.width + 1 + 'px'
break;
case DOWN:
style.fillDims['clip-path'] = 'inset(0% 0% ' + bar.progress + '% 0%)'
style.fillDims.width = bar.width + 1 + 'px'
break;
case RIGHT:
style.fillDims['clip-path'] = 'inset(0% ' + bar.progress + '% 0% 0%)'
break;
case LEFT:
style.fillDims['clip-path'] = 'inset(0% 0% 0% ' + bar.progress + '%)'
break;
case DEFAULT:
style.fillDims['clip-path'] = 'inset(0% 50% 0% 0%)'
}
return style
}
function constructTabFormat(layer, id, family){
let tabTemp, tabLayer, tabFunc, location, key
if (id === undefined){
tabTemp = tmp[layer].tabFormat
tabLayer = layers[layer].tabFormat
tabFunc = funcs[layer].tabFormat
location = tmp[layer]
key = "tabFormat"
}
else if (family === undefined) {
tabTemp = tmp[layer].tabFormat[id].content
tabLayer = layers[layer].tabFormat[id].content
tabFunc = funcs[layer].tabFormat[id].content
location = tmp[layer].tabFormat[id]
key = "content"
}
else {
tabTemp = tmp[layer].microtabs[family][id].content
tabLayer = layers[layer].microtabs[family][id].content
tabFunc = funcs[layer].microtabs[family][id].content
location = tmp[layer].microtabs[family][id]
key = "tabFormat"
}
if (isFunction(tabLayer)) {
let bound = tabLayer.bind(layers[layer])
Vue.set(tabTemp, key, bound())
}
updateTempData(tabLayer, tabTemp, tabFunc)
return tabTemp
}
function updateTabFormats() {
updateTabFormat(player.tab)
updateTabFormat(player.navTab)
}
function updateTabFormat(layer) {
if (layers[layer]?.tabFormat === undefined) return
let tab = player.subtabs[layer]?.mainTabs
if (isFunction(layers[layer].tabFormat)) {
Vue.set(temp[layer], 'tabFormat', layers[layer].tabFormat())
}
else if (Array.isArray(layers[layer].tabFormat)) {
Vue.set(temp[layer], 'tabFormat', constructTabFormat(layer))
}
else if (isPlainObject(layers[layer].tabFormat)) {
if (layers[layer].tabFormat[tab].embedLayer === undefined)
Vue.set(temp[layer].tabFormat[tab], 'content', constructTabFormat(layer, tab))
}
// Check for embedded layer
if (isPlainObject(tmp[layer].tabFormat) && tmp[layer].tabFormat[tab].embedLayer !== undefined) {
constructTabFormat(tmp[layer].tabFormat[tab].embedLayer)
}
// Update microtabs
for (family in layers[layer].microtabs) {
tab = player.subtabs[layer][family]
if (tmp[layer].microtabs[family][tab]) {
if (tmp[layer].microtabs[family][tab].embedLayer)
constructTabFormat(tmp[layer].microtabs[family][tab].embedLayer)
else
constructTabFormat(layer, tab, family)
}
}
}

View file

@ -118,7 +118,9 @@ function setupLayer(layer){
layers[layer].buyables[thing].layer = layer
if (layers[layer].buyables[thing].unlocked === undefined)
layers[layer].buyables[thing].unlocked = true
}
}
layers[layer].buyables[thing].canBuy = function() {return canBuyBuyable(this.layer, this.id)}
if (layers[layer].buyables[thing].purchaseLimit === undefined) layers[layer].buyables[thing].purchaseLimit = new Decimal(Infinity)
}
}
@ -172,6 +174,7 @@ function setupLayer(layer){
if(layers[layer].displayRow === undefined) layers[layer].displayRow = layers[layer].row
if(layers[layer].name === undefined) layers[layer].name = layer
if(layers[layer].layerShown === undefined) layers[layer].layerShown = true
if(layers[layer].glowColor === undefined) layers[layer].glowColor = "#ff0000"
let row = layers[layer].row

View file

@ -7,8 +7,8 @@ const systemComponents = {
<div v-for="tab in Object.keys(data)">
<button v-if="data[tab].unlocked === undefined || data[tab].unlocked"
v-bind:class="{tabButton: true, notify: subtabShouldNotify(layer, name, tab), resetNotify: subtabResetNotify(layer, name, tab)}"
v-bind:style="[{'border-color': tmp[layer].color}, tmp[layer].componentStyles['tab-button'], data[tab].buttonStyle]"
v-on:click="function(){player.subtabs[layer][name] = tab; needCanvasUpdate = true;}">{{ tab }}
v-bind:style="[{'border-color': tmp[layer].color}, (data[tab].glowColor && subtabShouldNotify(layer, name, tab) ? {'box-shadow': 'var(--hqProperty2a), 0 0 20px ' + data[tab].glowColor} : {}), tmp[layer].componentStyles['tab-button'], data[tab].buttonStyle]"
v-on:click="function(){player.subtabs[layer][name] = tab; updateTabFormats(); needCanvasUpdate = true;}">{{ tab }}
</button>
</div>
</div>
@ -41,7 +41,7 @@ const systemComponents = {
resetNotify: tmp[layer].prestigeNotify,
can: ((player[layer].unlocked || tmp[layer].isLayer) && tmp[layer].isLayer) || (!tmp[layer].isLayer && tmp[layer].canClick),
}"
v-bind:style="tmp[layer].computedNodeStyle">
v-bind:style="constructNodeStyle(layer)">
<span v-html="(abb !== '' && tmp[layer].image === undefined) ? abb : '&nbsp;'"></span>
<tooltip
v-if="tmp[layer].tooltip != ''"
@ -119,9 +119,12 @@ const systemComponents = {
<span v-if="player.offTime !== undefined" class="overlayThing">
<br>Offline Time: {{formatTime(player.offTime.remain)}}<br>
</span>
<span v-if="false && !player.keepGoing" class="overlayThing">
<br>Reach {{formatWhole(ENDGAME)}} to beat the game!<br>
</span>
<br>
<span v-if="player.points.lt('1e1000')" class="overlayThing">You have </span>
<h2 class="overlayThing" id="points">{{format(player.points)}}</h2>
<span v-if="player.points.lt('1e1e6')" class="overlayThing"> {{modInfo.pointsName}}</span>
<br>
<span v-if="canGenPoints()" class="overlayThing">({{tmp.other.oompsMag != 0 ? format(tmp.other.oomps) + " OOM" + (tmp.other.oompsMag < 0 ? "^OOM" : tmp.other.oompsMag > 1 ? "^" + tmp.other.oompsMag : "") + "s" : formatSmall(getPointGen())}}/sec)</span>
<div v-for="thing in tmp.displayThings" class="overlayThing"><span v-if="thing" v-html="thing"></span></div>
</div>
`
@ -176,8 +179,7 @@ const systemComponents = {
<td><button class="opt" onclick="toggleOpt('offlineProd')">Offline Prod: {{ player.offlineProd?"ON":"OFF" }}</button></td>
</tr>
<tr>
<td><button class="opt" onclick="toggleOpt('hideChallenges')">Completed Challenges: {{ player.hideChallenges?"HIDDEN":"SHOWN" }}</button></td>
<td><button class="opt" onclick="adjustMSDisp()">Show Milestones: {{ player.msDisplay.toUpperCase() }}</button></td>
<td><button class="opt" onclick="adjustMSDisp()">Show Milestones: {{ MS_DISPLAYS[MS_SETTINGS.indexOf(player.msDisplay)]}}</button></td>
<td><button class="opt" onclick="toggleOpt('hqTree')">High-Quality Tree: {{ player.hqTree?"ON":"OFF" }}</button></td>
</table>`
},

View file

@ -8,7 +8,9 @@ let NaNalert = false;
const activeFunctions = [
"startData", "onPrestige", "doReset", "update", "automate",
"buy", "buyMax", "respec", "onComplete", "onPurchase", "onPress", "onClick", "masterButtonPress",
"sellOne", "sellAll", "pay",
"sellOne", "sellAll", "pay", "actualCostFunction", "actualEffectFunction",
"effectDescription", "display", "fullDisplay", "effectDisplay", "rewardDisplay",
"tabFormat", "content",
];
const noCall = doNotCallTheseFunctionsEveryTick;
@ -34,10 +36,26 @@ function setupTemp() {
tmp[layer].canReset = {}
tmp[layer].notify = {}
tmp[layer].prestigeNotify = {}
tmp[layer].prestigeButtonText = {}
tmp[layer].computedNodeStyle = []
setupBarStyles(layer)
setupBuyables(layer)
tmp[layer].trueGlowColor = []
}
tmp.other = {
screenWidth: window.innerWidth,
splitScreen: window.innerWidth >=1024,
lastPoints: player.points || new Decimal(0),
oomps: new Decimal(0),
held: {
time: null,
id: null,
layer: null,
type: null,
}
}
temp = tmp
}
@ -84,14 +102,9 @@ function updateTemp() {
tmp[layer].nextAt = getNextAt(layer)
tmp[layer].nextAtDisp = getNextAt(layer, true)
tmp[layer].canReset = canReset(layer)
tmp[layer].trueGlowColor = tmp[layer].glowColor
tmp[layer].notify = shouldNotify(layer)
tmp[layer].prestigeNotify = prestigeNotify(layer)
tmp[layer].prestigeButtonText = prestigeButtonText(layer)
constructBarStyles(layer)
constructAchievementStyles(layer)
constructNodeStyle(layer)
updateChallengeDisplay(layer)
}
tmp.pointGen = getPointGen()
@ -101,13 +114,13 @@ function updateTemp() {
if (isFunction(text)) text = text()
tmp.displayThings.push(text)
}
}
function updateTempData(layerData, tmpData, funcsData) {
for (let item in funcsData){
if (Array.isArray(layerData[item])) {
if (item === "tabFormat" || item === "content") return // These are only updated when needed
updateTempData(layerData[item], tmpData[item], funcsData[item])
}
else if ((!!layerData[item]) && (layerData[item].constructor === Object) || (typeof layerData[item] === "object") && traversableClasses.includes(layerData[item].constructor.name)){
@ -126,8 +139,6 @@ function updateTempData(layerData, tmpData, funcsData) {
NaNalert = true;
}
}
Vue.set(tmpData, item, value)
}
}
@ -136,20 +147,8 @@ function updateTempData(layerData, tmpData, funcsData) {
function updateChallengeTemp(layer)
{
updateTempData(layers[layer].challenges, tmp[layer].challenges, funcs[layer].challenges)
updateChallengeDisplay(layer)
}
function updateChallengeDisplay(layer) {
for (let id in player[layer].challenges) {
let style = "locked"
if (player[layer].activeChallenge === id && canCompleteChallenge(layer, id)) style = "canComplete"
else if (hasChallenge(layer, id)) style = "done"
tmp[layer].challenges[id].defaultStyle = style
tmp[layer].challenges[id].buttonText = (player[layer].activeChallenge===(id)?(canCompleteChallenge(layer, id)?"Finish":"Exit Early"):(hasChallenge(layer, id)?"Completed":"Start"))
}
}
function updateBuyableTemp(layer)
{
@ -161,75 +160,20 @@ function updateClickableTemp(layer)
updateTempData(layers[layer].clickables, tmp[layer].clickables, funcs[layer].clickables)
}
function constructNodeStyle(layer){
let style = []
if ((tmp[layer].isLayer && layerunlocked(layer)) || (!tmp[layer].isLayer && tmp[layer].canClick))
style.push({'background-color': tmp[layer].color})
if (tmp[layer].image !== undefined)
style.push({'background-image': 'url("' + tmp[layer].image + '")'})
style.push(tmp[layer].nodeStyle)
Vue.set(tmp[layer], 'computedNodeStyle', style)
}
function constructAchievementStyles(layer){
for (let id in tmp[layer].achievements) {
let ach = tmp[layer].achievements[id]
if (isPlainObject(ach)) {
let style = []
if (ach.image){
style.push({'background-image': 'url("' + ach.image + '")'})
}
if (!ach.unlocked) style.push({'visibility': 'hidden'})
style.push(ach.style)
Vue.set(ach, 'computedStyle', style)
}
}
}
function constructBarStyles(layer){
if (layers[layer].bars === undefined)
return
for (let id in layers[layer].bars){
if (id !== "layer") {
let bar = tmp[layer].bars[id]
if (bar.progress instanceof Decimal)
bar.progress = bar.progress.toNumber()
bar.progress = (1 -Math.min(Math.max(bar.progress, 0), 1)) * 100
bar.dims = {'width': bar.width + "px", 'height': bar.height + "px"}
let dir = bar.direction
bar.fillDims = {'width': (bar.width + 0.5) + "px", 'height': (bar.height + 0.5) + "px"}
if (dir !== undefined)
{
bar.fillDims['clip-path'] = 'inset(0% 50% 0% 0%)'
if(dir === UP){
bar.fillDims['clip-path'] = 'inset(' + bar.progress + '% 0% 0% 0%)'
}
else if(dir === DOWN){
bar.fillDims['clip-path'] = 'inset(0% 0% ' + bar.progress + '% 0%)'
}
else if(dir === RIGHT){
bar.fillDims['clip-path'] = 'inset(0% ' + bar.progress + '% 0% 0%)'
}
else if(dir === LEFT){
bar.fillDims['clip-path'] = 'inset(0% 0% 0% ' + bar.progress + '%)'
}
function setupBuyables(layer) {
for (id in layers[layer].buyables) {
if (!isNaN(id)) {
let b = layers[layer].buyables[id]
b.actualCostFunction = b.cost
b.cost = function(x) {
x = (x === undefined ? player[this.layer].buyables[this.id] : x)
return layers[this.layer].buyables[this.id].actualCostFunction(x)
}
b.actualEffectFunction = b.effect
b.effect = function(x) {
x = (x === undefined ? player[this.layer].buyables[this.id] : x)
return layers[this.layer].buyables[this.id].actualEffectFunction(x)
}
}
}
}
function setupBarStyles(layer){
if (layers[layer].bars === undefined)
return
for (let id in layers[layer].bars){
let bar = tmp[layer].bars[id]
bar.dims = {}
bar.fillDims = {}
}
}

View file

@ -18,6 +18,11 @@ function canAffordUpgrade(layer, id) {
return canAffordPurchase(layer, upg, cost);
}
function canBuyBuyable(layer, id) {
let b = temp[layer].buyables[id]
return (b.unlocked && b.canAfford && player[layer].buyables[id].lt(b.purchaseLimit))
}
function hasUpgrade(layer, id) {
return (player[layer].upgrades.includes(toNumber(id)) || player[layer].upgrades.includes(id.toString()));
}
@ -179,7 +184,7 @@ function buyBuyable(layer, id) {
if (!tmp[layer].buyables[id].unlocked) {
return;
}
if (!tmp[layer].buyables[id].canAfford) {
if (!tmp[layer].buyables[id].canBuy) {
return;
}
@ -229,14 +234,14 @@ function showTab(name) {
if (player.tab === name && isPlainObject(tmp[name].tabFormat)) {
player.subtabs[name].mainTabs = Object.keys(layers[name].tabFormat)[0];
}
var toTreeTab = name == "none"
player.tab = name
if (player.navTab == "none" && (tmp[name].row !== "side") && (tmp[name].row !== "otherside")) player.lastSafeTab = name
delete player.notify[name]
updateTabFormats()
needCanvasUpdate = true
document.activeElement.blur()
player.tab = name;
if (player.navTab === "none" && (tmp[name].row !== "side") && (tmp[name].row !== "otherside")) {
player.lastSafeTab = name;
}
delete player.notify[name];
needCanvasUpdate = true;
document.activeElement.blur();
}
function showNavTab(name) {
@ -244,10 +249,11 @@ function showNavTab(name) {
return;
}
player.navTab = name;
player.notify[name] = false;
needCanvasUpdate = true;
var toTreeTab = name == "tree"
player.navTab = name
player.notify[name] = false
updateTabFormats()
needCanvasUpdate = true
}
@ -338,6 +344,7 @@ function layerunlocked(layer) {
function keepGoing() {
player.keepGoing = true;
needCanvasUpdate = true;
goBack()
}
function toNumber(x) {
@ -360,6 +367,7 @@ function updateMilestones(layer) {
if (tmp[layer].milestonePopups || tmp[layer].milestonePopups === undefined) {
doPopup("milestone", tmp[layer].milestones[id].requirementDescription, "Milestone Gotten!", 3, tmp[layer].color);
}
player[layer].lastMilestone = id;
}
}
}
@ -423,7 +431,6 @@ document.onkeydown = function (e) {
if (ctrlDown && hotkeys[key]) e.preventDefault()
if (hotkeys[key]) {
let k = hotkeys[key]
console.log(tmp[k.layer].hotkeys)
if (player[k.layer].unlocked && tmp[k.layer].hotkeys[k.id].unlocked)
k.onPress()
}
@ -441,20 +448,6 @@ function focused(x) {
onFocused = x;
}
function prestigeButtonText(layer) {
if (layers[layer].prestigeButtonText !== undefined) {
return layers[layer].prestigeButtonText();
} else 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 "You need prestige button text";
}
}
function isFunction(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
@ -522,7 +515,7 @@ function adjustPopupTime(diff) {
}
function run(func, target, args = null) {
if (func && func.constructor && func.call && func.apply) {
if (ifFunction(func)) {
let bound = func.bind(target);
return bound(args);
} else {

107
js/utils/NumberFormating.js Normal file
View file

@ -0,0 +1,107 @@
function exponentialFormat(num, precision, mantissa = true) {
let e = num.log10().floor()
let m = num.div(Decimal.pow(10, e))
if (m.toStringWithDecimalPlaces(precision) == 10) {
m = new Decimal(1)
e = e.add(1)
}
e = (e.gte(1e9) ? format(e, 1) : (e.gte(10000) ? commaFormat(e, 0) : e.toStringWithDecimalPlaces(0)))
if (mantissa)
return m.toStringWithDecimalPlaces(precision) + "e" + e
else return "e" + e
}
function commaFormat(num, precision) {
if (num === null || num === undefined) return "NaN"
if (num.mag < 0.001) return (0).toFixed(precision)
return num.toStringWithDecimalPlaces(precision).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,")
}
function regularFormat(num, precision) {
if (num === null || num === undefined) return "NaN"
if (num.mag < 0.001) return (0).toFixed(precision)
if (num.mag < 0.01) precision = 3
return num.toStringWithDecimalPlaces(precision)
}
function fixValue(x, y = 0) {
return x || new Decimal(y)
}
function sumValues(x) {
x = Object.values(x)
if (!x[0]) return new Decimal(0)
return x.reduce((a, b) => Decimal.add(a, b))
}
function format(decimal, precision = 2, small) {
small = small || modInfo.allowSmall
decimal = new Decimal(decimal)
if (isNaN(decimal.sign) || isNaN(decimal.layer) || isNaN(decimal.mag)) {
player.hasNaN = true;
return "NaN"
}
if (decimal.sign < 0) return "-" + format(decimal.neg(), precision)
if (decimal.mag == Number.POSITIVE_INFINITY) return "Infinity"
if (decimal.gte("eeee1000")) {
var slog = decimal.slog()
if (slog.gte(1e6)) return "F" + format(slog.floor())
else return Decimal.pow(10, slog.sub(slog.floor())).toStringWithDecimalPlaces(3) + "F" + commaFormat(slog.floor(), 0)
}
else if (decimal.gte("1e100000")) return exponentialFormat(decimal, 0, false)
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 if (decimal.gte(0.001) || !small) return regularFormat(decimal, precision)
else if (decimal.eq(0)) return (0).toFixed(precision)
decimal = invertOOM(decimal)
let val = ""
if (decimal.lt("1e1000")){
val = exponentialFormat(decimal, precision)
return val.replace(/([^(?:e|F)]*)$/, '-$1')
}
else
return format(decimal, precision) + "⁻¹"
}
function formatWhole(decimal) {
decimal = new Decimal(decimal)
if (decimal.gte(1e9)) return format(decimal, 2)
if (decimal.lte(0.98) && !decimal.eq(0)) return format(decimal, 2)
return format(decimal, 0)
}
function formatTime(s) {
if (s < 60) return format(s) + "s"
else if (s < 3600) return formatWhole(Math.floor(s / 60)) + "m " + format(s % 60) + "s"
else if (s < 86400) return formatWhole(Math.floor(s / 3600)) + "h " + formatWhole(Math.floor(s / 60) % 60) + "m " + format(s % 60) + "s"
else if (s < 31536000) return formatWhole(Math.floor(s / 86400) % 365) + "d " + formatWhole(Math.floor(s / 3600) % 24) + "h " + formatWhole(Math.floor(s / 60) % 60) + "m " + format(s % 60) + "s"
else return formatWhole(Math.floor(s / 31536000)) + "y " + formatWhole(Math.floor(s / 86400) % 365) + "d " + formatWhole(Math.floor(s / 3600) % 24) + "h " + formatWhole(Math.floor(s / 60) % 60) + "m " + format(s % 60) + "s"
}
function toPlaces(x, precision, maxAccepted) {
x = new Decimal(x)
let result = x.toStringWithDecimalPlaces(precision)
if (new Decimal(result).gte(maxAccepted)) {
result = new Decimal(maxAccepted - Math.pow(0.1, precision)).toStringWithDecimalPlaces(precision)
}
return result
}
// Will also display very small numbers
function formatSmall(x, precision=2) {
return format(x, precision, true)
}
function invertOOM(x){
let e = x.log10().ceil()
let m = x.div(Decimal.pow(10, e))
e = e.neg()
x = new Decimal(10).pow(e).times(m)
return x
}

View file

@ -43,12 +43,13 @@ function regularFormat(num, precision) {
return (0).toFixed(precision);
}
if (num.mag < 0.01) {
return num.toExponential(precision);
precision = 3;
}
return num.toStringWithDecimalPlaces(precision);
}
function format(decimal, precision=2,) {
function format(decimal, precision=2, small) {
small = small || modInfo.allowSmall;
decimal = new Decimal(decimal);
if (isNaN(decimal.sign)||isNaN(decimal.layer)||isNaN(decimal.mag)) {
player.hasNaN = true;
@ -75,9 +76,19 @@ function format(decimal, precision=2,) {
return exponentialFormat(decimal, precision);
} else if (decimal.gte(1e3)) {
return commaFormat(decimal, 0);
} else {
} else if (decimal.gte(0.001) || !small) {
return regularFormat(decimal, precision);
} else if (decimal.eq(0)) {
return (0).toFixed(precision);
}
decimal = invertOOM(decimal);
if (decimal.lt("1e1000")){
const val = exponentialFormat(decimal, precision);
return val.replace(/([^(?:e|F)]*)$/, '-$1');
} else {
return format(decimal, precision) + "⁻¹";
}
}
function formatWhole(decimal) {
@ -108,3 +119,25 @@ function formatTime(s) {
}
}
function toPlaces(x, precision, maxAccepted) {
x = new Decimal(x);
let result = x.toStringWithDecimalPlaces(precision);
if (new Decimal(result).gte(maxAccepted)) {
result = new Decimal(maxAccepted - Math.pow(0.1, precision)).toStringWithDecimalPlaces(precision);
}
return result;
}
// Will also display very small numbers
function formatSmall(x, precision=2) {
return format(x, precision, true);
}
function invertOOM(x){
let e = x.log10().ceil();
let m = x.div(Decimal.pow(10, e));
e = e.neg();
x = new Decimal(10).pow(e).times(m);
return x;
}

View file

@ -28,9 +28,13 @@ function changeTreeQuality() {
function toggleAuto(toggle) {
player[toggle[0]][toggle[1]] = !player[toggle[0]][toggle[1]];
}
const MS_DISPLAYS = ["ALL", "LAST, AUTO, INCOMPLETE", "AUTOMATION, INCOMPLETE", "INCOMPLETE", "NONE"];
const MS_SETTINGS = ["always", "last", "automation", "incomplete", "never"];
function adjustMSDisp() {
let displays = ["always", "automation", "incomplete", "never"];
player.msDisplay = displays[(displays.indexOf(player.msDisplay) + 1) % 4];
player.msDisplay = MS_SETTINGS[(MS_SETTINGS.indexOf(player.msDisplay) + 1) % 5];
}
function milestoneShown(layer, id) {
let complete = player[layer].milestones.includes(id);
@ -39,6 +43,8 @@ function milestoneShown(layer, id) {
switch (player.msDisplay) {
case "always":
return true;
case "last":
return (auto) || !complete || player[layer].lastMilestone === id;
case "automation":
return (auto) || !complete;
case "incomplete":

View file

@ -94,6 +94,7 @@ function getStartLayerData(layer) {
layerdata.spentOnBuyables = new Decimal(0);
layerdata.upgrades = [];
layerdata.milestones = [];
layerdata.lastMilestone = null;
layerdata.achievements = [];
layerdata.challenges = getStartChallenges(layer);
return layerdata;
@ -219,6 +220,7 @@ function load() {
setupTemp();
updateTemp();
updateTemp();
updateTabFormats();
loadVue();
}
function setupModInfo() {