Merge branch 'master' of https://github.com/IvarK/HahaSlabWontGetHere into omsi_sfx

This commit is contained in:
Omsi 2020-06-13 17:46:20 -07:00
commit 78f6351dea
362 changed files with 26861 additions and 11415 deletions

View File

@ -108,7 +108,6 @@
"computed-property-spacing": "error",
"consistent-this": "error",
"func-call-spacing": "error",
"guard-for-in": "warn",
"id-blacklist": [
"error",
"ret",

View File

@ -8,7 +8,7 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
A clear and concise description of what the problem is. Ex. I'm always frustrated when Blind shows me information
**Describe the solution you'd like**
A clear and concise description of what you want to happen.

View File

@ -4,7 +4,7 @@
<link rel="stylesheet" type="text/css" href="stylesheets/styles.css?3">
<style>
td {
border: 1px solid gray;
border: 0.1rem solid gray;
}
</style>
</head>

View File

@ -1,56 +1,54 @@
body {
font-family: Arial;
html {
font-size: 62.5%;
height: 100%;
width: 100%;
}
#container {
body {
font-family: Arial, serif;
}
.depression {
text-align: center;
padding: 4%;
font-size: 2.5rem
}
.depression-amount {
font-size: 4.5rem;
font-weight: bold;
}
.stuff-container {
margin: auto;
text-align: center;
}
#depression {
text-align:center;
padding: 4%;
font-size: 25px
.button {
margin: 1rem auto;
width: 13rem;
height: 5rem;
border: .2rem solid green;
border-radius: 1rem;
vertical-align: top;
cursor: pointer;
transition-duration: 0.3s;
}
#amount {
font-size: 45px;
font-weight: bold;
}
.btn {
margin: 10px auto;
width: 130px;
height: 50px;
border: 2px solid green;
border-radius: 10px;
.button--stuff {
background-color: grey;
color: white;
vertical-align: top;
cursor: pointer;
transition-duration: 0.3s;
}
.btn:hover {
background-color: rgb(125, 151, 118);
.button--stuff:hover {
background-color: #7d9776;
}
.prestigebtn {
margin: 10px auto;
width: 130px;
height: 50px;
border: 2px solid green;
border-radius: 10px;
background-color: rgb(221, 221, 221);
color: rgb(0, 0, 0);
vertical-align: top;
cursor: pointer;
transition-duration: 0.3s;
.button--prestige {
background-color: #dddddd;
color: black;
}
.prestigebtn:hover {
background-color: rgb(141, 177, 221);
.button--prestige:hover {
background-color: #8db1dd;
}

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
@ -7,11 +7,10 @@
<title>The Depression Game</title>
</head>
<body>
<p id="depression">You have <span id="amount">0</span> depression</p>
<div id="container">
<button id="1" class="btn" onclick="buyStuff(1)">Amount: 0<br>Cost:1</button>
</div>
<div id="depression"></div>
</body>
<script type="text/javascript" src="../javascripts/lib/vue.min.js"></script>
<script type="text/javascript" src="../javascripts/lib/break_infinity.min.js"></script>
<script type="text/javascript" src="../javascripts/lib/ad-notations.min.js"></script>
<script type="text/javascript" src="depression.js"></script>
</html>
</html>

View File

@ -1,172 +1,308 @@
"use strict";
const UPDATE_INTERVAL = 33;
const SAVE_INTERVAL = 5000;
const notation = new ADNotations.ScientificNotation();
let uiUpdateHooks = [];
function updateUI() {
for (const hook of uiUpdateHooks) {
hook.update();
}
}
let game = {
costs: [new Decimal(1)],
amounts: [new Decimal(0)],
purchases: [new Decimal(0)],
depression: new Decimal(1),
prestige: [new Decimal(1)]
}
};
function formatValue(x, places) {
if (x instanceof Decimal) {
if (x.lt(1000)) return x.toFixed(0);
else return (x.m.toFixed(places) + "e" + x.e)
class StuffState {
constructor(id) {
this._id = id;
}
const power = Math.floor(Math.log10(x))
const matissa = x / Math.pow(10, power)
if (x < 1000) return x.toString()
else return ((matissa).toFixed(places) + "e" + power)
}
get id() {
return this._id;
}
function insertAfter(newElement,targetElement) {
// target is what you want it to go after. Look for this elements parent.
const parent = targetElement.parentNode;
get previousStuff() {
return this.id > 1 ? Stuff(this.id - 1) : undefined;
}
// if the parents lastchild is the targetElement...
if (parent.lastChild == targetElement) {
// add the newElement after the target element.
parent.appendChild(newElement);
get nextStuff() {
return Stuff(this.id + 1);
}
get isUnlocked() {
return Stuffs.count >= this.id;
}
get amount() {
return game.amounts[this.id - 1];
}
set amount(value) {
game.amounts[this.id - 1] = value;
}
get purchases() {
return game.purchases[this.id - 1];
}
set purchases(value) {
game.purchases[this.id - 1] = value;
}
get cost() {
const baseCost = Decimal.pow(100, this.id - 1);
const costIncrease = Decimal.pow(2, this.purchases);
return baseCost.times(costIncrease);
}
get isAffordable() {
return game.depression.gte(this.cost);
}
purchase() {
if (!this.isAffordable) return;
game.depression = game.depression.minus(this.cost);
const nextStuff = this.nextStuff;
if (!nextStuff.isUnlocked) {
nextStuff.amount = new Decimal(0);
nextStuff.purchases = new Decimal(0);
nextStuff.prestige = new Decimal(1);
}
this.amount = this.amount.plus(1).max(this.amount.times(1.05).min(nextStuff.amount.times(10000)));
this.purchases = this.purchases.plus(1);
updateUI();
}
get prestige() {
return game.prestige[this.id - 1];
}
set prestige(value) {
game.prestige[this.id - 1] = Decimal.max(this.prestige, value);
}
get canPrestige() {
return Stuff(this.id + 6).isUnlocked;
}
get nextPrestige() {
return Decimal.max(Stuffs.count - this.id - 5, 1);
}
doPrestige() {
this.prestige = this.nextPrestige;
game = {
amounts: [new Decimal(0)],
purchases: [new Decimal(0)],
depression: new Decimal(1),
prestige: game.prestige
};
updateUI();
}
tick() {
const production = this.amount.times(this.prestige.dividedBy(UPDATE_INTERVAL));
if (this.id === 1) {
game.depression = game.depression.plus(production);
} else {
// else the target has siblings, insert the new element between the target and it's next sibling.
parent.insertBefore(newElement, targetElement.nextSibling);
this.previousStuff.amount = this.previousStuff.amount.plus(production);
}
}
}
const Stuff = id => new StuffState(id);
function buyStuff(id) {
const elem = document.getElementById(id)
var i = id-1
if (game.depression.gte(game.costs[i])) {
const next = document.getElementById(id+1)
if (next == null) {
const btn = document.createElement("button")
const br = document.createElement("br")
btn.innerHTML = "Amount: 0<br>Cost:"+formatValue(game.costs[i].times(100), 2)
btn.id = id+1
btn.className = "btn"
btn.onclick = function() {buyStuff(id+1);}
insertAfter(br, elem)
insertAfter(btn, br)
game.costs.push(game.costs[i].times(100))
game.amounts.push(new Decimal(0))
if (game.prestige[id] === undefined) game.prestige[id] = 1
if (id > 5) {
const pbtn = document.createElement("button")
const otherbtn = document.getElementById(id-5)
pbtn.id = id-5+"prestige"
pbtn.className = "prestigebtn"
pbtn.onclick = function() {prestige(parseInt(this.id));}
insertAfter(pbtn, otherbtn)
for (var i=1; i<game.costs.length-5; i++) document.getElementById(i+"prestige").innerHTML = "Reset to increase bonus to "+Math.max(game.costs.length-i-5, game.prestige[id-1])+"x boost."
const Stuffs = {
get count() {
return game.amounts.length;
},
get unlocked() {
return Stuffs.range(Stuffs.count);
},
range(count) {
const stuffs = [];
for (let i = 1; i < count + 1; i++) {
stuffs.push(Stuff(i));
}
return stuffs;
},
tick() {
for (const stuff of Stuffs.unlocked) {
stuff.tick();
}
},
get last() {
return Stuff(Stuffs.count);
}
game.amounts[i] = game.amounts[i].plus(1).max(game.amounts[i].times(1.05).min(game.amounts[id].times(10000)))
game.depression = game.depression.minus(game.costs[i])
game.costs[i] = game.costs[i].times(2)
}
}
function hardreset() {
game = {
costs: [new Decimal(1)],
amounts: [new Decimal(0)],
depression: new Decimal(1),
prestige: [1]
}
}
function prestige(id) {
console.log(id)
game.prestige[id-1] = Math.max(game.costs.length-id-5, game.prestige[id-1])
for (var i=2; i<=game.costs.length; i++) {
var btn = document.getElementById(i)
btn.parentNode.removeChild(btn)
}
var prestiges = document.getElementsByClassName("prestigebtn")
while(prestiges[0]) prestiges[0].parentNode.removeChild(prestiges[0])
const brs = document.getElementsByTagName("br")
while(brs[0]) brs[0].parentNode.removeChild(brs[0])
game = {
costs: [new Decimal(1)],
amounts: [new Decimal(0)],
depression: new Decimal(1),
prestige: game.prestige
}
for (var i=1; i<game.costs.length-5; i++) document.getElementById(i+"prestige").innerHTML = "Reset to increase bonus to "+Math.max(game.costs.length-id-5, game.prestige[id-1])+"x boost."
}
};
function save() {
localStorage.setItem("funsave",JSON.stringify(game));
localStorage.setItem("funsave", JSON.stringify(game));
}
function load() {
var save = JSON.parse(localStorage.getItem("funsave"))
if (save) game = save
var elem = document.getElementById("1")
for (var i=1; i<game.costs.length; i++) {
var btn = document.createElement("button")
var br = document.createElement("br")
btn.innerHTML = "Amount: 0<br>Cost:"+formatValue(game.costs[i])
btn.id = i+1
btn.className = "btn"
btn.onclick = function() {buyStuff(parseInt(this.id));}
insertAfter(br, elem)
insertAfter(btn, br)
elem = btn
const saveData = JSON.parse(localStorage.getItem("funsave"));
if (saveData === null) return;
saveData.depression = new Decimal(saveData.depression);
for (let i = 0; i < Object.keys(saveData.amounts).length; i++) {
saveData.amounts[i] = new Decimal(saveData.amounts[i]);
}
for (var i=1; i<game.costs.length-5; i++) {
var pbtn = document.createElement("button")
var otherbtn = document.getElementById(i)
pbtn.innerHTML = "Reset to increase bonus to "+Math.max(game.costs.length-i-5, game.prestige[i-1])+"x boost."
pbtn.id = i+"prestige"
pbtn.className = "prestigebtn"
pbtn.onclick = function() {prestige(parseInt(this.id));}
insertAfter(pbtn, otherbtn)
for (let i = 0; i < Object.keys(saveData.prestige).length; i++) {
saveData.prestige[i] = new Decimal(saveData.prestige[i]);
}
game.depression = new Decimal(game.depression)
for (var i=0; i<Object.keys(game.amounts).length; i++) {
game.amounts[i] = new Decimal(game.amounts[i])
if (saveData.purchases !== undefined) {
for (let i = 0; i < Object.keys(saveData.purchases).length; i++) {
saveData.purchases[i] = new Decimal(saveData.purchases[i]);
}
}
for (var i=0; i<Object.keys(game.costs).length; i++) {
game.costs[i] = new Decimal(game.costs[i])
if (saveData.costs !== undefined) {
saveData.purchases = [];
for (let i = 0; i < Object.keys(saveData.costs).length; i++) {
const cost = new Decimal(saveData.costs[i]);
const baseCost = Decimal.pow(100, i);
const costIncrease = cost.dividedBy(baseCost);
saveData.purchases[i] = Decimal.floor(Decimal.log2(costIncrease)).max(0);
}
}
game = saveData;
}
// eslint-disable-next-line prefer-const
let cheat = false;
function gameLoop() {
Stuffs.tick();
var cheat = false;
setInterval(function() {
game.depression = game.depression.plus(game.amounts[0].times(game.prestige[0]/33))
document.getElementById("1").innerHTML = "Amount: "+formatValue(game.amounts[0], 2)+"<br>Power: "+formatValue(game.prestige[0], 2)+"x<br>Cost:"+formatValue(game.costs[0], 2)
for (var i=2; i <= game.costs.length; i++) {
document.getElementById(i).innerHTML = "Amount: "+formatValue(game.amounts[i-1], 2)+"<br>Power: "+formatValue(game.prestige[i-1], 2)+"x<br>Cost:"+formatValue(game.costs[i-1], 2)
game.amounts[i-2] = game.amounts[i-2].plus(game.amounts[i-1].times(game.prestige[i-1]/33))
}
if (cheat) {
if (game.amounts[game.amounts.length-2] < 5) document.getElementById(game.amounts.length-1).click()
document.getElementById(game.amounts.length).click()}
document.getElementById("amount").innerHTML = formatValue(game.depression, 2)
}, 33)
const preLastStuff = Stuffs.last.previousStuff;
if (preLastStuff !== undefined && preLastStuff.amount.lessThan(5)) {
preLastStuff.purchase();
}
Stuffs.last.purchase();
}
updateUI();
}
setInterval(function() { save() }, 5000)
Vue.mixin({
methods: {
format(value, places, placesUnder1000) {
return notation.format(value, places, placesUnder1000);
}
},
created() {
if (this.update) {
uiUpdateHooks.push(this);
this.update();
}
},
destroyed() {
uiUpdateHooks = uiUpdateHooks.filter(h => h !== this);
}
});
const StuffButton = {
props: {
stuff: Object
},
data: () => ({
amount: new Decimal(0),
cost: new Decimal(0),
prestige: new Decimal(1)
}),
methods: {
update() {
if (!this.stuff.isUnlocked) return;
this.amount.fromDecimal(this.stuff.amount);
this.cost.fromDecimal(this.stuff.cost);
this.prestige.fromDecimal(this.stuff.prestige);
}
},
template: `
<button class="button button--stuff" @click="stuff.purchase()">
Amount: {{format(amount, 2)}}
<br>
Power: {{format(prestige, 2)}}x
<br>
Cost: {{format(cost, 2)}}
</button>
`
};
const PrestigeButton = {
props: {
stuff: Object
},
data: () => ({
canPrestige: false,
nextPrestige: new Decimal(1)
}),
methods: {
update() {
if (!this.stuff.isUnlocked) return;
this.canPrestige = this.stuff.canPrestige;
this.nextPrestige.fromDecimal(this.stuff.nextPrestige);
}
},
template: `
<button v-if="canPrestige" class="button button--prestige" @click="stuff.doPrestige()">
Reset to increase bonus to {{format(nextPrestige, 2)}}x boost.
</button>
`
};
load()
const Depression = {
components: {
"stuff-button": StuffButton,
"prestige-button": PrestigeButton
},
data: () => ({
depression: new Decimal(1),
stuffCount: 0
}),
computed: {
stuffs() {
return Stuffs.range(this.stuffCount);
}
},
methods: {
update() {
this.depression.fromDecimal(game.depression);
this.stuffCount = Stuffs.count;
}
},
template: `
<div class="app">
<p class="depression">You have <span class="depression-amount">{{format(depression, 2)}}</span> depression</p>
<div class="stuff-container">
<template v-for="stuff in stuffs">
<br v-if="stuff.id > 1">
<stuff-button :stuff="stuff"/>
<prestige-button :stuff="stuff"/>
</template>
</div>
</div>
`
};
let vue;
window.onload = () => {
load();
setInterval(gameLoop, UPDATE_INTERVAL);
setInterval(save, SAVE_INTERVAL);
vue = new Vue({
el: "#depression",
components: {
depression: Depression
},
template: "<depression/>"
});
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 615 KiB

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

BIN
images/kred_single.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
images/loading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
images/std_coin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 KiB

View File

@ -40,7 +40,7 @@
</head>
<body>
<img id="loading" src="images/Loading.png">
<div id="loading"></div>
<div id="ui"></div>
<div id="background-animations">
<div class="videocontainer videocontainer--background">
@ -48,10 +48,10 @@
<source src="images/s6-bg.webm" type="video/webm">
</video>
</div>
<div id="snow" style="display: none; "></div>
<div id="snow" style="display: none;"></div>
</div>
<div class="videocontainer">
<video preload muted id="realityanimbg" style="opacity: 0; display: none; ">
<video preload muted id="realityanimbg" style="opacity: 0; display: none;">
<source src="images/realityanimbg.webm" type="video/webm">
</video>
</div>
@ -62,7 +62,6 @@
<script type="text/javascript" src="javascripts/lib/v-tooltip.min.js"></script>
<script type="text/javascript" src="javascripts/lib/mousetrap.min.js"></script>
<script type="text/javascript" src="javascripts/lib/break_infinity.min.js"></script>
<script type="text/javascript" src="javascripts/lib/chart.min.js"></script>
<script type="text/javascript" src="javascripts/lib/lz-string.min.js"></script>
<script type="text/javascript" src="javascripts/lib/vis-network.min.js"></script>
<script type="text/javascript" src="javascripts/lib/sha512.min.js"></script>
@ -80,6 +79,7 @@
<script type="text/javascript" src="javascripts/lib/active-line.js"></script>
<script type="text/javascript" src="javascripts/lib/closebrackets.js"></script>
<script type="text/javascript" src="javascripts/lib/ad-notations.min.js"></script>
<script type="text/javascript" src="javascripts/lib/svg-pan-zoom.min.js"></script>
<script type="text/javascript" src="PlayFab/PlayFabClientApi.js"></script>
<script type="text/javascript" src="javascripts/DragDropTouch.js"></script>
@ -94,14 +94,20 @@
<script type="text/javascript" src="javascripts/core/math.js"></script>
<script type="text/javascript" src="javascripts/core/async-utils.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/effect.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/effects.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/game-mechanic.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/puchasable.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/set-purchasable.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/bit-purchasable.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanics/rebuyable.js"></script>
<script type="text/javascript" src="javascripts/core/automator/automator-backend.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/effects.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/game-database.js"></script>
<script type="text/javascript" src="javascripts/core/glyph-effects.js"></script>
<script type="text/javascript" src="javascripts/core/player.js"></script>
<script type="text/javascript" src="javascripts/core/performance-stats.js"></script>
<script type="text/javascript" src="javascripts/core/currency.js"></script>
<script type="text/javascript" src="javascripts/core/game-mechanic.js"></script>
<script type="text/javascript" src="javascripts/core/event-hub.js"></script>
<script type="text/javascript" src="javascripts/core/cache.js"></script>
<script type="text/javascript" src="javascripts/core/intervals.js"></script>
@ -122,12 +128,14 @@
<script type="text/javascript" src="javascripts/core/storage/serializer.js"></script>
<script type="text/javascript" src="javascripts/core/storage/storage.js"></script>
<script type="text/javascript" src="javascripts/core/storage/migrations.js"></script>
<script type="text/javascript" src="javascripts/core/storage/migrations.js?1"></script>
<script type="text/javascript" src="javascripts/core/storage/dev-migrations.js"></script>
<script type="text/javascript" src="javascripts/core/notations.js"></script>
<script type="text/javascript" src="javascripts/core/tutorial.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/tabs.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/tabs.js?1"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/tab-notifications.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/news.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/sounds.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/achievements/normal-achievements.js"></script>
@ -149,10 +157,12 @@
<script type="text/javascript" src="javascripts/core/secret-formula/reality/glyph-sacrifices.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/perk-shop.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/effarig.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/enslaved.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/v.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/alchemy.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/compression.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/annihilation-upgrade.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/shop-purchases.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/navigation.js"></script>
<script type="text/javascript" src="javascripts/core/secret-formula/celestials/singularity-milestones.js"></script>
<script type="text/javascript" src="javascripts/components/common/primary-button.js"></script>
<script type="text/javascript" src="javascripts/components/common/description-display.js"></script>
@ -169,7 +179,7 @@
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-amounts-line.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-tickspeed-row.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-gamma-display.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-gamespeed-display.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-big-crunch-button.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-new-dim-button.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/header/game-header-eternity-button.js"></script>
@ -179,30 +189,28 @@
<script type="text/javascript" src="javascripts/components/old-ui/old-ui-subtab-button.js"></script>
<script type="text/javascript" src="javascripts/components/old-ui/old-ui.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-tab-header.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-shift-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-galaxy-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-tab-progress-bar.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/normal/normal-dim-tab.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-tab-header.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-shift-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-galaxy-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-tab-progress-bar.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/antimatter/antimatter-dim-tab.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/infinity/infinity-dim-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/infinity/infinity-dim-tab.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/time/time-dim-row.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/time/time-dim-tab.js"></script>
<script type="text/javascript" src="javascripts/components/dimensions/dim-production-tab.js"></script>
<script type="text/javascript" src="javascripts/components/options/options-tab.js"></script>
<script type="text/javascript" src="javascripts/components/options/options-button-grid.js"></script>
<script type="text/javascript" src="javascripts/components/options/options-saving-tab.js"></script>
<script type="text/javascript" src="javascripts/components/options/options-visual-tab.js"></script>
<script type="text/javascript" src="javascripts/components/options/options-gameplay-tab.js"></script>
<script type="text/javascript" src="javascripts/components/options/select-notation.js"></script>
<script type="text/javascript" src="javascripts/components/options/select-theme.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/statistics-tab.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/challenges/challenge-records-list.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/challenges/challenge-records-tab.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/runs/past-runs-container.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/runs/past-runs-tab.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/runs/past-infinities-tab.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/runs/past-eternities-tab.js"></script>
<script type="text/javascript" src="javascripts/components/statistics/runs/past-realities-tab.js"></script>
<script type="text/javascript" src="javascripts/components/achievements/normal/normal-achievement.js"></script>
<script type="text/javascript" src="javascripts/components/achievements/normal/normal-achievement-row.js"></script>
@ -214,6 +222,7 @@
<script type="text/javascript" src="javascripts/components/celestials/modal-celestial-quote.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/celestial-quote-history.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/navigation.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/teresa/teresa-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/teresa/perk-shop-upgrade.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/effarig/effarig-tab.js"></script>
@ -223,12 +232,16 @@
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-pet.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-pet-level-bar.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-pet-recollection-button.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-upgrade-icon.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/ra-level-chevron.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/alchemy-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/alchemy-resource-info.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/alchemy-circle-node.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/reality-glyph-creation.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/laitela-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/laitela/singularity-milestone.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/laitela/singularity-milestones-modal.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/laitela/laitela-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/pelle-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/matter-dimension-row.js"></script>
@ -239,10 +252,9 @@
<script type="text/javascript" src="javascripts/core/celestials/V.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/ra/ra.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/ra/alchemy.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/ra/compression.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/laitela/laitela.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/laitela/matter_dimension.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/laitela/annihilation-upgrades.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/laitela/singularity.js"></script>
<script type="text/javascript" src="javascripts/core/celestials/celestials.js"></script>
<script type="text/javascript" src="javascripts/components/challenges/challenge-box.js"></script>
@ -276,12 +288,14 @@
<script type="text/javascript" src="javascripts/components/infinity/replicanti/replicanti-gain-text.js"></script>
<script type="text/javascript" src="javascripts/components/infinity/replicanti/replicanti-galaxy-button.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/eternity-points-header.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/rem.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/secret-time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/normal-time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/ec-time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/dilation-time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/triad-time-study.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/time-study-connection.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/time-studies/time-studies-tab.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/upgrades/eternity-upgrade-button.js"></script>
@ -293,9 +307,6 @@
<script type="text/javascript" src="javascripts/components/eternity/dilation/dilation-button.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/dilation/dilation-upgrade.js"></script>
<script type="text/javascript" src="javascripts/components/eternity/dilation/tachyon-particles.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/time-compression-tab.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/compression-upgrade.js"></script>
<script type="text/javascript" src="javascripts/components/celestials/subtabs/ra/compression-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/reality-machines-header.js"></script>
<script type="text/javascript" src="javascripts/components/reality/pp-label.js"></script>
@ -304,19 +315,26 @@
<script type="text/javascript" src="javascripts/components/reality/upgrades/reality-upgrade-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyphs-tab.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/reality-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/reset-reality-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-levels-and-weights.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-component.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-sacrifice-options.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-auto-pick-options.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-selection.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-set-preview.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-sort-options.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-clean-options.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-header.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/equipped-glyphs.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/current-glyph-effects.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/sacrificed-glyphs.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-inventory.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/reality-amplify-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/glyphs/glyph-peek.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-tab.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-animation.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-state-row.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-header-row.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-unlock-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-upgrade-button.js"></script>
<script type="text/javascript" src="javascripts/components/reality/black-hole/black-hole-upgrade-row.js"></script>
@ -337,18 +355,30 @@
<script type="text/javascript" src="javascripts/components/modals/modal-popup.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-shortcuts.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-message.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-ui-choice.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-import.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-import-tree.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-edit-tree.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-load-game.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-std-store.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-h2p.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-progress-bar.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-delete-companion.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-start-normal-challenge.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-start-infinity-challenge.js"></script>
<script type="text/javascript" src="javascripts/components/modals/modal-start-eternity-challenge.js"></script>
<script type="text/javascript" src="javascripts/components/modals/options/modal-options.js"></script>
<script type="text/javascript" src="javascripts/components/modals/options/modal-news-options.js"></script>
<script type="text/javascript" src="javascripts/components/modals/options/modal-animation-options.js"></script>
<script type="text/javascript" src="javascripts/components/modals/options/modal-confirmation-options.js"></script>
<script type="text/javascript" src="javascripts/components/modals/options/modal-info-display-options.js"></script>
<script type="text/javascript" src="javascripts/components/modals/cloud/modal-cloud-conflict-record.js"></script>
<script type="text/javascript" src="javascripts/components/modals/cloud/modal-cloud-load-conflict.js"></script>
<script type="text/javascript" src="javascripts/components/modals/cloud/modal-cloud-save-conflict.js"></script>
<script type="text/javascript" src="javascripts/components/shop/shop-tab.js"></script>
<script type="text/javascript" src="javascripts/components/shop/shop-button.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/sidebar.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/tab-button.js"></script>
<script type="text/javascript" src="javascripts/components/challenges/challenges-header.js"></script>
@ -361,13 +391,11 @@
<script type="text/javascript" src="javascripts/components/new-ui/time-dimensions-tab/new-time-dimension-row.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/time-dimensions-tab/new-time-dimensions-tab.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/dimensions-tab/new-galaxy-row.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/sidebar-resources/sidebar-am.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/sidebar-resources/sidebar-ip.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/sidebar-resources/sidebar-ep.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/sidebar-resources/sidebar-rm.js"></script>
<script type="text/javascript" src="javascripts/components/new-ui/new-ui.js"></script>
<script type="text/javascript" src="javascripts/components/common/news-ticker.js"></script>
<script type="text/javascript" src="javascripts/components/failable-ec-text.js"></script>
<script type="text/javascript" src="javascripts/components/game-ui.js"></script>
<script type="text/javascript" src="javascripts/components/help-me.js"></script>
@ -378,7 +406,6 @@
<script type="text/javascript" src="javascripts/core/automator/compiler.js"></script>
<script type="text/javascript" src="javascripts/core/app/ui.init.js"></script>
<script type="text/javascript" src="javascripts/core/ui/tabs.js"></script>
<script type="text/javascript" src="javascripts/core/app/player-progress.js"></script>
<script type="text/javascript" src="javascripts/core/app/modal.js"></script>
<script type="text/javascript" src="javascripts/core/app/themes.js"></script>
@ -386,13 +413,15 @@
<script type="text/javascript" src="javascripts/core/app/ui.js"></script>
<script type="text/javascript" src="javascripts/core/app/notify.js"></script>
<script type="text/javascript" src="javascripts/core/achievements/normal-achievement.js"></script>
<script type="text/javascript" src="javascripts/core/achievements/secret-achievement.js"></script>
<script type="text/javascript" src="javascripts/core/achievements/achievement-timer.js"></script>
<script type="text/javascript" src="javascripts/core/time.js"></script>
<script type="text/javascript" src="javascripts/core/achievements.js"></script>
<script type="text/javascript" src="javascripts/core/sound.js"></script>
<script type="text/javascript" src="javascripts/core/chart.js"></script>
<script type="text/javascript" src="javascripts/core/tickspeed.js"></script>
<script type="text/javascript" src="javascripts/core/dimensions/dimension.js"></script>
<script type="text/javascript" src="javascripts/core/dimensions/normal-dimension.js"></script>
<script type="text/javascript" src="javascripts/core/dimensions/antimatter-dimension.js"></script>
<script type="text/javascript" src="javascripts/core/dimensions/infinity-dimension.js"></script>
<script type="text/javascript" src="javascripts/core/dimensions/time-dimension.js"></script>
<script type="text/javascript" src="javascripts/core/dimboost.js"></script>
@ -410,6 +439,10 @@
<script type="text/javascript" src="javascripts/core/black_hole.js"></script>
<script type="text/javascript" src="javascripts/core/devtools.js"></script>
<script type="text/javascript" src="javascripts/core/kong.js"></script>
<script type="text/javascript" src="javascripts/core/playfab.js"></script>
<script type="text/javascript" src="javascripts/core/playfab.js?10"></script>
<script type="text/javascript" src="javascripts/core/ui/tabs.js"></script>
<script type="text/javascript" src="javascripts/core/ui/tab-notifications.js"></script>
<script type="text/javascript" src="javascripts/game.js?85"></script>
</html>

View File

@ -19,7 +19,7 @@ Vue.component("normal-achievement-row", {
},
methods: {
update() {
this.isCompleted = this.row.every(a => a.isEnabled);
this.isCompleted = this.row.every(a => a.isUnlocked);
}
},
template:

View File

@ -66,7 +66,7 @@ Vue.component("normal-achievement", {
:ach-tooltip="tooltip"
@mouseenter="onMouseEnter"
@mouseleave="onMouseLeave">
<hint-text class="l-hint-text--achievement">{{id}}</hint-text>
<hint-text type="achievements" class="l-hint-text--achievement">{{id}}</hint-text>
<br>
</div>`
});

View File

@ -3,23 +3,56 @@
Vue.component("normal-achievements-tab", {
data() {
return {
achPower: new Decimal(0),
achievementPower: 0,
achCountdown: 0,
disableAutoAchieve: false,
isCancer: 0
showAutoAchieve: false,
isAutoAchieveActive: false,
isCancer: 0,
achMultToIDS: false,
achMultToTDS: false,
achMultToBH: false,
canSwapImages: false
};
},
computed: {
rows: () => Achievements.rows(1, 15),
rows: () => Achievements.allRows,
swapImagesButton() {
return Theme.current().name === "S4" || this.isCancer ? "😂" : ".";
},
achievementMultiplierText() {
let text = "Current achievement multiplier to ";
if (this.achMultToIDS && this.achMultToTDS && this.achMultToBH)
text += "Black Hole power, Antimatter, Infinity, and Time";
else if (this.achMultToTDS && this.achMultToIDS) text += "Antimatter, Infinity, and Time";
else if (this.achMultToTDS) text += "Antimatter and Time";
else if (this.achMultToIDS) text += "Antimatter and Infinity";
else text += "Antimatter";
text += " Dimensions:";
return text;
},
imageSwapperStyleObject() {
if (this.canSwapImages) {
return { "cursor": "pointer" };
}
return {};
}
},
watch: {
isAutoAchieveActive(newValue) {
player.reality.autoAchieve = newValue;
}
},
methods: {
update() {
this.achPower.copyFrom(Player.achievementPower);
this.achCountdown = Achievements.timeToNextAutoAchieve();
this.achievementPower = Achievements.power;
this.achCountdown = Achievements.timeToNextAutoAchieve() / getGameSpeedupFactor();
this.showAutoAchieve = player.realities > 0 && !Perk.achievementGroup6.isBought;
this.isAutoAchieveActive = player.reality.autoAchieve;
this.isCancer = player.secretUnlocks.cancerAchievements;
this.achMultToIDS = Achievement(75).isUnlocked;
this.achMultToTDS = EternityUpgrade.tdMultAchs.isBought;
this.achMultToBH = V.has(V_UNLOCKS.ACHIEVEMENT_BH);
this.canSwapImages = Themes.available().find(v => v.name === "S4") !== undefined && Theme.current().name !== "S4";
},
timeDisplay(value) {
return timeDisplay(value);
@ -27,29 +60,30 @@ Vue.component("normal-achievements-tab", {
timeDisplayNoDecimals(value) {
return timeDisplayNoDecimals(value);
},
toggleAutoAchieve() {
// Negated because it happens before the v-model
player.reality.disableAutoAchieve = !this.disableAutoAchieve;
},
swapImages() {
if (Themes.available().find(v => v.name === "S4") !== undefined && Theme.current().name !== "S4") {
if (this.canSwapImages) {
player.secretUnlocks.cancerAchievements = !player.secretUnlocks.cancerAchievements;
}
}
},
created() {
this.disableAutoAchieve = player.reality.disableAutoAchieve;
},
template:
`<div>
template: `
<div class="l-achievements-tab">
<div class="c-subtab-option-container" v-if="showAutoAchieve">
<primary-button-on-off
v-model="isAutoAchieveActive"
class="o-primary-btn--subtab-option"
text="Auto achievement:"
/>
</div>
<div class="c-achievements-tab__header">
Current achievement multiplier on each Dimension: {{ shorten(achPower, 2, 3) }}x
<span @click="swapImages()" style="cursor: pointer">{{ swapImagesButton }}</span>
<span>
{{ achievementMultiplierText }} {{ formatX(achievementPower, 2, 3) }}<span
@click="swapImages()" :style="imageSwapperStyleObject">{{ swapImagesButton }}</span>
</span>
</div>
<div v-if="achCountdown > 0" class="c-achievements-tab__header">
Next automatic achievement in {{timeDisplayNoDecimals(achCountdown)}}
<input type="checkbox" name="autoAchieve" v-model="disableAutoAchieve" @click="toggleAutoAchieve()"/>
<label for="autoAchieve">Disable auto achievements</label>
Automatically gain the next missing achievement in {{timeDisplayNoDecimals(achCountdown)}}.
(left-to-right, top-to-bottom)
</div>
<div class="l-achievement-grid">
<normal-achievement-row v-for="(row, i) in rows" :key="i" :row="row" />

View File

@ -18,7 +18,7 @@ Vue.component("secret-achievement-row", {
}
},
created() {
this.on$(GameEvent.ACHIEVEMENT_UNLOCKED, this.updateState);
this.on$(GAME_EVENT.ACHIEVEMENT_UNLOCKED, this.updateState);
this.updateState();
},
methods: {

View File

@ -32,12 +32,15 @@ Vue.component("secret-achievement", {
};
},
tooltip() {
function evaluateText(prop) {
return typeof prop === "function" ? prop() : prop;
}
const config = this.achievement.config;
return this.isUnlocked ? config.tooltip : config.name;
return this.isUnlocked ? evaluateText(config.tooltip) : config.name;
}
},
created() {
this.on$(GameEvent.ACHIEVEMENT_UNLOCKED, this.updateState);
this.on$(GAME_EVENT.ACHIEVEMENT_UNLOCKED, this.updateState);
this.updateState();
},
methods: {
@ -56,6 +59,7 @@ Vue.component("secret-achievement", {
:style="styleObject"
:ach-tooltip="tooltip"
@click="onClick">
<hint-text type="achievements" class="l-hint-text--achievement">S{{row}}{{column}}</hint-text>
<br>
</div>`
});

View File

@ -0,0 +1,542 @@
"use strict";
const BezTestData = {
P0: new Vector(300, 300),
scale: 100,
rate: Math.log(2) / (2 * Math.PI),
t0: 0,
t1: Math.PI / 2,
offset: 10,
};
function cubicBezierArrayToPath(a, initialCommand = "M") {
const prefix = `${initialCommand} ${a[0].p0.x} ${a[0].p0.y}\n`;
const parts = a.map(b => `C ${b.p1.x} ${b.p1.y} ${b.p2.x} ${b.p2.y} ${b.p3.x} ${b.p3.y}\n`);
return prefix + parts.join("");
}
/**
* @param {object} d
* @param {number} d.rMajor
* @param {number} [d.rMinor]
* @param {number} [d.gapCenterDeg]
* @param {number} [d.gapDeg]
* @param {number} [d.gapAngleDeg]
*/
function svgRingPath(d) {
if (!d.gapDeg) {
if (!d.rMinor) {
d.rMinor = 0;
}
return `M -0.1, ${-d.rMajor}
a ${d.rMajor} ${d.rMajor} 0 1 0 0.2 0
z
m 0.2 ${d.rMajor - d.rMinor}
a ${d.rMinor} ${d.rMinor} 0 1 1 -0.2 0
z`;
}
const toRad = Math.PI / 180;
const gapAngleDeg = d.gapAngleDeg === undefined ? d.gapDeg / 2 : d.gapAngleDeg;
const edge0 = toRad * (d.gapCenterDeg + d.gapDeg / 2);
const c0 = Math.cos(edge0), s0 = Math.sin(edge0);
const edge1 = toRad * (d.gapCenterDeg - d.gapDeg / 2);
const c1 = Math.cos(edge1), s1 = Math.sin(edge1);
const x = d.rMajor / d.rMinor * Math.sin(toRad * (d.gapDeg / 2 - gapAngleDeg));
const innerAngle = Math.asin(x) + toRad * gapAngleDeg;
const edge2 = toRad * d.gapCenterDeg + innerAngle;
const c2 = Math.cos(edge2), s2 = Math.sin(edge2);
const edge3 = toRad * d.gapCenterDeg - innerAngle;
const c3 = Math.cos(edge3), s3 = Math.sin(edge3);
const big = d.gapDeg <= 180 ? 1 : 0;
return `M ${c0 * d.rMajor - 1e-3 * s0} ${s0 * d.rMajor + 1e-3 * c0}
A ${d.rMajor} ${d.rMajor} 0 ${big} 1 ${c1 * d.rMajor + 1e-3 * s1} ${s1 * d.rMajor - 1e-3 * c1}
L ${c3 * d.rMinor + 1e-3 * s3} ${s3 * d.rMinor - 1e-3 * c3}
A ${d.rMinor} ${d.rMinor} 0 ${big} 0 ${c2 * d.rMinor - 1e-3 * s2} ${s2 * d.rMinor + 1e-3 * c2}
z`;
}
const CelestialNavigationViewportCache = {
pan: { x: 125, y: 125 },
zoom: 0.75,
};
Vue.component("celestial-navigation", {
components: {
"node-ring": {
props: {
complete: Number,
position: Vector,
ring: Object,
symbol: {
type: String,
default: "",
},
symbolScale: {
type: Number,
default: 1.4
},
symbolOffset: {
type: String,
default: "0"
},
completeClass: String,
incompleteClass: String,
fill: String,
isStacked: {
type: Boolean,
default: false,
},
},
computed: {
baseTransform() {
return this.position.asTranslate();
},
pathData() {
return svgRingPath(this.ring);
},
ringClass() {
return this.complete >= 1 ? this.completeClass : this.incompleteClass;
},
symbolFontSize() {
return this.ring.rMajor * this.symbolScale;
},
ringFilter() {
return this.complete >= 1 && !this.isStacked ? "url(#completeGlow)" : "";
},
},
template: `
<g :transform="baseTransform">
<path :class="ringClass"
:d="pathData"
stroke="none" :fill="fill" :filter="ringFilter" />
<text v-if="symbol"
class="o-celestial-nav__symbol o-no-mouse"
fill="#000"
dominant-baseline="middle"
:font-size="symbolFontSize"
:dy="symbolOffset">{{symbol}}</text>
</g>
`
},
"node-background": {
props: {
position: Vector,
ring: Object,
isStacked: {
type: Boolean,
default: false,
},
},
computed: {
ringBackgroundTransform() {
return this.position.asTranslate();
},
ringBackgroundPath() {
return svgRingPath(this.ring);
},
ringBackgroundFilter() {
return this.isStacked ? "" : "url(#backgroundGlow)";
},
},
template: `
<path :transform="ringBackgroundTransform"
:d="ringBackgroundPath"
fill="rgba(0,0,0,0.75)" stroke="none"
:filter="ringBackgroundFilter" />
`
},
"node-overlay": {
props: {
complete: Number,
position: Vector,
legend: Object,
ring: Object,
fill: String,
alwaysShowLegend: Boolean,
clickAction: Function,
},
computed: {
LEGEND_FONT_SIZE: () => 16,
baseTransform() {
return this.position.asTranslate();
},
pathData() {
return svgRingPath(this.ring);
},
hasLegend() {
return Boolean(this.legend) && (!this.legend.hideWhenCompleted || this.complete < 1);
},
legendArrowPoints() {
const dir = Vector.unitFromDegrees(this.legend.angle);
const pts = [dir.times(this.ring.rMajor + 2)];
pts.push(pts[0].plus(dir.times(this.legend.diagonal)));
pts.push(pts[1].plus(Vector.horiz(this.legend.horizontal * Math.sign(dir.x))));
return pts;
},
legendArrowPointString() {
return this.legendArrowPoints.join(" ");
},
legendTransform() {
const pts = this.legendArrowPoints;
const xDir = Math.sign(pts[2].x - pts[0].x);
return pts[2].plus(Vector.horiz(xDir * 4)).asTranslate();
},
legendTextAnchor() {
const angle = (this.legend.angle + 360) % 360;
return angle > 90 && angle < 270 ? "end" : "start";
},
legendLines() {
const data = typeof (this.legend.text) === "function"
? this.legend.text(this.complete) : this.legend.text;
return typeof (data) === "string" ? [data] : data;
},
forceHoverClass() {
return this.alwaysShowLegend ? "o-celestial-nav__force-hover" : "";
},
},
methods: {
legendLineY(idx) {
const spacing = Math.round(this.LEGEND_FONT_SIZE * 1.25 / 2);
const num = this.legendLines.length;
return (2 * idx - (num - 1)) * spacing;
}
},
template: `
<g class="o-celestial-nav__hoverable" :class="forceHoverClass"
:transform="baseTransform"
v-on="clickAction ? { click: clickAction } : {}">
<path :d="pathData" class="o-celestial-nav__node-overlay" />
<g v-if="hasLegend" class="tooltiptext">
<polyline :points="legendArrowPointString"
class="o-celestial-nav__legend-arrow"/>
<!-- The ring radii are adjusted slightly to offset the stroke outside the node -->
<path :d="pathData" class="o-celestial-nav__legend-outline" />
<g :transform="legendTransform">
<text class="o-celestial-nav__legend-text"
:text-anchor="legendTextAnchor"
dominant-baseline="middle" :font-size="LEGEND_FONT_SIZE">
<tspan v-for="(line, idx) in legendLines" :key="idx"
x="0" :y="legendLineY(idx)">{{line}}</tspan>
</text>
</g>
</g>
</g>
`
},
"progress-connector": {
props: {
complete: Number,
completeWidth: {
type: Number,
default: 8
},
incompleteWidth: {
type: Number,
default: 6,
},
fill: {
type: String,
default: "#5151ec",
},
filterName: {
type: String,
default: "completeGlow",
},
path: Curve,
pathStart: Number,
pathEnd: Number,
pathPadStart: {
type: Number,
default: 0,
},
pathPadEnd: {
type: Number,
default: 0,
},
},
computed: {
unpaddedSpan() {
return (this.pathEnd - this.pathPadEnd) - (this.pathStart + this.pathPadStart);
},
incompleteStart() {
return this.complete >= 1
? this.pathEnd
: this.pathStart + this.pathPadStart + this.unpaddedSpan * this.complete;
},
incompleteStartShape() {
return this.shapeAt(this.incompleteStart);
},
completeStartShape() {
return this.shapeAt(this.pathStart);
},
incompleteTransform() {
const shape = this.incompleteStartShape;
return `${shape.position.asTranslate()} ${shape.direction.asRotate()}`;
},
pathEndShape() {
return this.shapeAt(this.pathEnd);
},
// In order to support gradients that fill along a completed path,
// we render in a coordinate system that's scaled to be 0..1 from start to end
totalPathOffsetPx() {
return this.pathEndShape.position.minus(this.completeStartShape.position);
},
completeTransform() {
const shape = this.completeStartShape;
const scale = this.totalPathOffsetPx.length;
return `${shape.position.asTranslate()} ${shape.direction.asRotate()} scale(${scale})`;
},
incompleteFadeEnd() {
const shape = this.incompleteStartShape;
const fadeLength = 12 / shape.derivative.length;
return this.pathEnd > this.pathStart
? Math.min(this.incompleteStart + fadeLength, this.pathEnd)
: Math.max(this.incompleteStart - fadeLength, this.pathEnd);
},
incompleteFadePath() {
return this.generateIncompletePath(this.incompleteStart, this.incompleteFadeEnd);
},
incompleteSolidPath() {
return this.generateIncompletePath(
this.incompleteFadeEnd - 1e-3 * (this.pathEnd - this.incompleteFadeEnd), this.pathEnd);
},
completePath() {
const startShape = this.completeStartShape;
const scale = 1 / this.totalPathOffsetPx.length;
const tform = AffineTransform
.translation(startShape.position.negative)
.rotated(-startShape.direction.angle)
.scaled(scale);
const tStart = this.pathStart, tEnd = this.incompleteStart;
const w = this.completeWidth;
const insetPath = this.getOffsetPath(-w / 2, tStart, tEnd).transformedBy(tform);
const outsetPath = this.getOffsetPath(w / 2, tEnd, tStart).transformedBy(tform);
const endVector = this.incompleteStartShape.direction.transformedBy(tform.withoutTranslation);
const inEnd = insetPath.path[insetPath.path.length - 1];
const outStart = outsetPath.path[0];
const capCP0 = inEnd.position(1).plus(endVector.times(w / 2));
const capCP1 = outStart.position(0).plus(endVector.times(w / 2));
const cap = `C ${capCP0.x} ${capCP0.y} ${capCP1.x} ${capCP1.y} ${outStart.p0.x} ${outStart.p0.y}\n`;
return insetPath.toSVG("M") + cap + outsetPath.toSVG("L");
},
hasIncompleteSolidPath() {
return this.incompleteFadeEnd !== this.pathEnd;
},
filter() {
return `url(#${this.filterName})`;
},
},
methods: {
generateIncompletePath(tStart, tEnd) {
const inset = this.getOffsetPath(-this.incompleteWidth / 2, tStart, tEnd);
const outset = this.getOffsetPath(this.incompleteWidth / 2, tEnd, tStart);
const s0 = this.incompleteStartShape;
const tform = AffineTransform.translation(s0.position.negative).rotated(-s0.direction.angle);
return inset.transformedBy(tform).toSVG("M") + outset.transformedBy(tform).toSVG("L");
},
getOffsetPath(offset, tStart, tEnd) {
if (this.path instanceof LinearPath) {
return new PiecewisePath([this.path.createOffsetLine(offset, tStart, tEnd)]);
}
const offsetPath = new OffsetCurve(this.path, offset);
return PiecewisePath.cubicBezierFitToCurveSection(offsetPath, tStart, tEnd);
},
shapeAt(t) {
const shape = this.path.shapeAt(t);
if (this.pathStart > this.pathEnd) {
shape.direction = shape.direction.negative;
shape.derivative = shape.derivative.negative;
}
return shape;
}
},
template: `
<g>
<g :transform="incompleteTransform">
<path :d="incompleteFadePath"
fill="url(#incompleteFade)" />
<path v-if="hasIncompleteSolidPath"
:d="incompleteSolidPath"
fill="#888" />
</g>
<g :filter="filter">
<path :transform="completeTransform"
:fill="fill" stroke="none" :d="completePath" />
</g>
</g>
`
},
},
data: () => ({
nodeState: Object.keys(GameDatabase.celestials.navigation).mapToObject(
name => name,
() => ({
visible: false,
complete: 0,
})
),
}),
computed: {
db: () => GameDatabase.celestials.navigation,
drawOrder: () => {
const db = GameDatabase.celestials.navigation;
const order = [];
for (const nodeId of Object.keys(db)) {
const node = db[nodeId];
if (node.connector) {
order.push({
nodeId,
is: "progress-connector",
config: node.connector,
drawOrder: node.connector.drawOrder || CELESTIAL_NAV_DRAW_ORDER.CONNECTORS,
});
}
if (node.node) {
order.push({
nodeId,
is: "node-background",
config: node.node,
drawOrder: node.node.bgDrawOrder || CELESTIAL_NAV_DRAW_ORDER.NODE_BG,
});
order.push({
nodeId,
is: "node-ring",
config: node.node,
drawOrder: node.node.drawOrder || CELESTIAL_NAV_DRAW_ORDER.NODES,
});
order.push({
nodeId,
is: "node-overlay",
config: node.node,
drawOrder: node.node.overlayDrawOrder || CELESTIAL_NAV_DRAW_ORDER.NODE_OVERLAYS,
});
}
order.sort((a, b) => a.drawOrder - b.drawOrder);
}
return order;
}
},
mounted() {
// eslint-disable-next-line no-unused-vars
const panLimiter = function(oldPan, newPan) {
// In the callback context, "this" is the svgPanZoom object.
// eslint-disable-next-line no-invalid-this
const sizes = this.getSizes();
const leftLimit = sizes.width - ((sizes.viewBox.x + sizes.viewBox.width) * sizes.realZoom);
const rightLimit = -sizes.viewBox.x * sizes.realZoom;
const topLimit = sizes.height - ((sizes.viewBox.y + sizes.viewBox.height) * sizes.realZoom);
const bottomLimit = -sizes.viewBox.y * sizes.realZoom;
return {
x: Math.max(leftLimit, Math.min(rightLimit, newPan.x)),
y: Math.max(topLimit, Math.min(bottomLimit, newPan.y))
};
};
this.panZoom = svgPanZoom(this.$el, {
controlIconsEnabled: true,
dblClickZoomEnabled: false,
center: false,
fit: false,
zoomScaleSensitivity: 0.3,
minZoom: 0.64,
maxZoom: 3,
beforePan: panLimiter,
});
if (CelestialNavigationViewportCache.pan) this.panZoom.pan(CelestialNavigationViewportCache.pan);
if (CelestialNavigationViewportCache.zoom) this.panZoom.zoom(CelestialNavigationViewportCache.zoom);
},
beforeDestroy() {
if (this.panZoom) {
CelestialNavigationViewportCache.zoom = this.panZoom.getZoom();
CelestialNavigationViewportCache.pan = this.panZoom.getPan();
this.panZoom.destroy();
delete this.panZoom;
}
},
methods: {
update() {
for (const key of Object.keys(this.db)) {
this.nodeState[key].visible = this.db[key].visible();
this.nodeState[key].complete = this.db[key].complete();
}
},
vec(x, y) {
return new Vector(x, y);
},
nodeVisibility(obj) {
return this.nodeState[obj.nodeId].visible ? "visible" : "hidden";
},
},
template: `
<svg height="600" width="960" class="l-celestial-navigation">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="incompleteFade" x1="0" x2="8" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color: #888; stop-opacity: 0"/>
<stop offset="8" style="stop-color: #888; stop-opacity: 1.0"/>
</linearGradient>
<linearGradient id="fadeGrad" y2="0" x2="1">
<stop offset="0.5" stop-color="white" stop-opacity="0"/>
<stop offset="1" stop-color="white" stop-opacity=".5"/>
</linearGradient>
<linearGradient id="gradTeresaEffarig" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#5151ec"/>
<stop offset="1" stop-color="#d13737"/>
</linearGradient>
<linearGradient id="gradEffarigEnslaved" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#d13737"/>
<stop offset="1" stop-color="#ffa337"/>
</linearGradient>
<linearGradient id="gradEnslavedV" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ffa337"/>
<stop offset="1" stop-color="#ffe066"/>
</linearGradient>
<linearGradient id="gradRaTeresa" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9063de"/>
<stop offset="1" stop-color="#5151ec"/>
</linearGradient>
<linearGradient id="gradRaEffarig" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9063de"/>
<stop offset="1" stop-color="#d13737"/>
</linearGradient>
<linearGradient id="gradRaEnslaved" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9063de"/>
<stop offset="1" stop-color="#ffa337"/>
</linearGradient>
<linearGradient id="gradRaV" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9063de"/>
<stop offset="1" stop-color="#ffe066"/>
</linearGradient>
<linearGradient id="gradRaLaitela" y2="0" x2="1" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#9063de"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<mask id="fade" maskContentUnits="objectBoundingBox">
<rect width="1" height="1" fill="url(#fadeGrad)"/>
</mask>
<filter id="completeGlow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" result="blurred" stdDeviation="2" />
<feMerge>
<feMergeNode in="blurred" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="backgroundGlow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceGraphic" result="blurred" stdDeviation="4" />
<feMerge>
<feMergeNode in="blurred" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<image x="-250" y="-350" height="1503" width="1503" href="images/celestial-navigation-bg.png" />
<g v-for="(obj, index) in drawOrder" :key="index" :visibility="nodeVisibility(obj)">
<component :is="obj.is"
:complete="nodeState[obj.nodeId].complete"
v-bind="obj.config" />
</g>
</svg>
`
});

View File

@ -38,21 +38,20 @@ Vue.component("effarig-tab", {
data() {
return {
relicShards: 0,
shardRarityBoost: 0,
shardsGained: 0,
autosacrificeUnlocked: false,
adjusterUnlocked: false,
autopickerUnlocked: false,
runUnlocked: false,
quote: "",
quoteIdx: 0,
isRunning: false,
vIsFlipped: false,
relicShardRarityAlwaysMax: false
};
},
computed: {
shopUnlocks: () => [
EffarigUnlock.adjuster,
EffarigUnlock.autosacrifice,
EffarigUnlock.autopicker
EffarigUnlock.basicFilter,
EffarigUnlock.advancedFilter
],
runUnlock: () => EffarigUnlock.run,
runUnlocks: () => [
@ -69,52 +68,75 @@ Vue.component("effarig-tab", {
},
runDescription() {
return this.isRunning
? `All dimension multipliers, gamespeed, and tickspeed are severely lowered, like dilation. Infinity power
? `All dimension multipliers, gamespeed, and tickspeed are severely lowered, like Dilation. Infinity power
reduces the production and gamespeed penalties and time shards reduce the tickspeed penalty. Glyph levels
are temporarily capped to ${Effarig.glyphLevelCap}, rarity is unaffected.`
are temporarily capped to ${Effarig.glyphLevelCap}, rarity is unaffected. You will exit Effarig's Reality
when you complete a Layer of it for the first time.`
: `Start Effarig's Reality; all dimension multipliers, gamespeed, and tickspeed are severely lowered, like
dilation. Infinity power reduces the production and gamespeed penalties and time shards reduce the tickspeed
penalty. Glyph levels are temporarily capped, rarity is unaffected.`;
Dilation. Infinity power reduces the production and gamespeed penalties and time shards reduce the tickspeed
penalty. Glyph levels are temporarily capped, rarity is unaffected. You will exit Effarig's Reality when you
complete a Layer of it for the first time.`;
}
},
methods: {
update() {
this.relicShards = player.celestials.effarig.relicShards;
this.shardRarityBoost = Effarig.maxRarityBoost;
this.shardsGained = Effarig.shardsGained;
this.quote = Effarig.quote;
this.quoteIdx = player.celestials.effarig.quoteIdx;
this.runUnlocked = EffarigUnlock.run.isUnlocked;
this.autosacrificeUnlocked = EffarigUnlock.autosacrifice.isUnlocked;
this.adjusterUnlocked = EffarigUnlock.adjuster.isUnlocked;
this.autopickerUnlocked = EffarigUnlock.autopicker.isUnlocked;
this.isRunning = Effarig.isRunning;
this.vIsFlipped = V.isFlipped;
this.relicShardRarityAlwaysMax = Ra.has(RA_UNLOCKS.EXTRA_CHOICES_AND_RELIC_SHARD_RARITY_ALWAYS_MAX);
},
startRun() {
if (this.isRunning) startRealityOver();
else Effarig.startRun();
if (!resetReality()) return;
Effarig.initializeRun();
},
nextQuote() {
Effarig.nextQuote();
},
hasNextQuote() {
return this.quoteIdx < Effarig.maxQuoteIdx;
createCursedGlyph() {
if (Glyphs.freeInventorySpace === 0) {
Modal.message.show("Inventory cannot hold new glyphs. Delete/sacrifice (shift-click) some glyphs.");
return;
}
const cursedCount = player.reality.glyphs.active
.concat(player.reality.glyphs.inventory)
.filter(g => g !== null && g.type === "cursed")
.length;
if (cursedCount >= 5) {
GameUI.notify.error("You don't need any more cursed glyphs!");
} else {
Glyphs.addToInventory(GlyphGenerator.cursedGlyph());
GameUI.notify.error("Created a cursed glyph");
}
this.emitClose();
}
},
template:
`<div class="l-teresa-celestial-tab">
template: `
<div class="l-teresa-celestial-tab">
<celestial-quote-history celestial="effarig"/>
<div class="c-effarig-relics">You have {{ shorten(relicShards, 2, 0) }} Relic Shards.</div>
<div class="c-effarig-relic-description">
You will gain {{ shorten(shardsGained, 2, 0) }} Relic Shards next reality. More EP slightly increases <br>
shards gained. More distinct glyph effects significantly increases shards gained.
</div>
<div class="l-effarig-shop-and-run">
<div class="l-effarig-shop">
<div class="c-effarig-relics">
You have {{ format(relicShards, 2, 0) }} Relic Shards, which increases <br>
the rarity of new glyphs by {{ relicShardRarityAlwaysMax ? "" : "up to" }}
+{{ format(shardRarityBoost, 2, 2) }}%.
</div>
<div class="c-effarig-relic-description">
You will gain {{ format(shardsGained, 2, 0) }} Relic Shards next reality. More EP slightly increases <br>
shards gained. More distinct glyph effects significantly increases shards gained.
</div>
<effarig-unlock-button
v-for="(unlock, i) in shopUnlocks"
:key="i"
:unlock="unlock" />
<effarig-unlock-button v-if="!runUnlocked" :unlock="runUnlock" />
<button
class="c-effarig-shop-button c-effarig-shop-button--available"
@click="createCursedGlyph"
v-if="vIsFlipped"
>
Get a cursed glyph...
</button>
</div>
<div v-if="runUnlocked" class="l-effarig-run">
<div class="c-effarig-run-description">
@ -133,4 +155,4 @@ Vue.component("effarig-tab", {
</div>
</div>
</div>`
});
});

View File

@ -4,14 +4,16 @@ Vue.component("effarig-unlock-button", {
props: {
unlock: Object
},
data: function() {
data() {
return {
isBought: false
isBought: false,
isAvailable: false
};
},
methods: {
update() {
this.isBought = this.unlock.isUnlocked;
this.isAvailable = Effarig.shardAmount >= this.unlock.cost;
},
purchase() {
this.unlock.purchase();
@ -20,14 +22,17 @@ Vue.component("effarig-unlock-button", {
computed: {
config() {
return this.unlock.config;
},
classObject() {
return {
"c-effarig-shop-button": true,
"c-effarig-shop-button--bought": this.isBought,
"c-effarig-shop-button--available": this.isAvailable && !this.isBought
};
}
},
template: `
<button
class="o-effarig-shop-button"
:class="{ 'effarig-unlock-bought': isBought }"
@click="purchase"
>
<button :class="classObject" @click="purchase">
<description-display :config="config"/>
<cost-display
v-if="!isBought"

View File

@ -1,14 +1,140 @@
"use strict";
Vue.component("modal-enslaved-hints", {
data() {
return {
currentStored: 0,
nextHintCost: 0,
canGetHint: false,
shownEntries: [],
realityHintsLeft: 0,
glyphHintsLeft: 0,
hints: 0,
};
},
methods: {
update() {
this.currentStored = player.celestials.enslaved.stored;
this.nextHintCost = Enslaved.nextHintCost;
this.canGetHint = this.currentStored >= this.nextHintCost;
this.shownEntries = [];
this.realityHintsLeft = Object.values(EnslavedProgress).length;
for (const prog of Object.values(EnslavedProgress)) {
if (prog.hasHint) {
this.shownEntries.push([prog.hasProgress
? prog.config.progress
: "(You haven't figured this hint out yet)", prog.config.hint]);
this.realityHintsLeft--;
}
}
const glyphHintCount = player.celestials.enslaved.glyphHintsGiven;
for (let hintNum = 0; hintNum < glyphHintCount; hintNum++) {
this.shownEntries.push(["", GameDatabase.celestials.enslaved.glyphHints[hintNum]]);
}
this.glyphHintsLeft = GameDatabase.celestials.enslaved.glyphHints.length - glyphHintCount;
this.hints = Enslaved.hintCostIncreases;
},
giveRealityHint(available) {
if (available <= 0 || !Enslaved.spendTimeForHint()) return;
Object.values(EnslavedProgress).filter(prog => !prog.hasHint).randomElement().giveHint();
},
giveGlyphHint(available) {
if (available <= 0 || !Enslaved.spendTimeForHint()) return;
player.celestials.enslaved.glyphHintsGiven++;
}
},
computed: {
hintCost() {
return `${format(TimeSpan.fromMilliseconds(this.nextHintCost).totalYears, 2)} years`;
},
hasProgress(id) {
return this.progressEntries.some(entry => entry.id === id);
},
// Note: This calculation seems to behave extremely poorly if the goal has been raised more than 12 hints worth
// of cost bumps and I'm not entirely sure why. There's probably a numerical issue I can't quite figure out, but
// considering that much cost raising can't happen in practice I think I'm just going to leave it be.
timeEstimate() {
if (this.currentStored >= this.nextHintCost) return "";
// Relevant values are stored as milliseconds, so multiply the rate by 1000 to get to seconds
const storeRate = 1000 * (Enslaved.isStoringGameTime
? Enslaved.currentBlackHoleStoreAmountPerMs
: getGameSpeedupFactor());
const alreadyWaited = this.currentStored / storeRate;
const decaylessTime = this.nextHintCost / storeRate;
// Check if decay is irrelevant and don't do the hard calculations if so
const minCostEstimate = (TimeSpan.fromYears(1e40).totalMilliseconds - this.currentStored) / storeRate;
if (TimeSpan.fromSeconds(minCostEstimate).totalDays > this.hints) {
return `${TimeSpan.fromSeconds(minCostEstimate).toStringShort(true)}`;
}
// Decay is 3x per day, but the math needs decay per second
const K = Math.pow(3, 1 / 86400);
const x = decaylessTime * Math.log(K) * Math.pow(K, alreadyWaited);
const timeToGoal = productLog(x) / Math.log(K) - alreadyWaited;
return `${TimeSpan.fromSeconds(timeToGoal).toStringShort(true)}`;
}
},
template: `
<div class="c-reality-glyph-creation">
<modal-close-button @click="emitClose"/>
<div>
This Reality seems to be resisting your efforts to complete it. So far you have done the following:
</div><br>
<div v-for="entry in shownEntries">
<div v-if="entry[0]">
<b>{{ entry[0] }}</b>
<br>
- {{ entry[1] }}
</div>
<div v-else>
* <i>Glyph hint: {{ entry[1] }}</i>
</div>
<br>
</div>
<div v-if="realityHintsLeft + glyphHintsLeft > 0">
You can spend some time looking for some more cracks in the Reality, but every hint you spend time on
will increase the time needed for the next by a factor of {{ formatInt(3) }}. This cost bump will
gradually go away over {{ formatInt(24) }} hours and figuring out what the hint means will immediately
divide the cost by {{ formatInt(2) }}. The cost can't be reduced below {{ format(1e40) }} years.
<br>
<br>
The next hint requires {{ hintCost }} stored in your Black Hole, which will be used up.
<span v-if="currentStored < nextHintCost">
You will reach this if you charge your Black Hole for {{ timeEstimate }}.
</span>
<br>
<br>
<button class="o-primary-btn"
:class="{ 'o-primary-btn--disabled': realityHintsLeft <= 0 || !canGetHint }"
v-on:click="giveRealityHint(realityHintsLeft)">
Get a hint about the Reality itself ({{ formatInt(realityHintsLeft) }} left)
</button>
<br>
<button class="o-primary-btn"
:class="{ 'o-primary-btn--disabled': glyphHintsLeft <= 0 || !canGetHint }"
v-on:click="giveGlyphHint(glyphHintsLeft)">
Get a hint on what glyphs to use ({{ formatInt(glyphHintsLeft) }} left)
</button>
</div>
<div v-else>
There are no more hints left!
</div>
</div>`,
});
Vue.component("enslaved-tab", {
data: () => ({
isStoringBlackHole: false,
isStoringReal: false,
autoStoreReal: false,
hasAmplifyStoredReal: false,
canAdjustStoredTime: false,
storedFraction: 0,
inEnslaved: false,
isRunning: false,
completed: false,
storedBlackHole: 0,
storedReal: 0,
@ -17,14 +143,12 @@ Vue.component("enslaved-tab", {
autoRelease: false,
autoReleaseSpeed: 0,
unlocks: [],
buyableUnlocks: [],
quote: "",
quoteIdx: 0,
currentSpeedUp: 0
currentSpeedUp: 0,
hintsUnlocked: false
}),
computed: {
amplifiedGameDesc() {
return `${formatPow(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.gameTimeAmplification(), 2, 2)}`;
},
storedRealEfficiencyDesc() {
return formatPercents(this.storedRealEffiency);
},
@ -38,10 +162,13 @@ Vue.component("enslaved-tab", {
return Enslaved.storedTimeInsideEnslaved(this.storedBlackHole);
},
realityTitle() {
return this.inEnslaved
return this.isRunning
? "You're inside Enslaved Ones' Reality"
: "Start Enslaved One's Reality";
},
storedTimeRate() {
return formatPercents(this.storedFraction / 1000, 1);
},
sliderProps() {
return {
min: 0,
@ -51,28 +178,39 @@ Vue.component("enslaved-tab", {
width: "60rem",
tooltip: false
};
},
runButtonClassObject() {
return {
"c-enslaved-run-button__icon": true,
"c-enslaved-run-button__icon--running": this.isRunning,
};
}
},
watch: {
autoRelease(newValue) {
player.celestials.enslaved.isAutoReleasing = newValue;
}
},
methods: {
update() {
this.isStoringBlackHole = player.celestials.enslaved.isStoring;
this.isStoringBlackHole = Enslaved.isStoringGameTime;
this.storedBlackHole = player.celestials.enslaved.stored;
this.isStoringReal = player.celestials.enslaved.isStoringReal;
this.isStoringReal = Enslaved.isStoringRealTime;
this.autoStoreReal = player.celestials.enslaved.autoStoreReal;
this.hasAmplifyStoredReal = Ra.has(RA_UNLOCKS.IMPROVED_STORED_TIME);
this.canAdjustStoredTime = Ra.has(RA_UNLOCKS.ADJUSTABLE_STORED_TIME);
this.inEnslaved = Enslaved.isRunning;
this.isRunning = Enslaved.isRunning;
this.completed = Enslaved.isCompleted;
this.storedReal = player.celestials.enslaved.storedReal;
this.storedRealEffiency = Enslaved.storedRealTimeEfficiency;
this.storedRealCap = Enslaved.storedRealTimeCap;
this.unlocks = Array.from(player.celestials.enslaved.unlocks);
this.buyableUnlocks = Object.values(ENSLAVED_UNLOCKS).map(x => Enslaved.canBuy(x));
this.quote = Enslaved.quote;
this.quoteIdx = player.celestials.enslaved.quoteIdx;
this.storedFraction = 1000 * player.celestials.enslaved.storedFraction;
this.autoRelease = player.celestials.enslaved.isAutoReleasing;
this.autoReleaseSpeed = Enslaved.isAutoReleasing ? Enslaved.autoReleaseSpeed : 0;
this.currentSpeedUp = Enslaved.currentBlackHoleStoreAmountPerMs;
this.hintsUnlocked = EnslavedProgress.hintsUnlocked.hasProgress;
},
toggleStoreBlackHole() {
Enslaved.toggleStoreBlackHole();
@ -96,19 +234,19 @@ Vue.component("enslaved-tab", {
Enslaved.buyUnlock(info);
},
startRun() {
Enslaved.startRun();
// This needs to be added here before the reset so that TD autobuyers don't buy too much on start
player.celestials.enslaved.run = true;
if (!resetReality()) return;
Enslaved.initializeRun();
},
hasUnlock(info) {
return Enslaved.has(info);
},
canBuyUnlock(info) {
return Enslaved.canBuy(info);
},
nextQuote() {
Enslaved.nextQuote();
},
hasNextQuote() {
return this.quoteIdx < Enslaved.maxQuoteIdx;
// This (rather than just using Enslaved.canBuy(info) and removing this.buyableUnlocks)
// is needed for proper reactivity of button styles (e.g., if you get a level 5000 glyph
// while on the Enslaved tab).
return this.buyableUnlocks[info.id];
},
unlockClassObject(info) {
return {
@ -120,9 +258,6 @@ Vue.component("enslaved-tab", {
this.storedFraction = value;
player.celestials.enslaved.storedFraction = value / 1000;
},
toggleAutoRelease() {
player.celestials.enslaved.isAutoReleasing = !player.celestials.enslaved.isAutoReleasing;
},
glitchStyle(x) {
const xScale = 15 / 27;
const yScale = 5;
@ -135,95 +270,111 @@ Vue.component("enslaved-tab", {
};
}
},
template:
`<div class="l-enslaved-celestial-tab">
<celestial-quote-history celestial="enslaved"/>
<div class="l-enslaved-top-container">
<div class="l-enslaved-top-container__half">
Charging your black hole
{{ canAdjustStoredTime ? "reduces your black hole speed" : "sets your game speed to 1" }}. You can use
time from charging to unlock Enslaved upgrades. You can also discharge it all in a single "supertick"
which acts as if it was the duration of all of your stored time.
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': isStoringBlackHole }]"
@click="toggleStoreBlackHole">
<div class="o-enslaved-stored-time">{{ timeDisplayShort(storedBlackHole) }}</div>
<div>{{ isStoringBlackHole ? "Charging black hole": "Charge black hole" }}</div>
</button>
<button class="o-enslaved-mechanic-button" @click="useStored">
Discharge black hole
<p v-if="inEnslaved">{{timeDisplayShort(nerfedBlackHoleTime)}} in this reality</p>
</button>
<div v-if="hasAmplifyStoredReal"> Amplified: {{ amplifiedGameDesc }} </div>
</div>
<div class="l-enslaved-top-container__half">
Storing real time completely halts all production, setting game speed to 0. You can use stored real time to
"amplify" a reality, simulating repeated runs of it. Amplified realities give all the rewards that normal
realities do.
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': isStoringReal}]"
@click="toggleStoreReal">
<div class="o-enslaved-stored-time">{{ timeDisplayShort(storedReal) }}</div>
<div>{{ isStoringReal ? "Storing real time": "Store real time" }}</div>
</button>
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': autoStoreReal}]"
@click="toggleAutoStoreReal">
<div>{{ autoStoreReal ? "Offline time stored": "Offline time used for production" }}</div>
</button>
<div> Efficiency: {{ storedRealEfficiencyDesc }} </div>
<div> Maximum: {{ storedRealCapDesc }} </div>
</div>
</div>
<div v-if="canAdjustStoredTime" class="l-enslaved-shop-container">
<ad-slider-component
v-bind="sliderProps"
:value="storedFraction"
@input="adjustSlider($event)"
/>
</div>
<br>
<div v-if="canAdjustStoredTime">
<input type="checkbox"
id="autoReleaseBox"
template: `
<div class="l-enslaved-celestial-tab">
<div class="c-subtab-option-container" v-if="canAdjustStoredTime">
<primary-button-on-off
v-model="autoRelease"
:value="autoRelease"
@input="toggleAutoRelease()">
<label for="autoReleaseBox">Pulse black hole (uses 1% every 5 ticks)</label>
class="o-primary-btn--subtab-option"
text="Pulse Black Hole:"
/>
</div>
<div class="l-enslaved-shop-container">
<button
v-for="unlock in unlocksInfo"
:key="unlock.id"
class="o-enslaved-shop-button"
:class="unlockClassObject(unlock)"
@click="buyUnlock(unlock)">
{{ unlock.description }} <br>
Costs: {{ timeDisplayShort(unlock.price) }}<br>
<span v-if="isStoringBlackHole && !hasUnlock(unlock)">Time to obtain:
{{ timeDisplayShort(timeUntilBuy(unlock.price)) }}</span>
</button>
</div>
<div class="l-enslaved-unlocks-container" v-if="hasUnlock(unlocksInfo.RUN)">
<div class="o-enslaved-run-box">
<div class="o-enslaved-run-box__title">{{realityTitle}}</div>
<div v-if="completed"><b>(Completed)</b></div>
<div class="o-enslaved-run-button" @click="startRun">
<div class="o-enslaved-run-button__sigil fas fa-link" />
<div v-for="x in 25" class="o-enslaved-run-button__glitch"
:style="glitchStyle(x)"/>
<div class="l-enslaved-celestial-tab--inner">
<div class="l-enslaved-run-container">
<div v-if="hasUnlock(unlocksInfo.RUN)">
<div class="c-enslaved-run-button">
<div class="c-enslaved-run-button__title">{{realityTitle}}</div>
<div v-if="completed"><b>(Completed)</b></div>
<div :class="runButtonClassObject" @click="startRun">
<div class="c-enslaved-run-button__icon__sigil fas fa-link" />
<div v-if="isRunning" v-for="x in 25" class="c-enslaved-run-button__icon__glitch"
:style="glitchStyle(x)"/>
</div>
<p>Glyph levels will be boosted to a minimum of {{ formatInt(5000) }}</p>
<p>Infinity, Time, and 8th Antimatter Dimension purchases are limited to {{ formatInt(1) }} each</p>
<p>Antimatter Dimension multipliers are always Dilated (the glyph effect still only
applies in actual Dilation)</p>
<p>Time study 192 (uncapped Replicanti) is locked</p>
<p>The Black Hole is disabled</p>
<p>Tachyon production and Dilated Time production are severely reduced</p>
<p>Time Theorem generation from Dilation glyphs is disabled</p>
<p>Certain challenge goals have been increased</p>
<p>Stored time is effectively Dilated (exponent^{{ format(0.55, 2, 2) }})</p>
<b>Reward: Unlock Tesseracts, which let you increase Infinity Dimension caps
(see Infinity Dimension tab)</b>
</div>
</div>
</div>
<div class="l-enslaved-upgrades-column">
<celestial-quote-history celestial="enslaved"/>
<primary-button
v-if="hintsUnlocked"
class="o-primary-btn"
onclick="Modal.enslavedHints.show()">
Examine the Reality more closely...
</primary-button>
<div class="l-enslaved-top-container">
<div class="l-enslaved-top-container__half">
While charging, the Black Hole's speed boost is {{ canAdjustStoredTime ? "decreased" : "disabled" }},
and the lost speed is converted into stored time. Discharging the Black Hole allows you to skip
forward in time. Stored time is also used to unlock certain upgrades.
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': isStoringBlackHole }]"
@click="toggleStoreBlackHole">
<div class="o-enslaved-stored-time">{{ timeDisplayShort(storedBlackHole) }}</div>
<div>{{ isStoringBlackHole ? "Charging Black Hole": "Charge Black Hole" }}</div>
</button>
<button class="o-enslaved-mechanic-button" @click="useStored">
Discharge Black Hole
<p v-if="isRunning">{{timeDisplayShort(nerfedBlackHoleTime)}} in this reality</p>
</button>
</div>
<div class="l-enslaved-top-container__half">
Storing real time completely halts all production, setting game speed to {{ formatInt(0) }}.
You can use stored real time to "amplify" a reality, simulating repeated runs of it.
Amplified realities give all the rewards that normal realities do.
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': isStoringReal}]"
@click="toggleStoreReal">
<div class="o-enslaved-stored-time">{{ timeDisplayShort(storedReal) }}</div>
<div>{{ isStoringReal ? "Storing real time": "Store real time" }}</div>
</button>
<button :class="['o-enslaved-mechanic-button',
{'o-enslaved-mechanic-button--storing-time': autoStoreReal}]"
@click="toggleAutoStoreReal">
<div>{{ autoStoreReal ? "Offline time stored": "Offline time used for production" }}</div>
</button>
<div> Efficiency: {{ storedRealEfficiencyDesc }} </div>
<div> Maximum stored real time: {{ storedRealCapDesc }} </div>
</div>
</div>
<div v-if="canAdjustStoredTime" class="l-enslaved-top-container__half">
Black Hole charging rate: {{ storedTimeRate }}
<br>
<br>
<ad-slider-component
v-bind="sliderProps"
:value="storedFraction"
@input="adjustSlider($event)"
/>
</div>
<br>
<div class="l-enslaved-shop-container">
<button
v-for="unlock in unlocksInfo"
:key="unlock.id"
class="o-enslaved-shop-button"
:class="unlockClassObject(unlock)"
@click="buyUnlock(unlock)">
{{ unlock.description() }}
<br>
Costs: {{ timeDisplayShort(unlock.price) }}
<br>
<span v-if="isStoringBlackHole && !hasUnlock(unlock)">
Time to obtain: {{ timeDisplayShort(timeUntilBuy(unlock.price)) }}
</span>
</button>
</div>
<p>Glyph levels will be boosted to a minimum of 5000</p>
<p>Infinity, time, and 8th dimension purchases are limited to 1 each.</p>
<p>Normal dimension multipliers are always dilated (the glyph effect still only
applies in actual dilation)</p>
<p>Time study 192 (uncapped replicanti) is locked</p>
<p>The black hole is disabled</p>
<p>Tachyon production and dilated time production are severely reduced</p>
<p>Time theorem generation from dilation glyphs is disabled</p>
<p>Certain challenge goals have been increased</p>
<p>Stored time is effectively dilated (exponent^0.5)</p>
<b>Reward: Unlock Tesseracts, which let you increase Infinity Dimension caps (see Infinity Dimension tab)</b>
</div>
</div>
</div>`

View File

@ -1,130 +0,0 @@
"use strict";
Vue.component("laitela-tab", {
data() {
return {
matter: new Decimal(0),
nextUnlock: "",
matterEffectPercentage: "",
dimMultNerf: 0,
activeDimensions: [],
higgs: new Decimal(0),
higgsGain: new Decimal(0),
showReset: false,
darkEnergyChance: 0,
darkEnergy: 0,
annihilated: false,
};
},
methods: {
update() {
this.matter.copyFrom(player.celestials.laitela.matter);
this.nextUnlock = Laitela.nextMatterDimensionThreshold;
this.matterEffectPercentage = Laitela.matterEffectPercentage;
this.dimMultNerf = Laitela.dimMultNerf;
this.realityReward = Laitela.realityReward;
this.activeDimensions = Array.range(0, 4).filter(i => MatterDimension(i + 1).amount.neq(0));
this.higgs.copyFrom(player.celestials.laitela.higgs);
this.higgsGain.copyFrom(Laitela.higgsGain);
this.annihilated = player.celestials.laitela.annihilated;
this.showReset = this.annihilated || this.higgsGain.gt(0);
this.darkEnergyChance = Laitela.darkEnergyChance;
this.darkEnergy = player.celestials.laitela.darkEnergy;
this.isRunning = Laitela.isRunning;
},
startRun() {
if (this.isRunning) startRealityOver();
else Laitela.startRun();
},
buyUnlock(info) {
Laitela.buyUnlock(info);
},
hasUnlock(info) {
return Laitela.has(info);
},
canBuyUnlock(info) {
return Laitela.canBuyUnlock(info);
},
runButtonClassObject() {
return {
"o-laitela-run-button__icon": true,
"o-laitela-run-button__icon--running": this.isRunning,
};
},
unlockClassObject(upgrade) {
return {
"o-laitela-shop-button--bought": upgrade.isBought,
"o-laitela-shop-button--available": upgrade.canBeBought
};
},
annihilate() {
Laitela.annihilate();
}
},
computed: {
dimensions: () => MatterDimensionState.list,
runUnlockThresholds: () => laitelaRunUnlockThresholds,
unlocksInfo: () => LAITELA_UNLOCKS,
upgrades: () => AnnihilationUpgrade.all,
darkEnergyUpgrades: () => DarkEnergyUpgrade.all
},
template:
`<div class="l-laitela-celestial-tab">
<div class="o-laitela-matter-amount">You have {{ shorten(matter, 2, 0) }} Dark Matter</div>
<div v-if="annihilated">You have {{ shorten(higgs, 2, 0)}} Higgs {{"Boson" | pluralize(higgs)}}</div>
<div v-if="higgs.gt(0)">Which cause you to have a {{ (darkEnergyChance * 100).toFixed(3) }}% chance of generating dark energy each dimension interval</div>
<div v-if="darkEnergy > 0">You have {{ shorten(darkEnergy, 2, 0)}} Dark Energy</div>
<div class="l-laitela-mechanics-container">
<div>
<matter-dimension-row
v-for="i in activeDimensions"
:key="i"
:dimension="dimensions[i]"
/>
<div>{{ nextUnlock }}</div>
</div>
<div class="l-laitela-unlocks-container" v-if="showReset">
<button
v-for="upgrade in upgrades"
:key="upgrade.id"
class="o-laitela-shop-button"
:class="{'o-laitela-shop-button--available': upgrade.canBeBought }"
@click="upgrade.purchase()">
{{ upgrade.description }} <br/> Costs: <b>{{ shorten(upgrade.cost, 2, 0) }}</b> Higgs Bosons
<br/>Currently: {{ upgrade.formattedEffect }}, Next: {{ upgrade.formattedNextEffect }}
</button>
</div>
</div>
<div class="l-laitela-mechanics-lower">
<div class="l-laitela-buttons-container">
<button class="o-laitela-run-button" @click="startRun">
<b>Start Lai'tela's Reality</b>
<div v-bind:class="runButtonClassObject()"></div>
Tickspeed is disabled and all dimension multipliers are decreased based on dark matter,
currently <b>x^{{ shorten(dimMultNerf, 3, 4) }}</b>
<br>
Multiply all dark matter dimensions based on highest AM reached,
Currently: <b>{{ shorten(realityReward, 2, 3)}}x</b>
</button>
<button class="c-laitela-annihilation-button" @click="annihilate()" v-if="showReset">
<h2>Annihilation</h2>
<p>
Resets your dark matter dimensions and Dark Matter, but gain <b>{{ shorten(higgsGain, 2, 0) }}</b>
Higgs {{"Boson" | pluralize(higgsGain)}}
</p>
</button>
</div>
<div class="l-laitela-dark-energy-upgrades">
<button
v-for="upgrade in darkEnergyUpgrades"
:key="upgrade.id"
class="o-laitela-shop-button--dark-energy"
:class="unlockClassObject(upgrade)"
@click="upgrade.purchase()">
{{ upgrade.description }} <br/> Costs: <b>{{ shorten(upgrade.cost, 2, 0) }}</b> Dark Energy
<br/>{{ upgrade.formattedEffect }}
</button>
</div>
</div>
</div>`
});

View File

@ -0,0 +1,467 @@
"use strict";
Vue.component("laitela-tab", {
data() {
return {
matter: new Decimal(0),
maxMatter: new Decimal(0),
matterExtraPurchasePercentage: 0
};
},
methods: {
update() {
this.matter.copyFrom(player.celestials.laitela.matter);
this.maxMatter.copyFrom(player.celestials.laitela.maxMatter);
this.matterExtraPurchasePercentage = Laitela.matterExtraPurchaseFactor - 1;
},
maxAll() {
Laitela.maxAllDMDimensions(4);
},
showLaitelaHowTo() {
ui.view.h2pForcedTab = GameDatabase.h2p.tabs.filter(tab => tab.name === "Lai'tela")[0];
Modal.h2p.show();
ui.view.h2pActive = true;
},
},
template: `
<div class="l-laitela-celestial-tab">
<div class="c-subtab-option-container">
<primary-button
class="o-primary-btn--subtab-option"
@click="showLaitelaHowTo()"
>Click for Lai'tela info</primary-button>
<primary-button
class="o-primary-btn--subtab-option"
@click="maxAll"
>Max all Dark Matter Dimensions</primary-button>
</div>
<div class="o-laitela-matter-amount">You have {{ format(matter.floor(), 2, 0) }} Dark Matter.</div>
<div class="o-laitela-matter-amount">Your maximum Dark Matter ever is {{ format(maxMatter.floor(), 2, 0) }},
giving {{ formatPercents(matterExtraPurchasePercentage, 2) }} more purchases from Continuum.</div>
<singularity-container />
<div class="l-laitela-mechanics-container">
<laitela-run-button />
<div>
<dark-matter-dimension-group />
<annihilation-button />
</div>
<singularity-milestone-pane />
</div>
<laitela-autobuyer-settings />
</div>`
});
Vue.component("singularity-container", {
data() {
return {
darkEnergy: 0,
darkEnergyGainPerSecond: 0,
singularities: 0,
singularityCapIncreases: 0,
canPerformSingularity: false,
singularityCap: 0,
baseTimeToSingularity: 0,
singularitiesGained: 0,
autoSingularityFactor: 0,
perStepFactor: 0,
isAutoEnabled: false,
hasAutoSingularity: false,
};
},
methods: {
update() {
const laitela = player.celestials.laitela;
this.darkEnergy = laitela.darkEnergy;
this.darkEnergyGainPerSecond = Array.range(1, 4)
.map(n => MatterDimension(n))
.filter(d => d.amount.gt(0))
.map(d => d.powerDE * 1000 / d.interval)
.sum();
this.singularities = laitela.singularities;
this.singularityCapIncreases = laitela.singularityCapIncreases;
this.canPerformSingularity = Singularity.capIsReached;
this.singularityCap = Singularity.cap;
this.baseTimeToSingularity = this.singularityCap / this.darkEnergyGainPerSecond;
this.singularitiesGained = Singularity.singularitiesGained;
this.autoSingularityFactor = SingularityMilestone.autoCondense.effectValue;
this.perStepFactor = Singularity.gainPerCapIncrease;
this.isAutoEnabled = laitela.automation.singularity && SingularityMilestone.autoCondense.isUnlocked;
this.hasAutoSingularity = Number.isFinite(this.autoSingularityFactor);
},
doSingularity() {
Singularity.perform();
},
increaseCap() {
Singularity.increaseCap();
},
decreaseCap() {
Singularity.decreaseCap();
},
formatRate(rate) {
if (rate < 1 / 60) return `${format(3600 * rate, 2, 3)} per hour`;
if (rate < 1) return `${format(60 * rate, 2, 3)} per minute`;
return `${format(rate, 2, 3)} per second`;
}
},
computed: {
singularityFormText() {
const formText = this.singularitiesGained === 1 ? "condense all Dark Energy into a Singularity"
: `condense all Dark Energy into ${format(this.singularitiesGained, 2, 0)} Singularities`;
if (this.canPerformSingularity) {
// Capitalize the string
return `${formText.charAt(0).toUpperCase()}${formText.slice(1)}`;
}
return `Reach ${format(this.singularityCap)} Dark Energy to ${formText}`;
},
singularityWaitText() {
let singularityTime = (this.singularityCap - this.darkEnergy) / this.darkEnergyGainPerSecond;
if (this.canPerformSingularity) {
singularityTime += this.singularityCap * (this.autoSingularityFactor - 1) / this.darkEnergyGainPerSecond;
return this.isAutoEnabled
? `(auto-condensing in ${TimeSpan.fromSeconds(singularityTime).toStringShort(false)})`
: "";
}
return `(enough Dark Energy in ${TimeSpan.fromSeconds(singularityTime).toStringShort(false)})`;
},
baseSingularityTime() {
return TimeSpan.fromSeconds(this.baseTimeToSingularity).toStringShort(false);
},
additionalSingularityTime() {
return TimeSpan.fromSeconds(this.baseTimeToSingularity * (this.autoSingularityFactor - 1))
.toStringShort(false);
},
manualSingularityRate() {
const totalTime = this.singularityCap / this.darkEnergyGainPerSecond;
return this.formatRate(this.singularitiesGained / totalTime);
},
autoSingularityRate() {
const totalTime = this.singularityCap / this.darkEnergyGainPerSecond * this.autoSingularityFactor;
return this.formatRate(this.singularitiesGained / totalTime);
}
},
template: `
<div class="c-laitela-singularity-container">
<div>
<h2>
You have {{ format(singularities, 2, 0) }} {{ "Singularity" | pluralize(singularities, "Singularities")}}
</h2>
<button
class="c-laitela-singularity"
:class="{ 'c-laitela-singularity--active' : canPerformSingularity }"
@click="doSingularity">
<h2>{{ singularityFormText }}</h2>
<br v-if="singularityWaitText !== ''">
<h2>{{ singularityWaitText }}</h2>
</button>
</div>
<div>
<div class="o-laitela-matter-amount">
You have {{ format(darkEnergy, 2, 4) }} Dark Energy. (+{{ format(darkEnergyGainPerSecond, 2, 4) }}/s)
</div>
<button class="c-laitela-singularity__cap-control" @click="decreaseCap">
Decrease Singularity cap.
</button>
<button class="c-laitela-singularity__cap-control" @click="increaseCap">
Increase Singularity cap.
</button>
<br>
Each step increases the required Dark Energy by {{ formatX(10) }},
<br>
but also increases gained Singularities by {{ formatX(perStepFactor) }}.
<br>
<br>
Total time to <span v-if="hasAutoSingularity">(auto-)</span>condense:
{{ baseSingularityTime }}
<span v-if="hasAutoSingularity && autoSingularityFactor !== 1">
(+{{ additionalSingularityTime }})
</span>
<br>
<span v-if="hasAutoSingularity && autoSingularityFactor !== 1">Manual </span>
Singularity gain rate: {{ manualSingularityRate }}
<br>
<span v-if="hasAutoSingularity && autoSingularityFactor !== 1">
Automatic Singularity gain rate: {{ autoSingularityRate }}
</span>
</div>
</div>`
});
Vue.component("laitela-run-button", {
data() {
return {
realityTime: 0,
maxDimTier: 0,
isRunning: false,
realityReward: 1,
};
},
methods: {
update() {
this.realityTime = player.celestials.laitela.fastestCompletion;
this.maxDimTier = Laitela.maxAllowedDimension;
this.realityReward = Laitela.realityReward;
this.isRunning = Laitela.isRunning;
},
startRun() {
if (!resetReality()) return;
Laitela.initializeRun();
},
runButtonClassObject() {
return {
"o-laitela-run-button__icon": true,
"o-laitela-run-button__icon--running": this.isRunning,
};
},
},
computed: {
completionTime() {
return TimeSpan.fromSeconds(this.realityTime).toStringShort();
}
},
template: `
<button class="o-laitela-run-button">
<b>Start Lai'tela's Reality</b>
<div :class="runButtonClassObject()" @click="startRun"></div>
<div v-if="realityReward > 1">
<b>All DM multipliers are {{ formatX(realityReward, 2, 2) }} higher</b>
<br>
<br>
Fastest Completion: {{ completionTime }}
<br>
<br>
<span v-if="maxDimTier <= 7">
Highest active dimension: {{ formatInt(maxDimTier) }}
</span>
<br>
<br>
</div>
Infinity Point and Eternity Point gain are Dilated. Game speed is reduced to {{ formatInt(1) }}
and gradually comes back over {{ formatInt(10) }} minutes, and Black Hole discharging and pulsing
is disabled.
<br>
<br>
Antimatter generates entropy inside of this Reality. At {{ formatPercents(1) }} entropy, the Reality
becomes destabilized and you gain a reward based on how quickly you reached {{ formatPercents(1) }}.
If you can destabilize in less than {{ formatInt(30) }} seconds, the Reality becomes more difficult
but also gives a stronger reward.
</button>`
});
Vue.component("dark-matter-dimension-group", {
data() {
return {
activeDimensions: [],
nextDimensionThreshold: 0,
};
},
methods: {
update() {
this.activeDimensions = Array.range(0, 4).filter(i => MatterDimension(i + 1).amount.neq(0));
this.nextDimensionThreshold = Array.range(0, 4)
.filter(i => MatterDimension(i + 1).amount.eq(0))
.map(i => MatterDimension(i + 1).adjustedStartingCost)
.min();
},
},
computed: {
dimensions: () => MatterDimensionState.list,
},
template: `
<span>
<matter-dimension-row
v-for="i in activeDimensions"
:key="i"
:dimension="dimensions[i]"
/>
<div v-if="nextDimensionThreshold !== 0">
<b>Next dimension unlocks at {{ format(nextDimensionThreshold) }} Dark Matter.</b>
<br><br>
</div>
</span>`
});
Vue.component("annihilation-button", {
data() {
return {
matter: new Decimal(0),
darkMatterMult: 0,
darkMatterMultGain: 0,
hasAnnihilated: false,
showAnnihilation: false,
matterRequirement: 0,
darkMatterMultRatio: 0,
autoAnnihilationInput: player.celestials.laitela.autoAnnihilationSetting,
isEnabled: true,
isMouseoverDisabled: false
};
},
methods: {
update() {
this.matter.copyFrom(player.celestials.laitela.matter);
this.darkMatterMult = Laitela.darkMatterMult;
this.darkMatterMultGain = Laitela.darkMatterMultGain;
this.hasAnnihilated = Laitela.darkMatterMult > 1;
this.showAnnihilation = this.hasAnnihilated || !MatterDimensionState.list.some(d => d.amount.eq(0));
this.matterRequirement = Laitela.annihilationDMRequirement;
this.darkMatterMultRatio = Laitela.darkMatterMultRatio;
this.isEnabled = player.celestials.laitela.automation.annihilation;
},
annihilate() {
if (this.isMouseoverDisabled) return;
Laitela.annihilate();
},
handleAutoAnnihilationInputChange() {
const float = parseFloat(this.autoAnnihilationInput);
if (isNaN(float)) {
this.autoAnnihilationInput = player.celestials.laitela.autoAnnihilationSetting;
} else {
player.celestials.laitela.autoAnnihilationSetting = float;
}
}
},
computed: {
annihilationInputStyle() {
return {
width: "6rem",
"background-color": this.isEnabled ? "" : "var(--color-disabled)",
};
}
},
template: `
<button class="c-laitela-annihilation-button"
@click="annihilate()"
v-if="showAnnihilation">
<h2>Annihilation</h2>
<span v-if="hasAnnihilated">
Current multiplier to all DM multipliers: <b>{{ formatX(darkMatterMult, 2, 2) }}</b>
<br><br>
</span>
Resets your Dark Matter, Dark Matter Dimensions, and Dark Energy,
<span v-if="!hasAnnihilated">
unlocking Auto-Annihilation, and
</span>
<span v-if="hasAnnihilated && matter.gte(matterRequirement)">
but adds <b>{{ format(darkMatterMultGain, 2, 2) }}</b> to your Annihilation multiplier.
(<b>{{ formatX(darkMatterMultRatio, 2, 2) }}</b> from previous multiplier)
</span>
<span v-else-if="hasAnnihilated">
adding to your current Annihilation multiplier (requires {{ format(matterRequirement) }} Dark Matter).
</span>
<span v-else-if="matter.gte(matterRequirement)">
multiplying DM multipliers by <b>{{ formatX(1 + darkMatterMultGain, 2, 2) }}</b>.
</span>
<span v-else>
giving a multiplier to all DM multipliers (requires {{ format(matterRequirement) }} Dark Matter).
</span>
<div v-if="hasAnnihilated">
<br>
Auto-Annihilate when adding
<input type="text"
v-model="autoAnnihilationInput"
@change="handleAutoAnnihilationInputChange()"
@mouseover="isMouseoverDisabled = true"
@mouseleave="isMouseoverDisabled = false"
:style="annihilationInputStyle"/>
to the multiplier.
</div>
</button>`
});
Vue.component("singularity-milestone-pane", {
data() {
return {
milestones: [],
hasNew: false,
};
},
methods: {
update() {
this.milestones = SingularityMilestones.nextMilestoneGroup;
this.hasNew = SingularityMilestones.unseenMilestones.length !== 0;
},
},
computed: {
glowStyle() {
if (this.hasNew) return { "box-shadow": "inset 0 0 1rem 0.5rem var(--color-celestials)" };
return {};
}
},
template: `
<div class="c-laitela-next-milestones">
<div class="o-laitela-singularity-modal-button"
onclick="Modal.singularityMilestones.show()"
:style="glowStyle">
Show all milestones
</div>
<singularity-milestone
v-for="milestone in milestones"
:key="milestone.id"
:milestone="milestone"
:suppressGlow="true"/>
</div>`
});
Vue.component("laitela-autobuyer-settings", {
data() {
return {
hasDimension: false,
hasAscension: false,
hasSingularity: false,
hasAnnihilated: false,
dimension: false,
ascension: false,
singularity: false,
annihilation: false,
};
},
methods: {
update() {
this.hasDimension = SingularityMilestone.darkDimensionAutobuyers.isUnlocked;
this.hasAscension = SingularityMilestone.darkDimensionAutobuyers.isUnlocked;
this.hasSingularity = SingularityMilestone.autoCondense.isUnlocked;
this.hasAnnihilated = Laitela.darkMatterMult > 1;
const auto = player.celestials.laitela.automation;
this.dimension = auto.dimensions;
this.ascension = auto.ascension;
this.singularity = auto.singularity;
this.annihilation = auto.annihilation;
},
},
watch: {
dimension(newValue) {
player.celestials.laitela.automation.dimensions = newValue;
},
ascension(newValue) {
player.celestials.laitela.automation.ascension = newValue;
},
singularity(newValue) {
player.celestials.laitela.automation.singularity = newValue;
},
annihilation(newValue) {
player.celestials.laitela.automation.annihilation = newValue;
},
},
template: `
<div class="c-laitela-singularity-container">
<primary-button-on-off
v-if="hasDimension"
v-model="dimension"
class="c-laitela-automation-toggle"
text="Auto-buy DM Dimensions:" />
<primary-button-on-off
v-if="hasAscension"
v-model="ascension"
class="c-laitela-automation-toggle"
text="Auto-Ascend:" />
<primary-button-on-off
v-if="hasSingularity"
v-model="singularity"
class="c-laitela-automation-toggle"
text="Auto-Singularity:" />
<primary-button-on-off
v-if="hasAnnihilated"
v-model="annihilation"
class="c-laitela-automation-toggle"
text="Auto-Annihilation:" />
</div>`
});

View File

@ -0,0 +1,103 @@
"use strict";
Vue.component("singularity-milestone", {
props: ["milestone", "suppressGlow"],
data: () => ({
isMaxed: false,
progressToNext: "",
remainingSingularities: 0,
description: "",
effectDisplay: "",
isUnique: false,
nextEffectDisplay: "",
start: 0,
completions: 0,
limit: 0
}),
methods: {
update() {
this.isMaxed = this.milestone.isMaxed;
this.progressToNext = this.milestone.progressToNext;
this.remainingSingularities = this.milestone.remainingSingularities;
this.description = this.milestone.description;
this.effectDisplay = this.milestone.effectDisplay;
this.isUnique = this.milestone.isUnique;
if (!this.isUnique && !this.isMaxed) this.nextEffectDisplay = this.milestone.nextEffectDisplay;
this.completions = this.milestone.completions;
this.limit = this.milestone.limit;
},
},
computed: {
barProgressStyle() {
let color;
if (this.isMaxed) color = "";
else if (this.isUnique) color = "var(--color-accent)";
else if (this.limit > 1) color = "var(--color-good-dark)";
else color = "var(--color-good)";
return {
background: color,
width: this.progressToNext
};
},
backgroundStyle() {
let color;
if (this.isUnique && this.isMaxed) color = "var(--color-accent)";
else if (this.limit > 1 && this.completions >= 1) {
if (this.isMaxed) color = "var(--color-good-dark)";
else color = "var(--color-good)";
} else {
color = "";
}
return {
"background-color": color
};
},
newGlowStyle() {
if (this.suppressGlow) return {};
const newMilestones = SingularityMilestones.unseenMilestones;
for (let rep = 0; this.completions === 0 || rep < this.completions; rep++) {
const thisLevel = this.milestone.start * Math.pow(this.milestone.repeat, rep);
if (newMilestones.includes(thisLevel)) return { "box-shadow": "0 0 0.3rem 0.3rem var(--color-celestials)" };
if (thisLevel > player.celestials.laitela.singularities) break;
}
return {};
},
upgradeDirectionIcon() {
switch (this.milestone.config.upgradeDirection) {
case LAITELA_UPGRADE_DIRECTION.SELF_BOOST:
return `<b>ᛝ</b>`;
case LAITELA_UPGRADE_DIRECTION.BOOSTS_MAIN:
return `<i class="fas fa-arrows-alt"></i>`;
case LAITELA_UPGRADE_DIRECTION.BOOSTS_LAITELA:
return `<i class="fas fa-compress-arrows-alt"></i>`;
default:
throw new Error("Unspecified Lai'tela upgrade direction in singularity milestone");
}
},
completionsDisplay() {
if (this.limit === 0) return `${this.completions} ${pluralize("completion", this.completions)}`;
if (this.isUnique) return this.isMaxed ? "Completed" : "Not completed";
return `${this.completions}/${this.limit} ${pluralize("completion", this.completions)}`;
}
},
template: `
<div class="c-laitela-milestone"
:class="{ 'c-laitela-milestone--completed': isUnique && isMaxed }"
:style="[backgroundStyle, newGlowStyle]">
<div class="c-laitela-milestone__progress" :style="barProgressStyle"/>
<b v-if="!isMaxed">
In {{ format(remainingSingularities, 2, 0) }}
{{ "Singularity" | pluralize(remainingSingularities, "Singularities")}}
</b>
<p> <span v-html="upgradeDirectionIcon"/> {{ description }}</p>
<br>
<b>
{{ effectDisplay }}
<span v-if="!isUnique && !isMaxed"> {{ nextEffectDisplay }}</span>
</b>
<div class="c-laitela-milestone__completions">
{{ completionsDisplay }}
</div>
</div>
`
});

View File

@ -0,0 +1,25 @@
"use strict";
Vue.component("singularity-milestones-modal", {
data: () => ({
milestones: []
}),
methods: {
update() {
this.milestones = SingularityMilestones.sortedForCompletions;
}
},
beforeDestroy() {
player.celestials.laitela.lastCheckedMilestones = player.celestials.laitela.singularities;
},
template: `
<div>
<modal-close-button @click="emitClose"/>
<div class="l-singularity-milestone-modal-container-outer">
<div class="l-singularity-milestone-modal-container-inner">
<singularity-milestone v-for="milestone in milestones" :key="milestone.id" :milestone="milestone"/>
</div>
</div>
</div>
`
});

View File

@ -6,56 +6,116 @@ Vue.component("matter-dimension-row", {
},
data() {
return {
chance: 0,
interval: new Decimal(0),
power: new Decimal(0),
chanceCost: 0,
tier: 0,
ascension: 0,
interval: 0,
powerDM: new Decimal(0),
powerDE: 0,
intervalCost: 0,
powerCost: 0,
powerDMCost: 0,
powerDECost: 0,
amount: new Decimal(0),
canBuyChance: false,
canBuyInterval: false,
canBuyPower: false
canBuyPowerDM: false,
canBuyPowerDE: false,
isIntervalCapped: false,
timer: 0,
timerPecent: 0,
intervalAscensionBump: 10000,
};
},
computed: {
name() {
const suffix = " Dark Matter Dimension";
switch (this.tier) {
case 0:
return `First ${suffix}`;
case 1:
return `Second ${suffix}`;
case 2:
return `Third ${suffix}`;
case 3:
return `Fourth ${suffix}`;
default:
throw new Error("Invalid Dark Matter Dimension index");
}
},
ascensionText() {
if (this.ascension === 0) return "";
return `(⯅${formatInt(this.ascension)})`;
},
intervalClassObject() {
return {
"o-matter-dimension-button--available": this.canBuyInterval,
"o-matter-dimension-button--ascend": this.isIntervalCapped
};
},
intervalText() {
if (this.interval > 1000) return `${format(this.interval / 1000, 2, 2)}s`;
return `${format(this.interval, 2, 2)}ms`;
},
ascensionTooltip() {
return `Multiply interval by ${formatInt(this.intervalAscensionBump)}
and both multipliers by ${formatInt(1000)}, but gain the ability to upgrade interval even further`;
}
},
methods: {
update() {
this.chance = this.dimension.chance;
this.interval.copyFrom(this.dimension.interval);
this.power.copyFrom(this.dimension.power);
this.chanceCost = this.dimension.chanceCost;
this.tier = this.dimension._tier;
this.ascension = this.dimension.ascensions;
this.interval = this.dimension.interval;
this.powerDM.copyFrom(this.dimension.powerDM);
this.powerDE = this.dimension.powerDE;
this.intervalCost = this.dimension.intervalCost;
this.powerCost = this.dimension.powerCost;
this.powerDMCost = this.dimension.powerDMCost;
this.powerDECost = this.dimension.powerDECost;
this.amount.copyFrom(this.dimension.amount);
this.canBuyChance = this.dimension.canBuyChance;
this.canBuyInterval = this.dimension.canBuyInterval;
this.canBuyPower = this.dimension.canBuyPower;
this.canBuyPowerDM = this.dimension.canBuyPowerDM;
this.canBuyPowerDE = this.dimension.canBuyPowerDE;
this.isIntervalCapped = this.dimension.interval <= this.dimension.intervalPurchaseCap;
this.timer = this.dimension.timeSinceLastUpdate;
this.timerPercent = this.timer / this.interval;
this.intervalAscensionBump = SingularityMilestone.ascensionIntervalScaling.effectValue;
},
handleIntervalClick() {
if (this.isIntervalCapped) this.dimension.ascend();
else this.dimension.buyInterval();
}
},
template:
`<div class="c-matter-dimension-container">
<div class="o-matter-dimension-amount"> {{ shorten(amount, 2, 0) }}</div>
<div class="o-matter-dimension-amount"> {{ name }} {{ ascensionText }}: {{ format(amount, 2, 0) }}</div>
<div class="c-matter-dimension-buttons">
<button
@click="dimension.buyChance()"
@click="handleIntervalClick()"
class="o-matter-dimension-button"
:class="{ 'o-matter-dimension-button--available': canBuyChance }">
{{ chance }}% <span v-if="chance !== 100"><br>Cost: {{ shorten(chanceCost, 2, 0) }}</span>
:class="intervalClassObject">
{{ intervalText }}
<span v-if="isIntervalCapped">
<br>Ascend!
<span :ach-tooltip="ascensionTooltip">
<i class="fas fa-question-circle"></i>
</span>
</span>
<span v-else><br>Cost: {{ format(intervalCost, 2, 0) }}</span>
</button>
<button
@click="dimension.buyInterval()"
class="o-matter-dimension-button"
:class="{ 'o-matter-dimension-button--available': canBuyInterval }">
{{ interval.toFixed(2) }}ms <span v-if="!interval.eq(50)"><br>Cost: {{ shorten(intervalCost, 2, 0) }}</span>
<button
@click="dimension.buyPowerDM()"
class="o-matter-dimension-button"
:class="{ 'o-matter-dimension-button--available': canBuyPowerDM }">
DM {{ formatX(powerDM, 2, 2) }}<br>Cost: {{ format(powerDMCost, 2, 0) }}
</button>
<button
@click="dimension.buyPower()"
class="o-matter-dimension-button"
:class="{ 'o-matter-dimension-button--available': canBuyPower }">
{{ shorten(power, 2, 2) }}x <br>Cost: {{ shorten(powerCost, 2, 0) }}
<button
@click="dimension.buyPowerDE()"
class="o-matter-dimension-button"
:class="{ 'o-matter-dimension-button--available': canBuyPowerDE }">
DE +{{ format(powerDE, 2, 4) }}<br>Cost: {{ format(powerDECost, 2, 0) }}
</button>
</div>
<div v-if="interval > 200">Tick: {{ formatInt(timer) }} ms ({{ formatPercents(timerPercent, 1) }})</div>
<div>DE: {{ format(powerDE * 1000 / interval, 2, 4) }}/s</div>
</div>
`
})

View File

@ -10,7 +10,7 @@ Vue.component("alchemy-circle-node", {
isReactionActive: false,
amount: 0,
flow: 0,
alwaysShowResource: false
isUnlocked: false
};
},
computed: {
@ -21,7 +21,7 @@ Vue.component("alchemy-circle-node", {
return this.resource.isBaseResource;
},
layoutStyle() {
const scaledFlow = Math.clamp(0.4 * Math.sqrt(Math.abs(this.flow)), 0, 1);
const scaledFlow = Math.clamp(0.7 * Math.sqrt(Math.abs(this.flow)), 0, 1);
return {
left: `${this.node.x}%`,
top: `${this.node.y}%`,
@ -34,6 +34,7 @@ Vue.component("alchemy-circle-node", {
"o-alchemy-node--base": this.isBaseResource,
"o-alchemy-node--active": this.isReactionActive,
"o-alchemy-node--unfocused": !this.isFocused,
"o-alchemy-node--locked": !this.isUnlocked,
};
},
hintClassObject() {
@ -45,7 +46,7 @@ Vue.component("alchemy-circle-node", {
this.isReactionActive = !this.isBaseResource && this.node.resource.reaction.isActive;
this.amount = this.resource.amount;
this.flow = this.resource.flow;
this.alwaysShowResource = player.options.showAlchemyResources;
this.isUnlocked = this.resource.isUnlocked;
}
},
template: `
@ -60,15 +61,13 @@ Vue.component("alchemy-circle-node", {
:resource="resource"
:classObject="classObject"
/>
<div v-if="alwaysShowResource"
class="o-alchemy-node-resource--always-visible">
{{ amount.toFixed(1) }}
</div>
<hint-text v-else
:class="hintClassObject"
class="o-hint-text--alchemy-node l-hint-text--alchemy-node">
{{ amount.toFixed(1) }}
</hint-text>
<span v-if="isUnlocked">
<hint-text type="alchemy"
:class="hintClassObject"
class="o-hint-text--alchemy-node l-hint-text--alchemy-node">
{{ amount.toFixed(1) }}
</hint-text>
</span>
</div>
`
});
@ -81,30 +80,32 @@ Vue.component("alchemy-resource-arc", {
data() {
return {
amount: 0,
fillFraction: 0,
};
},
computed: {
spinnerTransform() {
return {
transform: `rotate(${this.amount / Ra.alchemyResourceCap * 360}deg)`,
background: this.amount === Ra.alchemyResourceCap ? "#ff9800" : undefined
transform: `rotate(${this.fillFraction * 360}deg)`,
background: this.fillFraction === 1 ? "#ff9800" : undefined
};
},
fillerTransform() {
return {
opacity: this.amount / Ra.alchemyResourceCap > 0.5 ? 1 : 0,
background: this.amount === Ra.alchemyResourceCap ? "#ff9800" : undefined
opacity: this.fillFraction > 0.5 ? 1 : 0,
background: this.fillFraction === 1 ? "#ff9800" : undefined
};
},
maskTransform() {
return {
opacity: this.amount / Ra.alchemyResourceCap > 0.5 ? 0 : 1
opacity: this.fillFraction > 0.5 ? 0 : 1
};
}
},
methods: {
update() {
this.amount = this.resource.amount;
this.fillFraction = Math.clamp(this.amount / estimatedAlchemyCap(), 0, 1);
}
},
template: `

View File

@ -8,7 +8,9 @@ Vue.component("alchemy-resource-info", {
return {
amount: 0,
isReactionActive: false,
reactionProduction: 0
reactionProduction: 0,
isUnlocked: false,
unlockRequirement: ""
};
},
computed: {
@ -21,9 +23,9 @@ Vue.component("alchemy-resource-info", {
reactionText() {
if (this.resource === AlchemyResource.reality) return this.realityReactionText;
const reagents = this.reaction.reagents
.map(r => `${shorten(r.cost)}${r.resource.symbol}`)
.map(r => `${format(r.cost)}${r.resource.symbol}`)
.join(" + ");
return `${reagents}${shorten(this.reaction.reactionProduction, 2, 2)}${this.resource.symbol}`;
return `${reagents}${format(this.reaction.reactionProduction, 2, 2)}${this.resource.symbol}`;
},
realityReactionText() {
const reagents = this.reaction.reagents
@ -37,11 +39,16 @@ Vue.component("alchemy-resource-info", {
effect: () => resource.config.effect(resource.amount),
formatEffect: resource.config.formatEffect
};
},
resourceAmount() {
return formatFloat(this.amount, 1);
}
},
methods: {
update() {
this.amount = this.resource.amount;
this.isUnlocked = this.resource.isUnlocked;
this.unlockRequirement = this.resource.config.lockText;
if (!this.isBaseResource) {
this.isReactionActive = this.reaction.isActive;
this.reactionProduction = this.reaction.production;
@ -50,11 +57,12 @@ Vue.component("alchemy-resource-info", {
},
template: `
<div class="c-alchemy-resource-info">
<span>{{resource.symbol}} {{resource.name}}</span>
<span>Current: {{ shorten(amount, 2, 2) }}</span>
<span>{{isUnlocked ? resource.symbol : "?"}} {{resource.name}}</span>
<span>Current: {{ isUnlocked ? resourceAmount : "Locked!" }}</span>
<span v-if="isBaseResource">Base Resource</span>
<span v-else>Reaction: {{isReactionActive ? "Active" : "Inactive"}} ({{reactionText}})</span>
<effect-display title="Effect" :config="effectConfig" />
<span v-else>Reaction: {{isReactionActive ? "Active" : "Inactive"}} ({{isUnlocked ? reactionText : "???"}})</span>
<span v-if="isUnlocked"><effect-display title="Effect" :config="effectConfig" /></span>
<span v-else>Unlock requirement: {{unlockRequirement}}</span>
</div>
`
});

View File

@ -75,9 +75,10 @@ Vue.component("alchemy-tab", {
return {
infoResourceId: 0,
focusedResourceId: -1,
reactionsAvailable: false,
realityCreationAvailable: false,
alwaysShowResource: false,
reactionProgress: 0,
estimatedCap: 0,
};
},
computed: {
@ -100,10 +101,11 @@ Vue.component("alchemy-tab", {
},
methods: {
update() {
this.reactionsAvailable = AlchemyResources.all.filter(res => !res.isBaseResource && res.isUnlocked).length !== 0;
this.realityCreationAvailable = AlchemyResource.reality.amount !== 0;
this.alwaysShowResource = player.options.showAlchemyResources;
const animationTime = 800;
this.reactionProgress = (player.realTimePlayed % animationTime) / animationTime;
this.estimatedCap = estimatedAlchemyCap();
},
orbitSize(orbit) {
const maxRadius = this.layout.orbits.map(o => o.radius).max();
@ -118,6 +120,7 @@ Vue.component("alchemy-tab", {
},
handleClick(node) {
const resource = node.resource;
if (!resource.isUnlocked) return;
if (this.infoResourceId !== resource.id) {
this.infoResourceId = resource.id;
this.focusedResourceId = resource.id;
@ -127,17 +130,22 @@ Vue.component("alchemy-tab", {
resource.reaction.isActive = !resource.reaction.isActive;
GameUI.update();
},
isUnlocked(reactionArrow) {
return reactionArrow.product.resource.isUnlocked && reactionArrow.reagent.resource.isUnlocked;
},
isCapped(reactionArrow) {
return reactionArrow.product.resource.amount >= reactionArrow.reagent.resource.amount;
return reactionArrow.product.resource.amount > 0 &&
reactionArrow.product.resource.amount >= reactionArrow.reagent.resource.amount;
},
isActiveReaction(reactionArrow) {
return reactionArrow.reaction.isActive;
},
isFocusedReaction(reactionArrow) {
return reactionArrow.reaction.product.id === this.focusedResourceId;
return this.isUnlocked(reactionArrow) && reactionArrow.reaction.product.id === this.focusedResourceId;
},
isDisplayed(reactionArrow) {
return this.isActiveReaction(reactionArrow) || this.isFocusedReaction(reactionArrow);
return this.isUnlocked(reactionArrow) &&
(this.isActiveReaction(reactionArrow) || this.isFocusedReaction(reactionArrow));
},
isFocusedNode(node) {
if (this.focusedResourceId === -1) return true;
@ -173,7 +181,7 @@ Vue.component("alchemy-tab", {
},
reactionPathClass(reactionArrow) {
return {
"o-alchemy-reaction-path": true,
"o-alchemy-reaction-path": this.isUnlocked(reactionArrow),
"o-alchemy-reaction-path--limited": this.isCapped(reactionArrow) && this.isDisplayed(reactionArrow),
"o-alchemy-reaction-path--focused": !this.isCapped(reactionArrow) && this.isFocusedReaction(reactionArrow),
};
@ -184,36 +192,47 @@ Vue.component("alchemy-tab", {
"o-alchemy-reaction-arrow--focused": this.isFocusedReaction(reactionArrow),
};
},
toggleResourceVisibility() {
player.options.showAlchemyResources = !player.options.showAlchemyResources;
},
showAlchemyHowTo() {
Modal.message.show("You can now refine glyphs using \"Alchemy Mode\" in the glyph auto-sacrifice settings. " +
"Refined glyphs will give 1% of their level in alchemy resources. Alchemy reactions can be toggled on " +
"and off by clicking the respective nodes, and each resource gives its own boost to various resources " +
"in the game. Basic resource totals are limited to the level of the refined glyph, and compound resource " +
"totals are limited to the amount of the reactants. All active alchemy reactions are applied once per " +
"reality, unaffected by amplification. Toggle reactions on or off by clicking the nodes. You can show the " +
"current totals of all alchemy resources by holding shift.");
ui.view.h2pForcedTab = GameDatabase.h2p.tabs.filter(tab => tab.name === "Glyph Alchemy")[0];
Modal.h2p.show();
ui.view.h2pActive = true;
},
setAllReactions(value) {
for (const reaction of AlchemyReactions.all.compact()) {
reaction.isActive = value;
toggleAllReactions() {
const reactions = AlchemyReactions.all.compact().filter(r => r._product.isUnlocked);
const allReactionsDisabled = reactions.every(reaction => !reaction.isActive);
if (allReactionsDisabled) {
for (const reaction of reactions) {
reaction.isActive = true;
}
} else {
for (const reaction of reactions) {
reaction.isActive = false;
}
}
}
},
template:
`<div class="l-ra-alchemy-tab">
<div @click="showAlchemyHowTo()" class="o-primary-btn">Click for alchemy info</div>
<alchemy-resource-info :key="infoResourceId" :resource="infoResource" />
<div>
<input type="checkbox"
id="alwaysShowResourceBox"
v-model="alwaysShowResource"
:value="alwaysShowResource"
@input="toggleResourceVisibility()">
<label for="alwaysShowResourceBox">Always show resource totals</label>
template: `
<div class="l-ra-alchemy-tab">
<div class="c-subtab-option-container">
<primary-button class="o-primary-btn--subtab-option" @click="showAlchemyHowTo()">
Click for alchemy info
</primary-button>
<primary-button class="o-primary-btn--subtab-option" @click="toggleAllReactions()">
Toggle all reactions
</primary-button>
<primary-button
v-if="realityCreationAvailable"
class="o-primary-btn--subtab-option"
onclick="Modal.realityGlyph.show()"
>
Create a Reality glyph
</primary-button>
</div>
<alchemy-resource-info :key="infoResourceId" :resource="infoResource" />
Resource cap, based on glyph level in last 10 realities: {{ format(estimatedCap, 3, 2) }}.
<span v-if="reactionsAvailable">
Reactions trigger once every time you reality.
</span>
<div class="l-alchemy-circle" :style="circleStyle">
<svg class="l-alchemy-orbit-canvas">
<circle
@ -224,8 +243,8 @@ Vue.component("alchemy-tab", {
:r="orbitSize(orbit)"
:class="orbitClass"
/>
</svg>
<alchemy-circle-node
</svg>
<alchemy-circle-node
v-for="(node, i) in layout.nodes"
:key="i"
:node="node"
@ -245,13 +264,7 @@ Vue.component("alchemy-tab", {
v-bind="reactionArrowPositions(reactionArrow)"
:class="reactionArrowClass(reactionArrow)"
/>
</svg>
</svg>
</div>
<button class="o-primary-btn" @click="setAllReactions(true)">Turn on all reactions</button>
<button class="o-primary-btn" @click="setAllReactions(false)">Turn off all reactions</button>
<primary-button
v-if="realityCreationAvailable"
class="o-primary-btn"
onclick="Modal.realityGlyph.show()">Create a Reality glyph</primary-button>
</div>`
});

View File

@ -1,58 +0,0 @@
"use strict";
Vue.component("compression-button", {
data() {
return {
timeShards: new Decimal(0),
isRunning: false,
requiredForGain: new Decimal(0),
entanglementGain: 0
};
},
computed: {
shardRequirement: () => TimeCompression.timeShardRequirement,
canCompress() {
return this.timeShards.gte(this.shardRequirement);
}
},
methods: {
update() {
this.timeShards.copyFrom(player.timeShards);
this.isRunning = player.celestials.ra.compression.active;
if (!this.isRunning) return;
this.entanglementGain = this.gainedEntanglement();
if (this.entanglementGain <= 0) {
this.requiredForGain.copyFrom(this.minAntimatterForEntanglement());
}
},
minAntimatterForEntanglement() {
if (TimeCompression.totalEntanglement === 308) {
return Decimal.pow10(9e15);
}
const entanglementMult = Effects.max(1, CompressionUpgrade.moreEntanglement);
return Decimal.pow10(1.8e5 * Math.pow(1 + TimeCompression.totalEntanglement / (30.8 * entanglementMult), 2.5));
},
gainedEntanglement() {
return Math.max(0, TimeCompression.entanglementThisRun - TimeCompression.totalEntanglement);
},
},
template:
`<button class="o-compression-btn" onclick="TimeCompression.toggle()">
<span v-if="!canCompress && !isRunning">
Time compression requires {{ shorten(shardRequirement) }} time shards to activate.
<br>
Currently: {{ shorten(timeShards) }} time shards
</span>
<span v-else-if="!isRunning">Compress time.</span>
<span v-else-if="entanglementGain > 0">
Disable compression.
<br>
Gain {{shorten(entanglementGain, 0, 2)}} Entanglement.
</span>
<span v-else>
Disable compression.
<br>
Reach {{shorten(requiredForGain, 2, 2)}} antimatter to gain more Entanglement.
</span>
</button>`
});

View File

@ -1,60 +0,0 @@
"use strict";
Vue.component("compression-upgrade", {
props: {
upgrade: Object
},
data() {
return {
isBought: false,
isActive: false,
isAffordable: false,
currentDisplay: ""
};
},
computed: {
classObject() {
return {
"o-compression-upgrade": true,
"o-compression-upgrade--active": this.isBought && this.isActive,
"o-compression-upgrade--inactive": this.isBought && !this.isActive,
"o-compression-upgrade--available": !this.isBought && this.isAffordable,
"o-compression-upgrade--unavailable": !this.isBought && !this.isAffordable
};
}
},
methods: {
update() {
this.isBought = this.upgrade.isBought;
this.isActive = this.upgrade.canBeApplied;
this.isAffordable = this.upgrade.isAffordable;
this.currentDisplay = this.upgrade.config.currentDisplay();
this.effectDisplay = this.upgrade.effectDisplay;
}
},
template:
`<div class="l-spoon-btn-group">
<button :class="classObject" @click="upgrade.purchase()">
<description-display
:config="upgrade.config"
:length="70"
name="o-compression-upgrade__description"
/>
<span v-if="isBought && effectDisplay !== undefined"><br><br>Currently: {{ effectDisplay }}</span>
<br><br>
<span>To activate: {{ upgrade.config.secondary() }}</span>
<br>
<span>(Currently {{ currentDisplay }})</span>
<br><br>
<effect-display :config="upgrade.config" />
<cost-display
v-if="!isBought"
:config="upgrade.config"
singular="Entanglement"
plural="Entanglement"
/>
<div v-else-if="isActive">Active! (Cost: {{ shortenSmallInteger(upgrade.config.cost) }})</div>
<div v-else>Inactive (Cost: {{ shortenSmallInteger(upgrade.config.cost) }})</div>
</button>
</div>`
});

View File

@ -2,33 +2,27 @@
Vue.component("ra-level-chevron", {
props: {
minLevel: Number,
level: Number,
goal: Number,
unlock: Object,
singleLevel: {
type: Boolean,
defualt: false
},
isImportantLevel: Boolean
},
data() {
return {
mouseOverInterval: 0,
isMouseOver: false
};
},
computed: {
percentPerLevel() {
return this.singleLevel ? 0 : 100 / (this.goal - 1);
levelPercent() {
const startScl = Math.sqrt(Ra.totalExpForLevel(this.minLevel));
const endScl = Math.sqrt(Ra.totalExpForLevel(this.goal));
const currentScl = Math.sqrt(Ra.totalExpForLevel(this.level));
const expFraction = (currentScl - startScl) / (endScl - startScl);
return 100 * expFraction;
},
levelPosition() {
if (this.level === this.goal)
return {
right: "0%",
};
return {
left: `${this.percentPerLevel * (this.level - 1)}%`,
};
if (this.level === this.goal) return { right: "0%" };
if (this.singleLevel) return { left: "0%" };
return { left: `${this.levelPercent}%` };
},
classList() {
return [
@ -37,29 +31,13 @@ Vue.component("ra-level-chevron", {
];
}
},
methods: {
onMouseEnter() {
if (this.$viewModel.shiftDown) return;
clearTimeout(this.mouseOverInterval);
this.isMouseOver = true;
},
onMouseLeave() {
this.mouseOverInterval = setTimeout(() => this.isMouseOver = false, 500);
}
},
template: `
<div
<div v-if="level >= minLevel || singleLevel"
class="l-ra-lvl-chevron"
:style="levelPosition"
:class="classList"
@mouseenter="onMouseEnter"
@click="onMouseEnter"
@mouseleave="onMouseLeave">
<div v-if="isMouseOver && isImportantLevel" class="o-ra-unlock-hover-text">
{{unlock.reward}}
</div>
<span>
{{level}}
:class="classList">
<span v-if="isImportantLevel || level === goal">
{{formatInt(level)}}
</span>
</div>
`

View File

@ -16,24 +16,26 @@ Vue.component("ra-pet-level-bar", {
shiftDown() {
return ui.view.shiftDown;
},
importantLevels: () => [2, 3, 5, 10, 15, 25],
unlocks() {
return Object.values(RA_UNLOCKS).filter(unlock => unlock.pet === this.pet);
},
percentPerLevel() {
return 100 / (this.currentLevelGoal - 1);
},
percentToNextLevel() {
return this.exp / this.requiredExp;
importantLevels() {
return this.unlocks.map(u => u.level);
},
multiLevelStyle() {
if (Notations.current.name === "Blind") return { width: "0%" };
const startScl = Math.sqrt(Ra.totalExpForLevel(this.prevGoal));
const endScl = Math.sqrt(Ra.totalExpForLevel(this.nextGoal));
const currentScl = Math.sqrt(Ra.totalExpForLevel(this.level) + this.exp);
const expFraction = (currentScl - startScl) / (endScl - startScl);
return {
width: `${Math.min((this.level - 1 + this.percentToNextLevel) * this.percentPerLevel, 100)}%`
width: `${100 * Math.clampMax(expFraction, 1)}%`
};
},
singleLevelStyle() {
if (Notations.current.name === "Blind") return { width: "0%" };
return {
width: `${this.percentToNextLevel * 100}%`
width: `${100 * (this.exp / this.requiredExp)}%`
};
},
petStyle() {
@ -41,16 +43,18 @@ Vue.component("ra-pet-level-bar", {
"background-color": this.pet.color
};
},
importantGoal() {
return this.importantLevels.find(goal => goal > this.level || goal === 25);
prevGoal() {
const currentUpgrades = this.importantLevels.filter(goal => goal <= this.level);
return Math.clampMax(currentUpgrades.max(), 15);
},
nextGoal() {
const missingUpgrades = this.importantLevels.filter(goal => goal > this.level);
return missingUpgrades.length === 0 ? 25 : missingUpgrades.min();
},
currentLevelGoal() {
if (this.shiftDown) return this.level + 1;
return this.importantGoal;
return this.nextGoal;
},
activeUnlock() {
return this.unlocks.find(unlock => unlock.level === this.importantGoal);
}
},
methods: {
update() {
@ -61,9 +65,6 @@ Vue.component("ra-pet-level-bar", {
this.level = pet.level;
this.requiredExp = pet.requiredExp;
},
findUnlockByLevel(level) {
return this.unlocks.find(unlock => unlock.level === level);
},
isImportant(level) {
return this.importantLevels.includes(level);
}
@ -74,29 +75,23 @@ Vue.component("ra-pet-level-bar", {
<div v-if="shiftDown">
<ra-level-chevron v-for="lvl in 2"
:key="currentLevelGoal - 2 + lvl"
:level ="currentLevelGoal - 2 + lvl"
:level="currentLevelGoal - 2 + lvl"
:goal="currentLevelGoal"
:singleLevel="true"
:isImportantLevel="isImportant(lvl)"
/>
</div>
<div v-else>
<ra-level-chevron v-for="lvl in (currentLevelGoal - 1)"
<ra-level-chevron v-for="lvl in currentLevelGoal"
:key="lvl"
:minLevel="prevGoal"
:level="lvl"
:goal="currentLevelGoal"
:unlock="findUnlockByLevel(lvl)"
:isImportantLevel="isImportant(lvl)"
/>
</div>
<div class="l-ra-exp-bar-inner" :style="[shiftDown ? singleLevelStyle : multiLevelStyle, petStyle]" />
</div>
<div class="l-ra-unlock" :style="petStyle">
<div class="l-ra-unlock-inner">
<b>{{ activeUnlock.description }}</b>
<p>{{ activeUnlock.reward }}</p>
</div>
</div>
</div>
`
});

View File

@ -0,0 +1,53 @@
"use strict";
Vue.component("ra-pet-recollection-button", {
props: {
petConfig: Object,
},
data() {
return {
isUnlocked: false,
name: "",
hasRecollection: false,
};
},
computed: {
petStyle() {
return {
backgroundColor: this.hasRecollection ? this.petConfig.pet.color : "#555",
cursor: this.hasRecollection ? "" : "pointer",
"pointer-events": this.hasRecollection ? "none" : "",
"box-shadow": this.hasRecollection ? "0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.7)" : "",
"border-color": this.hasRecollection ? "black" : ""
};
}
},
methods: {
update() {
const pet = this.petConfig.pet;
this.isUnlocked = pet.isUnlocked;
if (!this.isUnlocked) return;
this.name = pet.name;
this.hasRecollection = pet.hasRecollection;
},
turnOnRecollection() {
Ra.petWithRecollection = this.petConfig.pet.name;
}
},
template: `
<div class="l-ra-pet-recollection-button-container">
<button
class="c-ra-pet-recollection-button"
v-if="isUnlocked"
:style="petStyle"
@click="turnOnRecollection">
<span v-if="hasRecollection">
Recollection given to {{ name }}
</span>
<span v-else>
Give Recollection to {{ name }}
</span>
</button>
</div>
`
});

View File

@ -7,59 +7,166 @@ Vue.component("ra-pet", {
data() {
return {
isUnlocked: false,
name: "",
level: 0,
exp: 0,
requiredExp: 0,
expBoost: 0,
lastTenGlyphLevels: [],
lastTenRunTimers: [],
nextLevelEstimate: "",
upgradeEstimate: "",
memoryChunks: 0,
memoryChunksPerSecond: 0,
memoriesPerSecond: 0,
memoryMultiplier: 1,
canGetMemoryChunks: false,
};
},
computed: {
pet() {
return this.petConfig.pet;
showScalingUpgrade() {
return this.petConfig.scalingUpgradeVisible(this.level);
},
scalingUpgradeText() {
return this.petConfig.scalingUpgradeText(this.level);
},
petStyle() {
return {
color: this.pet.color
color: this.petConfig.pet.color
};
},
expPerMin() {
const expGain = this.lastTenGlyphLevels.reduce((acc, value) =>
acc + Math.pow(2, value / 500 - 10), 0
) * this.expBoost / 10;
const avgTimeMs = this.lastTenRunTimers.reduce((acc, value) => acc + value, 0) / 10;
return Math.round(expGain / (avgTimeMs / 60000));
unlocks() {
return Object.values(RA_UNLOCKS)
.filter(unlock => unlock.pet === this.petConfig.pet)
.sort((a, b) => a.level - b.level);
},
chunkTooltip() {
switch (this.petConfig.pet.name) {
case "Teresa":
return "Based on EP";
case "Effarig":
return "Based on Relic Shards gained";
case "Enslaved":
return "Based on Time Shards";
case "V":
return "Based on Infinity Power";
default:
throw new Error(`Unrecognized celestial ${this.petConfig.pet.name} in Ra UI`);
}
},
memoryGainTooltip() {
switch (this.petConfig.pet.name) {
case "Teresa":
return "Based on current RM";
case "Effarig":
return "Based on best glyph level";
case "Enslaved":
return "Based on total time played";
case "V":
return "Based on total Celestial Memory levels";
default:
throw new Error(`Unrecognized celestial ${this.petConfig.pet.name} in Ra UI`);
}
},
experienceInformation() {
return `${shorten(this.exp, 2)}/${shorten(this.requiredExp, 2)}
memories - ${shorten(this.expPerMin, 2)} memories/min`;
}
},
methods: {
update() {
const pet = this.pet;
const pet = this.petConfig.pet;
this.isUnlocked = pet.isUnlocked;
if (!this.isUnlocked) return;
this.name = pet.name;
this.level = pet.level;
this.expBoost = pet.expBoost;
this.exp = pet.exp;
this.requiredExp = pet.requiredExp;
this.lastTenGlyphLevels = player.lastTenRealities.map(([, , , lvl]) => lvl);
this.lastTenRunTimers = player.lastTenRealities.map(([, , time]) => time);
this.memoryChunks = pet.memoryChunks;
this.memoryChunksPerSecond = pet.memoryChunksPerSecond;
this.memoriesPerSecond = pet.memoryChunks * Ra.productionPerMemoryChunk();
this.canGetMemoryChunks = pet.canGetMemoryChunks;
this.memoryMultiplier = pet.memoryProductionMultiplier;
const leftThisLevel = this.requiredExp - this.exp;
const toUnlock = Ra.totalExpForLevel(this.nextUnlockLevel()) - Ra.totalExpForLevel(this.level + 1);
this.nextLevelEstimate = this.timeToGoalString(leftThisLevel);
this.upgradeEstimate = this.timeToGoalString(leftThisLevel + toUnlock);
},
timeToGoalString(expToGain) {
const pet = this.petConfig.pet;
// Quadratic formula for growth (uses constant growth for a = 0)
const a = Ra.productionPerMemoryChunk() * pet.memoryChunksPerSecond / 2;
const b = Ra.productionPerMemoryChunk() * pet.memoryChunks;
const c = -expToGain;
const estimate = a === 0
? -c / b
: (Math.sqrt(Math.pow(b, 2) - 4 * a * c) - b) / (2 * a);
if (Number.isFinite(estimate)) {
return TimeSpan.fromSeconds(estimate).toStringShort();
}
return "never";
},
nextUnlockLevel() {
const missingUpgrades = Object.values(RA_UNLOCKS)
.filter(unlock => unlock.pet === this.petConfig.pet)
.map(u => u.level)
.filter(goal => goal > this.level);
return missingUpgrades.length === 0 ? 25 : missingUpgrades.min();
}
},
template: `
<div class="l-ra-pet-container" v-if="isUnlocked">
<div class="c-ra-pet-header" :style="petStyle">
<div class="c-ra-pet-title">{{ pet.name }} Lvl. {{ shortenSmallInteger(level) }}</div>
<div v-if="level >= 2">{{ scalingUpgradeText }}</div>
<div>{{ experienceInformation }}</div>
<div class="c-ra-pet-title">{{ name }} Level {{ formatInt(level) }}</div>
<div v-if="showScalingUpgrade"
:key="level">
{{ scalingUpgradeText }}
</div>
<div v-else>
<br>
</div>
<div v-if="level < 25">
<div>
{{ format(exp, 2) }} / {{ format(requiredExp, 2) }} {{ name }} memories
</div>
<div>
(next level in {{ nextLevelEstimate }})
</div>
<div>
(next upgrade in {{ upgradeEstimate }})
</div>
</div>
<div v-else>
<br>
</div>
<ra-pet-level-bar :pet="petConfig.pet" />
<div v-if="level < 25">
<div>
{{ format(memoryChunks, 2, 2) }} memory chunks, {{ format(memoriesPerSecond, 2, 2) }} memories/sec
</div>
<div>
Gaining {{ format(memoryChunksPerSecond, 2, 2) }} memory chunks/sec
<span :ach-tooltip="chunkTooltip">
<i class="fas fa-question-circle"></i>
</span>
</div>
</div>
<div v-else>
<div>
Capped at Level {{ formatInt(level) }}
</div>
</div>
<div v-if="memoryMultiplier > 1">
Multiplying all memory production by {{ format(memoryMultiplier, 2, 3) }}
<span :ach-tooltip="memoryGainTooltip">
<i class="fas fa-question-circle"></i>
</span>
</div>
<div v-else>
<br>
</div>
<br>
<div style="display: flex; justify-content: center;">
<!-- This choice of key forces a UI update every level up -->
<ra-upgrade-icon v-for="(unlock, i) in unlocks"
:key="25 * level + i"
:unlock="unlock" />
</div>
</div>
<ra-pet-level-bar :pet="pet" />
</div>
`
});

View File

@ -3,94 +3,144 @@
Vue.component("ra-tab", {
data() {
return {
expMults: [0, 0, 0, 0],
currentExpGain: 0,
memoriesPerChunk: 0,
showReality: false,
showLaitela: false
totalLevels: 0,
hasRecollection: false,
recollectionReq: 0,
recollectionMult: 1,
showLaitela: false,
laitelaReq: 0,
petWithRecollection: "",
isRunning: false
};
},
methods: {
update() {
this.expMults = this.pets.map(obj => obj.pet.expBoost);
this.currentExpGain = Ra.pets.teresa.baseExp;
this.showReality = Ra.pets.teresa.level > 2;
this.memoriesPerChunk = Ra.productionPerMemoryChunk();
this.totalLevels = Ra.totalPetLevel;
this.hasRecollection = Ra.has(RA_UNLOCKS.RA_RECOLLECTION_UNLOCK);
this.recollectionReq = RA_UNLOCKS.RA_RECOLLECTION_UNLOCK.totalLevels;
this.recollectionMult = RA_UNLOCKS.RA_RECOLLECTION_UNLOCK.effect;
this.showLaitela = Ra.pets.v.isUnlocked;
this.laitelaReq = RA_UNLOCKS.RA_LAITELA_UNLOCK.totalLevels;
this.petWithRecollection = Ra.petWithRecollection;
this.isRunning = Ra.isRunning;
},
startRun() {
Ra.startRun();
if (!resetReality()) return;
Ra.initializeRun();
},
toggleMode() {
Ra.toggleMode();
}
},
computed: {
laitelaUnlock: () => RA_LAITELA_UNLOCK,
laitelaUnlock: () => RA_UNLOCKS.RA_LAITELA_UNLOCK,
pets: () => [
{
pet: Ra.pets.teresa,
scalingUpgradeText: () => `You can charge ${shortenSmallInteger(Ra.totalCharges)} Infinity Upgrades.`,
scalingUpgradeVisible: () => Ra.totalCharges > 0,
scalingUpgradeText: () => `You can charge ${formatInt(Ra.totalCharges)}
Infinity ${pluralize("Upgrade", Ra.totalCharges)}.`,
},
{
pet: Ra.pets.effarig,
scalingUpgradeText: level => `Glyph rarity +${level}% and +${shortenSmallInteger(Math.floor(level / 5))}
additional choices.`,
scalingUpgradeVisible: () => AlchemyResources.all.filter(r => r.isUnlocked).length > 0,
scalingUpgradeText: () => {
const resources = AlchemyResources.all.filter(r => r.isUnlocked).length;
return `You have unlocked ${formatInt(resources)} alchemy ${pluralize("resource", resources)}.`;
},
},
{
pet: Ra.pets.enslaved,
scalingUpgradeText: () => `Stored game time ^
${shorten(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.gameTimeAmplification(), 0, 2)}, stored real time efficiency
+${formatPercents(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.realTimeEfficiency(), 0, 2)} and
+${shorten(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.realTimeCap() / 1000 / 3600, 0, 1)} hours maximum.`,
scalingUpgradeVisible: () => Ra.has(RA_UNLOCKS.IMPROVED_STORED_TIME),
scalingUpgradeText: () => `Stored game time
${formatPow(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.gameTimeAmplification(), 0, 2)} and real time
+${formatInt(RA_UNLOCKS.IMPROVED_STORED_TIME.effect.realTimeCap() / (1000 * 3600))} hours`,
},
{
pet: Ra.pets.v,
scalingUpgradeText: level => `+${shortenSmallInteger(level)} free achievements.`,
scalingUpgradeVisible: () => Math.clampMax(Math.floor(Ra.pets.v.level / 5), 4) > 0,
scalingUpgradeText: level => {
const triadCount = Math.clampMax(Math.floor(level / 5), 4);
return `You have unlocked ${formatInt(triadCount)} triad ${pluralize("study", triadCount, "studies")}.`;
},
}
]
],
petStyle() {
return {
color: (this.petWithRecollection === "")
? "white"
: this.pets.find(pet => pet.pet.name === this.petWithRecollection).pet.color,
};
},
runButtonClassObject() {
return {
"c-ra-run-button__icon": true,
"c-ra-run-button__icon--running": this.isRunning,
};
}
},
template:
`<div class="l-ra-celestial-tab">
template: `
<div class="l-ra-celestial-tab">
<div class="c-ra-memory-header">
You will gain {{ shorten(this.currentExpGain, 2, 2) }}{{ showReality ? " base" : ""}}
memories on Reality, based on glyph level.
Each memory chunk generates
{{ format(memoriesPerChunk, 2, 3) }} {{ "memory" | pluralize(memoriesPerChunk, "memories") }}
per second.
</div>
<div>
Mouse-over bolded numbers to see descriptions of upgrades you have already unlocked.
Hold shift to see progress on your current level.
</div>
<div>
Mouse-over the icons below the bar to see descriptions of upgrades,
<br>
and mouse-over <i class="fas fa-question-circle"></i> icons for specific resource information.
</div>
<div class="l-ra-all-pets-container">
<ra-pet v-for="(pet, i) in pets" :key="i" :petConfig="pet" />
</div>
<div class="l-ra-non-pets">
<button @click="startRun" class="l-ra-reality-container" v-if="showReality">
<div class="l-ra-reality-inner">
<h1> Start Ra's Reality</h1>
<p> Rules: You can't dimension boost and tick reduction is forced to be 11%. </p>
<br>
<br>
<h2> Memory multipliers: </h2>
<div class="c-ra-rewards">
<span class="c-ra-rewards-inner"> Teresa: {{formatX(expMults[0], 2, 2)}} </span>
<span
class="c-ra-rewards-inner"
v-if="pets[1].pet.isUnlocked"> Effarig: {{formatX(expMults[1], 2, 2)}} </span>
</div>
<div class="c-ra-rewards">
<span
class="c-ra-rewards-inner"
v-if="pets[2].pet.isUnlocked"> Enslaved: {{formatX(expMults[2], 2, 2)}} </span>
<span
class="c-ra-rewards-inner"
v-if="pets[3].pet.isUnlocked"> V: {{formatX(expMults[3], 2, 2)}} </span>
</div>
<button class="c-ra-run-button">
<h2> Start Ra's Reality </h2>
<div :class="runButtonClassObject" @click="startRun">
<span class="c-ra-run-button__icon__sigil fas fa-sun"></span>
</div>
You can't Dimension Boost, and the Tickspeed purchase multiplier is fixed at {{ formatX(1.1245, 0, 3) }}.
<br>
<br>
Inside of Ra's reality, some resources will generate memory chunks based on their amount.
</button>
<div class="l-ra-recollection-unlock">
<br>
<h1 :style="petStyle">Recollection</h1>
<span :style="petStyle">
Whichever celestial has recollection will get {{formatX(recollectionMult)}} memory chunk gain.
</span>
<div class="l-ra-recollection-unlock-inner" v-if="hasRecollection">
<ra-pet-recollection-button
v-for="(pet, i) in pets"
:key="i"
:petConfig="pet" />
</div>
<div v-else class="l-ra-recollection-unlock-inner">
Unlocked by getting {{ formatInt(recollectionReq) }} total Celestial Memory levels
(you need {{formatInt(recollectionReq - totalLevels)}} more)
</div>
</div>
<button class="l-ra-laitela-unlock" v-if="showLaitela">
<div class="l-ra-laitela-unlock-inner">
<h1> Lai'tela: </h1>
<h2> The Celestial of Matter </h2>
<p> Unlocked getting all four celestials to level {{ shortenSmallInteger(20) }} </p>
<h2> The Celestial of Dimensions </h2>
<p>
Unlocked by getting {{ formatInt(laitelaReq) }} total Celestial Memory levels
<span v-if="totalLevels < laitelaReq">
(you need {{formatInt(laitelaReq - totalLevels)}} more)
</span>
</p>
</div>
</button>
</div>
</div>`
</div>
`
});

View File

@ -0,0 +1,39 @@
"use strict";
Vue.component("ra-upgrade-icon", {
props: {
unlock: Object,
},
data() {
return {
isUnlocked: false,
level: 0,
icon: "",
description: "",
};
},
computed: {
update() {
this.isUnlocked = Ra.has(this.unlock);
this.level = this.unlock.level;
this.icon = this.unlock.displayIcon;
const rewardText = typeof this.unlock.reward === "function"
? this.unlock.reward()
: this.unlock.reward;
this.description = `Level ${this.level}: ${rewardText}`;
},
classObject() {
return {
"c-ra-upgrade-icon": true,
"c-ra-upgrade-icon--inactive": !this.isUnlocked,
};
}
},
template: `
<div
v-html="icon"
:ach-tooltip="description"
:class="classObject">
</div>
`
});

View File

@ -4,110 +4,53 @@ Vue.component("modal-reality-glyph-creation", {
data() {
return {
realityGlyphLevel: 0,
// This contains an array where each entry is an array looking like [4000, "realitygalaxies"]
possibleEffects: [],
selectedEffects: [],
maxEffects: 0,
effectCriteria: []
};
},
methods: {
update() {
this.realityGlyphLevel = AlchemyResource.reality.effectValue;
this.possibleEffects = orderedEffectList.filter(effect => !effect.match("effarig*"));
this.maxEffects = this.calculateMaxEffects();
this.effectCriteria = this.effectCriteriaList();
},
calculateMaxEffects() {
return this.effectCriteriaList().filter(c => c.value).length;
},
effectCriteriaList() {
const sumOfOtherAlchemyResources = AlchemyResources.all
.filter(r => r !== AlchemyResource.reality)
.map(r => r.amount)
.sum();
const highestLevelGlyph = player.reality.glyphs.active
.concat(player.reality.glyphs.inventory)
.map(glyph => glyph.level)
.max();
return [
{
desc: `At least ${shortenSmallInteger(5000)} Reality consumed for this glyph (Currently ` +
`${shortenSmallInteger(AlchemyResource.reality.amount)})`,
value: AlchemyResource.reality.amount >= 5000
},
{
desc: `${shortenSmallInteger(150000)} total of all other alchemy resources (Currently ` +
`${shortenSmallInteger(sumOfOtherAlchemyResources)})`,
value: sumOfOtherAlchemyResources >= 150000
},
{
desc: `${shorten(1e60, 0, 0)} relic shards (Currently ` +
`${shorten(player.celestials.effarig.relicShards, 2, 2)})`,
value: player.celestials.effarig.relicShards >= 1e60
},
{
desc: `A glyph with a level of at least ${shortenSmallInteger(12000)}, which is not consumed (highest: ` +
`${shortenSmallInteger(highestLevelGlyph)})`,
value: highestLevelGlyph >= 12000
}
];
const realityEffectConfigs = Object.values(GameDatabase.reality.glyphEffects)
.filter(eff => eff.id.match("reality*"))
.sort((a, b) => a.bitmaskIndex - b.bitmaskIndex);
const minRealityEffectIndex = realityEffectConfigs.map(cfg => cfg.bitmaskIndex).min();
this.possibleEffects = realityEffectConfigs
.map(cfg => [realityGlyphEffectLevelThresholds[cfg.bitmaskIndex - minRealityEffectIndex], cfg.id]);
},
createRealityGlyph() {
if (Glyphs.freeInventorySpace === 0) {
Modal.message.show("Inventory cannot hold new glyphs. Delete/sacrifice (shift-click) some glyphs.");
return;
}
Glyphs.addToInventory(GlyphGenerator.realityGlyph(
{ actualLevel: this.realityGlyphLevel, rawLevel: this.realityGlyphLevel }, this.selectedEffects));
AlchemyResources.resetAmount();
// If the player leaves a choice open, don't spend shards
if (player.celestials.effarig.relicShards >= 1e60 && (this.selectedEffects === this.calculateMaxEffects())) {
player.celestials.effarig.relicShards -= 1e60;
}
for (const reaction of AlchemyReactions.all.compact()) {
reaction.isActive = false;
}
Glyphs.addToInventory(GlyphGenerator.realityGlyph(this.realityGlyphLevel));
AlchemyResource.reality.amount = 0;
this.emitClose();
},
formatGlyphEffect(effect) {
const config = GameDatabase.reality.glyphEffects[effect];
if (this.realityGlyphLevel < effect[0]) return `(Requires glyph level ${formatInt(effect[0])})`;
const config = GameDatabase.reality.glyphEffects[effect[1]];
const value = config.effect(this.realityGlyphLevel, rarityToStrength(100));
const effectTemplate = typeof config.singleDesc === "function"
? config.singleDesc()
: config.singleDesc;
const effectText = effectTemplate
.replace("{value}", config.formatEffect(value))
.replace("[", "")
.replace("]", "");
if (config.conversion === undefined) return effectText;
return effectText.replace("{value2}", config.formatEffect(config.conversion(value)));
return effectTemplate.replace("{value}", config.formatEffect(value));
}
},
template: `
<div class="c-reality-glyph-creation">
<modal-close-button @click="emitClose"/>
<div>
Create a level {{ shortenSmallInteger(realityGlyphLevel) }} reality glyph. Rarity will always be 100% and level
is unaffected by the glyph level cap. This will reset all alchemy resources.
<div>
Create a level {{ formatInt(realityGlyphLevel) }} reality glyph. Rarity will always be 100% and level
scales on your current reality resource amount (which is all consumed). All other alchemy resources will be
unaffected. Reality glyphs have unique effects, some of which are only available with higher level glyphs.
Reality glyphs can also be sacrificed to increase the yield from alchemy reactions. Like Effarig glyphs,
you cannot equip more than one at the same time.
</div><br>
<div>
Reality glyphs always have {{ shortenSmallInteger(4) }} effects picked from any of the
{{ shortenSmallInteger(5) }} basic glyph types; you can choose up to {{ shortenSmallInteger(maxEffects) }},
based on how many of the below requirements you meet. The rest of the effects, if any, are chosen randomly.
<div v-for="criterion in effectCriteria">
<span v-if="criterion.value">+</span>
<span v-else>-</span>
{{ criterion.desc }}
</div><br>
</div>
<div>
Possible Effects:
Available Effects:
</div>
<div v-for="effect in possibleEffects">
<input type="checkbox"
:value="effect"
:disabled="selectedEffects.length >= maxEffects && !selectedEffects.includes(effect)"
v-model="selectedEffects">
{{ formatGlyphEffect(effect) }}
</div><br>
<button class="o-primary-btn" v-on:click="createRealityGlyph()">Create a Reality glyph!</button>

View File

@ -1,80 +0,0 @@
"use strict";
Vue.component("time-compression-tab", {
data() {
return {
entanglement: 0,
totalEntanglement: 0,
respec: false,
};
},
watch: {
respec(newValue) {
player.celestials.ra.compression.respec = newValue;
}
},
computed: {
upgrades() {
return [
[
CompressionUpgrade.freeBoost,
CompressionUpgrade.improvedDTMult,
CompressionUpgrade.replicantiSpeedFromDB
],
[
CompressionUpgrade.strongerDilationGalaxies,
CompressionUpgrade.freeGalaxySoftcap,
CompressionUpgrade.freeGalaxyScaling
],
[
CompressionUpgrade.infDimSoftcap,
CompressionUpgrade.moreEntanglement,
CompressionUpgrade.matterBoost
],
];
},
respecClassObject() {
return {
"o-primary-btn--respec-options": true,
"o-primary-btn--respec-active": this.respec
};
}
},
methods: {
update() {
this.entanglement = TimeCompression.entanglement;
this.totalEntanglement = TimeCompression.totalEntanglement;
this.respec = player.celestials.ra.compression.respec;
}
},
template:
`<div class="l-dilation-tab">
<span>
You have
<span class="c-dilation-tab__tachyons">{{shorten(entanglement, 2, 2)}}</span>
Entanglement.
</span>
<span>
Your total entanglement is {{shorten(totalEntanglement, 2, 2)}}.
</span>
<compression-button />
<span>
Time compression is a stronger type of dilation which dilates values twice, <br>
disables time dimensions, and causes the game to run {{ shorten(1e100) }} times slower.
</span>
<div class="l-dilation-upgrades-grid">
<div v-for="row in upgrades" class="l-dilation-upgrades-grid__row">
<compression-upgrade
v-for="upgrade in row"
:key="upgrade.id"
:upgrade="upgrade"
class="l-dilation-upgrades-grid__cell"
/>
</div>
</div>
<primary-button
:class="respecClassObject"
@click="respec = !respec"
>Respec compression upgrades on Reality</primary-button>
</div>`
});

View File

@ -6,7 +6,7 @@ Vue.component("perk-shop-upgrade", {
},
data() {
return {
isAvailable: false,
isAvailableForPurchase: false,
isCapped: false,
};
},
@ -14,24 +14,23 @@ Vue.component("perk-shop-upgrade", {
classObject() {
return {
"o-teresa-shop-button": true,
"o-teresa-shop-button--disabled": !this.isAvailable && !this.isCapped,
"o-teresa-shop-button--available": this.isAvailableForPurchase && !this.isCapped,
"o-teresa-shop-button--capped": this.isCapped
};
}
},
methods: {
update() {
this.isAvailable = this.upgrade.isAvailable;
this.isAvailableForPurchase = this.upgrade.isAvailableForPurchase;
this.isCapped = this.upgrade.isCapped;
}
},
template:
`<div class="l-spoon-btn-group">
<button :class="classObject" @click="upgrade.purchase()">
<description-display
<description-display
:config="upgrade.config"
:length="70"
name="o-compression-upgrade__description"
/>
<br>
<effect-display :config="upgrade.config" />
@ -44,4 +43,4 @@ Vue.component("perk-shop-upgrade", {
/>
</button>
</div>`
});
});

View File

@ -10,11 +10,14 @@ Vue.component("teresa-tab", {
percentage: "",
rmMult: 0,
bestAM: new Decimal(0),
lastRM: new Decimal(0),
runReward: 0,
pp: 0,
hasReality: false,
hasEPGen: false,
hasPerkShop: false,
isRunning: false,
canUnlockNextPour: false
};
},
computed: {
@ -28,6 +31,20 @@ Vue.component("teresa-tab", {
PerkShopUpgrade.autoSpeed,
PerkShopUpgrade.musicGlyph,
];
},
runButtonClassObject() {
return {
"c-teresa-run-button__icon": true,
"c-teresa-run-button__icon--running": this.isRunning,
};
},
pourButtonClassObject() {
return {
"o-teresa-shop-button": true,
"o-teresa-shop-button--available": true,
"c-teresa-pour": true,
"c-teresa-pour--unlock-available": this.canUnlockNextPour
};
}
},
methods: {
@ -39,18 +56,23 @@ Vue.component("teresa-tab", {
}
this.time = now;
this.rmStore = player.celestials.teresa.rmStore;
this.percentage = formatPercents(Teresa.fill, 2);
this.percentage = Notations.current.name === "Blind" ? "0%" : `${(Teresa.fill * 100).toFixed(2)}%`;
this.rmMult = Teresa.rmMultiplier;
this.hasReality = Teresa.has(TERESA_UNLOCKS.RUN);
this.hasEPGen = Teresa.has(TERESA_UNLOCKS.EPGEN);
this.hasPerkShop = Teresa.has(TERESA_UNLOCKS.SHOP);
this.bestAM.copyFrom(player.celestials.teresa.bestRunAM);
this.lastRM.copyFrom(player.celestials.teresa.lastRepeatedRM);
this.runReward = Teresa.runRewardMultiplier;
this.pp = player.reality.pp;
this.rm.copyFrom(player.reality.realityMachines);
this.isRunning = Teresa.isRunning;
this.canUnlockNextPour = Object.values(TERESA_UNLOCKS)
.filter(unlock => this.rm.plus(this.rmStore).gte(unlock.price) && !Teresa.has(unlock)).length > 0;
},
startRun() {
Teresa.startRun();
if (!resetReality()) return;
Teresa.initializeRun();
},
unlockDescriptionStyle(unlockInfo) {
const maxPrice = Teresa.unlockInfo[Teresa.lastUnlock].price;
@ -60,33 +82,35 @@ Vue.component("teresa-tab", {
};
},
},
template:
`<div class="l-teresa-celestial-tab">
template: `
<div class="l-teresa-celestial-tab">
<celestial-quote-history celestial="teresa"/>
<div>You have {{shorten(rm, 2, 2)}} {{"Reality Machine" | pluralize(rm)}}.</div>
<div>You have {{format(rm, 2, 2)}} {{"Reality Machine" | pluralize(rm)}}.</div>
<div class="l-mechanics-container">
<div class="l-teresa-unlocks l-teresa-mechanic-container">
<div class="c-teresa-unlock c-teresa-run-button" v-if="hasReality" @click="startRun()">
<div class="l-teresa-mechanic-container" v-if="hasReality">
<div class="c-teresa-unlock c-teresa-run-button">
<div :class="runButtonClassObject" @click="startRun()">Ϟ</div>
Start Teresa's Reality. Glyph TT generation is disabled and
you gain less IP and EP (x^{{shorten(0.55, 2, 2)}}).
you gain less IP and EP (x^{{format(0.55, 2, 2)}}).
<br><br>
Highest antimatter in Teresa's Reality: {{ shorten(bestAM, 2, 0) }}
<div v-if="bestAM.gt(0)">
Highest antimatter in Teresa's Reality: {{ format(bestAM, 2) }}
<br><br>
You last did Teresa's Reality at {{ format(lastRM, 2) }} RM.
</div>
<div v-else>
You have not completed Teresa's Reality yet.
</div>
</div>
<div class="c-teresa-unlock" v-if="hasReality">
<div class="c-teresa-unlock">
Teresa Reality reward: Glyph sacrifice power {{ formatX(runReward, 2, 2) }}
</div>
<div class="c-teresa-unlock" v-if="hasEPGen">You gain 1% of your peaked EP/min every second.</div>
<div class="c-teresa-shop" v-if="hasPerkShop">
<span class="o-teresa-pp"> You have {{ shorten(pp, 2, 0) }} {{"Perk Point" | pluralize(pp)}}.</span>
<perk-shop-upgrade
v-for="upgrade in upgrades"
:key="upgrade.id"
:upgrade="upgrade"
/>
<div class="c-teresa-unlock" v-if="hasEPGen">
You gain {{ formatPercents(0.01) }} of your peaked EP/min every second.
</div>
</div>
<div class="l-rm-container l-teresa-mechanic-container">
<button class="o-primary-btn c-teresa-pour"
<button :class="pourButtonClassObject"
@mousedown="pour = true"
@touchstart="pour = true"
@mouseup="pour = false"
@ -95,19 +119,27 @@ Vue.component("teresa-tab", {
>Pour RM</button>
<div class="c-rm-store">
<div class="c-rm-store-inner" :style="{ height: percentage}">
<div class="c-rm-store-label"> {{ shorten(rmMult, 2, 2) }}x RM gain
<br>{{ shorten(rmStore, 2, 2) }}/{{ shorten(rmStoreMax, 2, 2) }}
<div class="c-rm-store-label"> {{ formatX(rmMult, 2, 2) }} RM gain
<br>{{ format(rmStore, 2, 2) }}/{{ format(rmStoreMax, 2, 2) }}
</div>
</div>
<div v-for="unlockInfo in unlockInfo"
class="c-teresa-unlock-description"
:style="unlockDescriptionStyle(unlockInfo)"
:id="unlockInfo.id">
{{ shorten(unlockInfo.price, 2, 2) }}: {{ unlockInfo.description }}
{{ format(unlockInfo.price, 2, 2) }}: {{ unlockInfo.description }}
</div>
</div>
</div>
<div class="c-unlock-descriptions l-teresa-mechanic-container"></div>
<div class="l-rm-container-labels l-teresa-mechanic-container"/>
<div class="c-teresa-shop" v-if="hasPerkShop">
<span class="o-teresa-pp"> You have {{ format(pp, 2, 0) }} {{"Perk Point" | pluralize(pp)}}.</span>
<perk-shop-upgrade
v-for="upgrade in upgrades"
:key="upgrade.id"
:upgrade="upgrade"
/>
</div>
</div>
</div>`
});

View File

@ -5,105 +5,193 @@ Vue.component("v-tab", {
return {
mainUnlock: false,
totalUnlocks: 0,
totalAdditionalStudies: 0,
achievementsPerAdditionalStudy: 0,
realities: 0,
infinities: new Decimal(0),
eternities: new Decimal(0),
dilatedTime: new Decimal(0),
replicanti: new Decimal(0),
rm: new Decimal(0),
runRecords: Array.from(player.celestials.v.runRecords),
runGlyphs: player.celestials.v.runGlyphs.map(g => this.copyGlyphs(g)),
pp: 0,
showReduction: false,
runRecords: [],
runGlyphs: [],
isFlipped: false,
isRunning: false
};
},
methods: {
update() {
this.mainUnlock = V.has(V_UNLOCKS.MAIN_UNLOCK);
this.totalUnlocks = V.totalRunUnlocks;
this.totalAdditionalStudies = V.totalAdditionalStudies;
this.achievementsPerAdditionalStudy = V.achievementsPerAdditionalStudy;
this.mainUnlock = V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK);
this.totalUnlocks = V.spaceTheorems;
this.realities = player.realities;
this.infinities.copyFrom(player.infinitied);
this.eternities.copyFrom(player.eternities);
this.dilatedTime.copyFrom(player.dilation.dilatedTime);
this.replicanti.copyFrom(player.replicanti.amount);
this.rm.copyFrom(player.reality.realityMachines);
this.pp = player.reality.pp;
this.showReduction = V.has(V_UNLOCKS.SHARD_REDUCTION);
this.runRecords = Array.from(player.celestials.v.runRecords);
this.runGlyphs = player.celestials.v.runGlyphs.map(g => this.copyGlyphs(g));
},
copyGlyphs(glyphList) {
return glyphList.map(g => ({
type: g.type,
level: g.level,
strength: g.strength,
effects: g.effects,
}));
this.runGlyphs = player.celestials.v.runGlyphs.map(gList => Glyphs.copyForRecords(gList));
this.isFlipped = V.isFlipped;
this.isRunning = V.isRunning;
},
startRun() {
V.startRun();
if (!resetReality()) return;
V.initializeRun();
},
has(info) {
return V.has(info);
},
mode(hex) {
return hex.config.mode === V_REDUCTION_MODE.SUBTRACTION ? "reduced" : "divided";
},
reductionValue(hex) {
return hex.config.mode === V_REDUCTION_MODE.SUBTRACTION
? formatInt(hex.reduction)
: format(Decimal.pow10(hex.reduction));
},
showRecord(hex) {
return this.runRecords[hex.id] > 0 || hex.completions > 0;
},
rewardText(milestone) {
return typeof milestone.reward === "function"
? milestone.reward()
: milestone.reward;
},
reduceGoals(hex) {
if (player.reality.pp < hex.reductionCost) return;
player.reality.pp -= hex.reductionCost;
const steps = hex.config.reductionStepSize ? hex.config.reductionStepSize : 1;
player.celestials.v.goalReductionSteps[hex.id] += steps;
},
reductionTooltip(hex) {
return `Spend ${format(hex.reductionCost, 2, 0)} PP to reduce goal by ${format(hex.config.perReductionStep)}`;
}
},
computed: {
hexGrid: () => [
VRunUnlocks.all[0],
VRunUnlocks.all[1],
{},
VRunUnlocks.all[2],
{ isRunButton: true },
VRunUnlocks.all[3],
VRunUnlocks.all[4],
VRunUnlocks.all[5],
{}
],
runMilestones: () => V_UNLOCKS.RUN_UNLOCK_THRESHOLDS,
// If V is flipped, change the layout of the grid
hexGrid() {
return this.isFlipped ? [
VRunUnlocks.all[6],
{},
{},
{},
{ isRunButton: true },
VRunUnlocks.all[7],
VRunUnlocks.all[8],
{},
{}
]
: [
VRunUnlocks.all[0],
VRunUnlocks.all[1],
{},
VRunUnlocks.all[2],
{ isRunButton: true },
VRunUnlocks.all[3],
VRunUnlocks.all[4],
VRunUnlocks.all[5],
{}
];
},
vUnlock: () => V_UNLOCKS.V_ACHIEVEMENT_UNLOCK,
runMilestones() {
return [
[
V_UNLOCKS.SHARD_REDUCTION,
V_UNLOCKS.ND_POW,
V_UNLOCKS.FAST_AUTO_EC
],
[
V_UNLOCKS.AUTO_AUTOCLEAN,
V_UNLOCKS.ACHIEVEMENT_BH,
V_UNLOCKS.RA_UNLOCK
],
];
},
db: () => GameDatabase.celestials.v,
runButtonClassObject() {
return {
"l-v-hexagon": true,
"c-v-run-button": true,
"c-v-run-button--running": this.isRunning,
};
}
},
template:
`<div class="l-v-celestial-tab">
template: `
<div class="l-v-celestial-tab">
<div v-if="!mainUnlock">
You need {{ shorten(db.mainUnlock.realities, 2, 0) }} realities (currently {{ shorten(realities, 2, 0) }}),<br>
{{ shorten(db.mainUnlock.eternities, 2, 0) }} eternities (currently {{ shorten(eternities, 2, 0) }}),<br>
{{ shorten(db.mainUnlock.infinities, 2, 0) }} infinities (currently {{ shorten(infinities, 2, 0) }}),<br>
{{ shorten(db.mainUnlock.dilatedTime, 2, 0) }} dilated time (currently {{ shorten(dilatedTime, 2, 0) }}),<br>
{{ shorten(db.mainUnlock.replicanti, 2, 0) }} replicanti (currently {{ shorten(replicanti, 2, 0) }}),<br>
and {{ shorten(db.mainUnlock.rm, 2, 0) }} RM (currently {{ shorten(rm, 2, 0) }})
to unlock V, The Celestial of Achievements
{{ format(rm, 2, 0) }} / {{ format(db.mainUnlock.rm, 2, 0) }} RM
<br>
{{ format(realities, 2, 0) }} / {{ format(db.mainUnlock.realities, 2, 0) }} realities
<br>
{{ format(eternities, 2, 0) }} / {{ format(db.mainUnlock.eternities, 2, 0) }} eternities
<br>
{{ format(infinities, 2, 0) }} / {{ format(db.mainUnlock.infinities, 2, 0) }} infinities
<br>
{{ format(dilatedTime, 2, 0) }} / {{ format(db.mainUnlock.dilatedTime, 2, 0) }} dilated time
<br>
{{ format(replicanti, 2, 0) }} / {{ format(db.mainUnlock.replicanti, 2, 0) }} replicanti
<br>
<div class="l-v-milestones-grid__row">
<div class="o-v-milestone">
<p>{{ vUnlock.description }}</p>
<p>Reward: {{ rewardText(vUnlock) }}</p>
</div>
</div>
</div>
<div v-else>
<div v-if="isFlipped">
Cursed glyphs can be created in the Effarig tab, and the Black Hole can now be used to slow down time.
<br>
Each hard V-achievement will award {{ formatInt(2) }} Space Theorems instead of {{ formatInt(1) }}.
<br>
Goal reduction is significantly more expensive for hard V-achievements.
</div>
<div v-if="showReduction">
You have {{ format(pp, 2, 0) }} {{ "Perk Point" | pluralize(pp) }}.
</div>
<div class="l-v-unlocks-container">
<li v-for="hex in hexGrid">
<li v-for="hex in hexGrid" :style= "[hex.isRunButton ? {} : {zIndex: -1}]">
<div v-if="hex.config"
class="l-v-hexagon c-v-unlock"
:class="{ 'c-v-unlock-completed': hex.completions == 6 }">
<p class="o-v-unlock-name">{{ hex.config.name }}</p>
<p class="o-v-unlock-desc">{{ hex.formattedDescription }}</p>
:class="{ 'c-v-unlock-completed': hex.completions === hex.config.values.length }">
<p class="o-v-unlock-name"><br v-if="hex.canBeReduced && showReduction">{{ hex.config.name }}</p>
<p class="o-v-unlock-desc" v-html="hex.formattedDescription"></p>
<p class="o-v-unlock-goal-reduction" v-if="has(runMilestones[0]) && hex.isReduced">
Goal has been {{ mode(hex) }} by {{ reductionValue(hex) }}
</p>
<p class="o-v-unlock-amount">{{ hex.completions }}/{{hex.config.values.length}} done</p>
<p class="o-v-unlock-record">
Best: {{ hex.config.formatRecord(runRecords[hex.id]) }}
</p>
<p v-if="runRecords[hex.id] > 0">
<glyph-component v-for="(g, idx) in runGlyphs[hex.id]"
:key="idx"
style="margin: 0.2rem;"
:glyph="g"
:showSacrifice="false"
:draggable="false"
:circular="true"
size="2.8rem"
:textProportion="0.6"
glowBlur="0.2rem"
glowSpread="0.1rem" />
</p>
<div v-if="showRecord(hex)">
<p class="o-v-unlock-record">
Best: {{ hex.config.formatRecord(runRecords[hex.id]) }}
</p>
<p>
<glyph-set-preview
:show=true
:glyphs="runGlyphs[hex.id]" />
</p>
<div v-if="hex.canBeReduced && showReduction">
<div style="height:0.8rem;"/>
<button
class="o-primary-btn l-v-reduction"
:class="{ 'o-primary-btn--disabled': !hex.canBeReduced || pp < hex.reductionCost }"
:ach-tooltip="reductionTooltip(hex)"
@click="reduceGoals(hex)">
<i class="fas fa-angle-double-down"></i>
</button>
</div>
</div>
</div>
<div v-else-if="hex.isRunButton" @click="startRun()" class="l-v-hexagon o-v-run-button">
<p>
Start V's Reality.<br/>All dimension multipliers, EP gain, IP gain, and dilated time gain per second
<div v-else-if="hex.isRunButton" @click="startRun()" :class="runButtonClassObject">
<b style="font-size: 1.5rem">Start V's Reality.</b>
<br/>
All dimension multipliers, EP gain, IP gain, and Dilated Time gain per second
are square-rooted, and Replicanti interval is squared.
</p>
<div class="c-v-run-button__line c-v-run-button__line--1"></div>
<div class="c-v-run-button__line c-v-run-button__line--2"></div>
<div class="c-v-run-button__line c-v-run-button__line--3"></div>
</div>
<div v-else>
<div style="opacity: 0" class="l-v-hexagon"></div>
@ -111,27 +199,24 @@ Vue.component("v-tab", {
</li>
</div>
<div>
V-achievements can only be completed within V's reality, but are permanent and do not reset upon leaving
and re-entering the reality.
V-achievements can only be completed within V's Reality, but are permanent and do not reset upon leaving
and re-entering the Reality.
</div>
<div>
You have {{ shortenSmallInteger(totalUnlocks) }} V-achievements done. You can pick
{{ shortenSmallInteger(totalAdditionalStudies) }}
{{ "study" | pluralize(totalAdditionalStudies, "studies") }} on other paths you normally can't buy.
</div>
<div>
(You get one additional study per {{ shortenSmallInteger(achievementsPerAdditionalStudy) }}
V-achievements, rounded down.)
You have {{ formatInt(totalUnlocks) }} V-achievements done. You gain 1 Space Theorem for each completion,
allowing you to purchase Time Studies which are normally locked.
</div>
<br>
<div class="l-v-milestones-container">
<div class="o-v-milestone"
v-for="milestone in runMilestones"
:class="{'o-v-milestone-unlocked':
has(milestone)}">
<p>{{ milestone.description }}</p>
<p>Reward: {{ milestone.reward }}</p>
<p v-if="milestone.effect">Currently: <b>{{ milestone.format(milestone.effect()) }}</b></p>
<div class="l-v-milestones-grid">
<div v-for="row in runMilestones" class="l-v-milestones-grid__row">
<div class="o-v-milestone"
v-for="milestone in row"
:class="{'o-v-milestone--unlocked':
has(milestone)}">
<p>{{ milestone.description }}</p>
<p>Reward: {{ rewardText(milestone) }}</p>
<p v-if="milestone.effect">Currently: <b>{{ milestone.format(milestone.effect()) }}</b></p>
</div>
</div>
</div>
</div>

View File

@ -11,33 +11,53 @@ Vue.component("challenge-box", {
default: "",
}
},
data() {
return {
isEC: false,
};
},
computed: {
update() {
this.isEC = this.name.startsWith("EC");
this.inC1 = this.name === "C1" && !this.isCompleted && NormalChallenge.current === undefined &&
InfinityChallenge.current === undefined;
},
buttonClassObject() {
const classObject = {
"o-challenge-btn": true
};
if (this.isRunning) {
if (this.isRunning || this.inC1) {
classObject["o-challenge-btn--running"] = true;
} else if (this.isCompleted) {
} else if (this.isCompleted && ((this.isUnlocked && !this.isEC) || (!this.isUnlocked && this.isEC))) {
classObject["o-challenge-btn--completed"] = true;
} else if (this.isCompleted && this.isUnlocked && this.isEC) {
classObject["o-challenge-btn--redo"] = true;
} else if (this.isUnlocked) {
classObject["o-challenge-btn--unlocked"] = true;
} else {
classObject["o-challenge-btn--locked"] = true;
}
// ECs can be not unlocked and also not locked, because they're fully completed,
// but in that case you can't enter them and so it's important to give them a property
// that disables cursor on hover. The same thing happens with challenges that are running,
// of any type, and with Challenge 1.
classObject["o-challenge-btn--unenterable"] = !this.isUnlocked || this.isRunning || this.name === "C1";
return classObject;
},
buttonText() {
if (this.overrideLabel.length) return this.overrideLabel;
if (this.isRunning) return "Running";
if (this.isCompleted) return "Completed";
if (this.isRunning || this.inC1) return "Running";
if (this.isCompleted) {
if (this.isEC && this.isUnlocked) return "Redo";
return "Completed";
}
if (this.isUnlocked) return "Start";
return "Locked";
}
},
template:
`<div class="c-challenge-box l-challenge-box">
<hint-text class="l-hint-text--challenge">{{name}}</hint-text>
<hint-text type="challenges" class="l-hint-text--challenge">{{name}}</hint-text>
<slot name="top" />
<div class="l-challenge-box__fill" />
<button

View File

@ -13,7 +13,6 @@ Vue.component("challenges-header", {
remainingECTiers: 0,
untilNextEC: TimeSpan.zero,
untilAllEC: TimeSpan.zero,
newEC10: false,
};
},
watch: {
@ -23,9 +22,6 @@ Vue.component("challenges-header", {
showAllChallenges(newValue) {
player.options.showAllChallenges = newValue;
},
newEC10(newValue) {
player.newEC10Test = newValue;
}
},
methods: {
update() {
@ -34,7 +30,7 @@ Vue.component("challenges-header", {
Object.keys(player.eternityChalls).length > 0;
this.isECTabUnlocked = isECTabUnlocked;
const isICTabUnlocked = isECTabUnlocked ||
player.antimatter.gte(new Decimal("1e2000")) ||
Currency.antimatter.exponent >= 2000 ||
player.postChallUnlocked > 0;
this.isICTabUnlocked = isICTabUnlocked;
this.isInChallenge = NormalChallenge.isRunning || InfinityChallenge.isRunning || EternityChallenge.isRunning;
@ -49,7 +45,15 @@ Vue.component("challenges-header", {
this.untilNextEC.setFrom(untilNextEC);
this.untilAllEC.setFrom(untilNextEC + (autoECInterval * (remainingCompletions - 1)));
}
this.newEC10 = player.newEC10Test;
},
restartChallenge() {
const current = NormalChallenge.current ||
InfinityChallenge.current ||
EternityChallenge.current;
if (current !== undefined) {
current.exit();
current.start();
}
},
exitChallenge() {
const current = NormalChallenge.current ||
@ -59,41 +63,32 @@ Vue.component("challenges-header", {
current.exit();
}
},
toggleShowAll() {
this.showAllChallenges = !this.showAllChallenges;
},
toggleAutoEC() {
this.autoEC = !this.autoEC;
},
toggleNewEC10() {
this.newEC10 = !this.newEC10;
},
},
template:
`<div class="l-challenges-tab__header">
<primary-button v-if="isInChallenge"
class="o-primary-btn--exit-challenge l-challenges-tab__exit-btn"
@click="exitChallenge">
Exit Challenge
</primary-button>
<div>
<div class="o-challenges-tab__header-toggle"
@click="toggleNewEC10">
<input :checked="newEC10" type="checkbox" class="o-big-checkbox" />
<b>EC10 Reward Test</b>
</div>
<div v-if="isShowAllVisible"
class="o-challenges-tab__header-toggle"
@click="toggleShowAll">
<input :checked="showAllChallenges" type="checkbox" class="o-big-checkbox" />
<b>Show all</b>
</div>
<div v-if="isAutoECVisible"
class="o-challenges-tab__header-toggle"
@click="toggleAutoEC">
<input :checked="autoEC" type="checkbox" class="o-big-checkbox" />
<b>Auto EC completion</b>
</div>
template: `
<div class="l-challenges-tab__header">
<div class="c-subtab-option-container" v-if="isShowAllVisible || isAutoECVisible || isInChallenge">
<primary-button-on-off v-if="isShowAllVisible"
v-model="showAllChallenges"
class="o-primary-btn--subtab-option"
text="Show all challenges:"
/>
<primary-button-on-off v-if="isAutoECVisible"
v-model="autoEC"
class="o-primary-btn--subtab-option"
text="Auto EC:"
/>
<primary-button v-if="isInChallenge"
class="o-primary-btn--subtab-option"
@click="restartChallenge"
>
Restart Challenge
</primary-button>
<primary-button v-if="isInChallenge"
class="o-primary-btn--subtab-option"
@click="exitChallenge"
>
Exit Challenge
</primary-button>
</div>
<div v-if="autoEC && isAutoECVisible && remainingECTiers > 0"
class="c-challenges-tab__auto-ec-info l-challenges-tab__auto-ec-info">
@ -104,4 +99,4 @@ Vue.component("challenges-header", {
</div>
</div>
`
})
});

View File

@ -83,10 +83,10 @@ Vue.component("eternity-challenges-tab", {
this.enslavedSpanOverride = Enslaved.isRunning && this.challenge.id === 1;
},
start() {
this.challenge.start();
this.challenge.requestStart();
},
goalAtCompletions(completions) {
return shorten(this.challenge.goalAtCompletions(completions), 2, 1);
return format(this.challenge.goalAtCompletions(completions), 2, 1);
}
},
template:
@ -101,7 +101,7 @@ Vue.component("eternity-challenges-tab", {
<description-display :config="config" slot="top" />
<template slot="bottom">
<div :style="{ visiblity: completions < 5 ? 'visible' : 'hidden' }">
<div>Completed {{shortenSmallInteger(completions)}} {{"time" | pluralize(completions)}}</div>
<div>Completed {{formatInt(completions)}} {{"time" | pluralize(completions)}}</div>
<div v-if="!isCompleted">{{goalDisplay}}</div>
</div>
<span v-if="showGoalSpan">Goal Span: {{firstGoal}} IP - {{lastGoal}} IP</span>
@ -141,10 +141,10 @@ Vue.component("eternity-challenges-tab", {
template:
`<div class="l-challenges-tab">
<challenges-header/>
<div>Complete Eternity Challenges again for a bigger reward, maximum of {{shortenSmallInteger(5)}} times.</div>
<div>Complete Eternity Challenges again for a bigger reward, maximum of {{formatInt(5)}} times.</div>
<div>
(You have unlocked {{shortenSmallInteger(unlockedCount)}}
out of {{shortenSmallInteger(12)}} Eternity Challenges)
(You have unlocked {{formatInt(unlockedCount)}}
out of {{formatInt(12)}} Eternity Challenges)
</div>
<challenge-grid :count="12" :isChallengeVisible="isChallengeVisible">
<eternity-challenge-box slot-scope="slotProps" :challengeId="slotProps.challengeId" />

View File

@ -39,14 +39,14 @@ Vue.component("infinity-challenges-tab", {
:isRunning="isRunning"
:isCompleted="isCompleted"
class="c-challenge-box--infinity"
@start="challenge.start()"
@start="challenge.requestStart()"
>
<template slot="top">
<description-display :config="config" />
<effect-display v-if="isRunning" :config="config" />
</template>
<div slot="bottom" class="l-challenge-box__bottom--infinity">
<span>Goal: {{shorten(config.goal, 0, 0)}} antimatter</span>
<span>Goal: {{format(config.goal, 0, 0)}} antimatter</span>
<description-display :config="config.reward" title="Reward:"/>
<effect-display v-if="isCompleted" :config="config.reward" />
</div>
@ -65,7 +65,7 @@ Vue.component("infinity-challenges-tab", {
: undefined;
return next === undefined
? "All Infinity Challenges unlocked"
: `Next challenge unlocks at ${this.shorten(next, 0, 0)} antimatter.`;
: `Next challenge unlocks at ${format(next, 0, 0)} antimatter.`;
}
},
methods: {

View File

@ -17,18 +17,20 @@ Vue.component("normal-challenges-tab", {
challenge() {
return NormalChallenge(this.challengeId);
},
config() {
return this.challenge.config;
},
name() {
return `C${this.challengeId}`;
},
overrideLabel() {
return this.isBroken ? "Broken" : "";
}
},
},
methods: {
update() {
this.isRunning = this.challenge.isRunning;
this.isBroken = Enslaved.isRunning &&
!Enslaved.BROKEN_CHALLENGE_EXEMPTIONS.includes(this.challengeId);
this.isBroken = Enslaved.isRunning && Enslaved.BROKEN_CHALLENGES.includes(this.challengeId);
this.isCompleted = this.challenge.isCompleted && !this.isBroken;
}
},
@ -40,9 +42,9 @@ Vue.component("normal-challenges-tab", {
:isCompleted="isCompleted"
:overrideLabel="overrideLabel"
class="c-challenge-box--normal"
@start="challenge.start()"
@start="challenge.requestStart()"
>
<span slot="top">{{challenge.config.description}}</span>
<description-display :config="config" slot="top" />
<span slot="bottom">Reward: {{challenge.config.reward}}</span>
</challenge-box>`
}

View File

@ -982,7 +982,9 @@ Vue.component("ad-slider-component", {
}
},
dotContents() {
return this.valueInDot ? '' + this.getValue() : '';
// Doesn't work if the slider needs to show more precision than integers,
// but I don't think we have any such sliders.
return this.valueInDot ? Math.round(this.getValue()) : '';
},
},
mounted() {

View File

@ -42,7 +42,7 @@ Vue.component("cost-display", {
},
computed: {
costDisplay() {
return this.formatCost ? this.formatCost(this.cost) : this.shorten(this.cost, 0, 0);
return this.formatCost ? this.formatCost(this.cost) : format(this.cost);
}
},
methods: {

View File

@ -37,7 +37,7 @@ Vue.component("description-display", {
const classObject = {};
classObject[name] = true;
if (this.length !== undefined && this.description.length >= this.length) {
classObject[name + "--small-text"] = true;
classObject[`${name}--small-text`] = true;
}
return classObject;
}

View File

@ -40,11 +40,17 @@ Vue.component("effect-display", {
this.updateFn = isNumber
? () => this.effectValue = effect()
: () => this.effectValue.copyFrom(effect());
const cap = config.cap;
// If the config has a reachedCapFn, we assume its effect value calculation
// takes account of the cap itself, so we don't have to.
const cap = config.reachedCapFn === undefined ? config.cap : () => this.effectValue;
if (cap === undefined) return;
if (config.reachedCapFn) {
this.reachedCapFn = config.reachedCapFn;
} else {
this.reachedCapFn = isNumber
? () => this.effectValue >= this.cap
: () => this.effectValue.gte(this.cap);
}
if (typeof cap !== "function") {
this.hasCap = true;
this.cap = isNumber ? cap : Decimal.fromDecimal(cap);
@ -68,7 +74,7 @@ Vue.component("effect-display", {
return this.reachedCapFn();
},
titleDisplay() {
if (this.config.staticEffect) return undefined;
if (this.config.noTitle) return "";
return `${this.hasCap && this.reachedCap ? "Capped" : this.title}: `;
},
effectDisplay() {

View File

@ -81,16 +81,16 @@ Vue.component("expanding-control-box", {
},
updateBaseWidth() {
if (this.widthSource === "content") {
this.$refs.container.style.width = this.$refs.dropdown.offsetWidth + "px";
this.$refs.root.style.width = this.$refs.dropdown.offsetWidth + "px";
this.$refs.container.style.width = `${this.$refs.dropdown.offsetWidth}px`;
this.$refs.root.style.width = `${this.$refs.dropdown.offsetWidth}px`;
} else if (this.widthSource === "header") {
this.$refs.root.style.width = this.$refs.container.offsetWidth + "px";
this.$refs.root.style.width = `${this.$refs.container.offsetWidth}px`;
}
},
updateHeightInfo() {
const headerHeight = this.$refs.expandButton.offsetHeight;
this.closedHeight = headerHeight + "px";
this.openHeight = (headerHeight + this.$refs.dropdown.offsetHeight) + "px";
this.closedHeight = `${headerHeight}px`;
this.openHeight = `${headerHeight + this.$refs.dropdown.offsetHeight}px`;
},
transitionEnd(event) {
if (event.propertyName !== "max-height") return;

View File

@ -7,7 +7,7 @@ Vue.component("footer-links", {
};
},
created() {
this.on$(GameEvent.TAB_CHANGED, () => this.isVisible = !Tab.eternity.studies.isOpen);
this.on$(GAME_EVENT.TAB_CHANGED, () => this.isVisible = !Tab.eternity.studies.isOpen);
},
template:
`<div v-if="isVisible" class="o-footer">

View File

@ -1,6 +1,16 @@
"use strict";
Vue.component("hint-text", {
props: {
type: String
},
computed: {
showThisHintText() {
// Accessing the player object in this computed is intentional for the sake of performance.
// Always access the player object in update method and store required stuff in component data.
return player.options.showHintText[this.type];
}
},
template:
`<div v-show="$viewModel.shiftDown" class="o-hint-text l-hint-text"><slot /></div>`
`<div v-show="$viewModel.shiftDown || showThisHintText" class="o-hint-text l-hint-text"><slot /></div>`
});

View File

@ -1,6 +1,11 @@
"use strict";
Vue.component("news-ticker", {
data() {
return {
recentTickers: [],
};
},
watch: {
isHidden() {
this.restart();
@ -36,20 +41,34 @@ Vue.component("news-ticker", {
prepareNextMessage() {
const line = this.$refs.line;
if (line === undefined) return;
const isUnlocked = news => news.unlocked || news.unlocked === undefined;
if (this.currentNews && this.currentNews.id === "a236") {
this.currentNews = GameDatabase.news.find(message => message.id === "a216");
if (nextNewsMessageId && GameDatabase.news.find(message => message.id === nextNewsMessageId)) {
this.currentNews = GameDatabase.news.find(message => message.id === nextNewsMessageId);
nextNewsMessageId = undefined;
} else if (this.currentNews && this.currentNews.id === "a236") {
this.currentNews = GameDatabase.news
.filter(message => message.isAdvertising && isUnlocked(message))
.randomElement();
} else {
const isUnlocked = news => news.unlocked || news.unlocked === undefined;
do {
this.currentNews = GameDatabase.news.randomElement();
} while (!isUnlocked(this.currentNews));
const isAI = Math.random() < player.options.news.AIChance;
this.currentNews = GameDatabase.news
.filter(message => message.id.includes("ai") === isAI)
.filter(message => !this.recentTickers.includes(message) && isUnlocked(message))
.randomElement();
// Prevent tickers from repeating if they were seen recently
this.recentTickers.push(this.currentNews.id);
while (this.recentTickers.length > player.options.news.repeatBuffer) this.recentTickers.shift();
}
if (this.currentNews.reset) {
this.currentNews.reset();
}
line.innerHTML = this.currentNews.text;
line.style["transition-duration"] = "0ms";
if (this.currentNews && this.currentNews.id === "a244") {
if (this?.currentNews.id === "a244" || this?.currentNews.id === "ai63" ) {
line.style.transform = "translateX(-100%)";
} else {
line.style.transform = "translateX(0)";
@ -62,7 +81,7 @@ Vue.component("news-ticker", {
const line = this.$refs.line;
// SCROLL_SPEED is in pixels per second
const SCROLL_SPEED = 100;
const SCROLL_SPEED = player.options.news.speed * 100;
const scrollDuration = (this.$refs.ticker.clientWidth + line.clientWidth) / SCROLL_SPEED;
line.style["transition-duration"] = `${scrollDuration}s`;
@ -78,39 +97,13 @@ Vue.component("news-ticker", {
this.scrollTimeout = setTimeout(this.prepareNextMessage.bind(this), scrollDuration * 1000);
},
onLineClick() {
if (this.currentNews.id === "a130") {
if (this.currentNews.onClick !== undefined) {
SecretAchievement(24).unlock();
}
if (this.currentNews.id === "a196") {
let random = Math.random();
// Golden ratio
random += 0.618033988749895;
random %= 1;
random *= 255;
const color = `hsl(${random}, 90%, 60%)`;
this.$refs.line.innerHTML =
`<span style='color: ${color}; text-shadow: 0 0 0.5rem ${color}; animation: text-grow 0.4s infinite;'>
Disco Time!</span>`;
}
if (this.currentNews.id === "a210") {
player.secretUnlocks.uselessNewsClicks++;
this.$refs.line.innerHTML = this.currentNews.text;
}
if (this.currentNews.id === "a247") {
if (this.$refs.line.innerHTML.includes("This")) {
this.$refs.line.innerHTML =
"¡uʍop ǝpᴉsdn ɯǝɥʇ dᴉlɟ oʇ sǝƃɐssǝɯ sʍǝu uo ʞɔᴉlɔ oʇ ʎʇᴉlᴉqɐ ǝɥʇ ǝʞᴉl sƃuᴉɥʇ ǝɹnʇɐǝɟ llᴉʍ 0˙ᄅ sʍǝN " +
"˙,,0˙ᄅ sʍǝN,, ɟo ʇsǝʇ ɐ sᴉ ǝƃɐssǝɯ sʍǝu sᴉɥ┴";
} else {
this.$refs.line.innerHTML =
"This news message is a test of \"News 2.0\". News 2.0 will feature things like the ability to click " +
"on news messages to flip them upside down!";
const updatedText = this.currentNews.onClick();
if (updatedText !== undefined) {
this.$refs.line.innerHTML = updatedText;
}
}
if (this.currentNews.id === "a289") {
player.secretUnlocks.paperclips++;
GameOptions.toggleNews();
}
}
},
template: `

View File

@ -22,7 +22,7 @@ Vue.component("plus-minus-button", {
height: this.size,
position: "relative !important",
"border-radius": "50% !important",
border: "1px solid " + this.color + " !important",
border: `0.1rem solid ${this.color} !important`,
};
},
horizStyle() {

View File

@ -40,9 +40,22 @@ Vue.component("primary-button-on-off", {
'<primary-button v-bind="$attrs" @click="emitInput(!value)">{{displayText}}</primary-button>',
computed: {
displayText() {
let text = this.text;
text = text && text.length > 0 ? text + " " : "";
return this.value ? text + "ON" : text + "OFF";
return `${this.text} ${this.value ? "ON" : "OFF"}`.trim();
}
}
});
Vue.component("primary-button-cycle", {
props: {
text: String,
labels: Array,
value: Number
},
template:
'<primary-button v-bind="$attrs" @click="emitInput((value + 1) % labels.length)">{{displayText}}</primary-button>',
computed: {
displayText() {
return `${this.text} ${this.labels[this.value]}`.trim();
}
}
});

View File

@ -0,0 +1,99 @@
"use strict";
Vue.component("antimatter-dim-galaxy-row", {
data() {
return {
type: GALAXY_TYPE.NORMAL,
galaxies: {
normal: 0,
replicanti: 0,
dilation: 0
},
requirement: {
tier: 1,
amount: 0
},
canBeBought: false,
distantStart: 0,
lockText: null
};
},
computed: {
dimName() {
return AntimatterDimension(this.requirement.tier).displayName;
},
buttonText() {
return this.lockText === null
? "Reset your Dimensions and Dimension Boosts for a tickspeed boost"
: this.lockText;
},
sumText() {
const parts = [this.galaxies.normal];
if (this.galaxies.replicanti > 0) parts.push(this.galaxies.replicanti);
if (this.galaxies.dilation > 0) parts.push(this.galaxies.dilation);
const sum = parts.map(formatInt).join(" + ");
if (parts.length >= 2) {
return `${sum} = ${formatInt(parts.sum())}`;
}
return sum;
},
typeName() {
switch (this.type) {
case GALAXY_TYPE.NORMAL: return "Antimatter Galaxies";
case GALAXY_TYPE.DISTANT: return "Distant Antimatter Galaxies";
case GALAXY_TYPE.REMOTE: return "Remote Antimatter Galaxies";
}
return undefined;
},
hasIncreasedScaling() {
return this.type !== GALAXY_TYPE.NORMAL;
},
costScalingText() {
switch (this.type) {
case GALAXY_TYPE.DISTANT:
return `Each galaxy is more expensive past ${formatInt(this.distantStart)} galaxies`;
case GALAXY_TYPE.REMOTE:
return "Increased galaxy cost scaling: " +
`Quadratic past ${formatInt(this.distantStart)} (distant),
exponential past ${formatInt(800)} (remote)`;
}
return undefined;
},
tutorialClass() {
return Tutorial.glowingClass(TUTORIAL_STATE.GALAXY, this.canBeBought);
}
},
methods: {
update() {
this.type = Galaxy.type;
this.galaxies.normal = player.galaxies;
this.galaxies.replicanti = Replicanti.galaxies.total;
this.galaxies.dilation = player.dilation.freeGalaxies;
const requirement = Galaxy.requirement;
this.requirement.amount = requirement.amount;
this.requirement.tier = requirement.tier;
this.canBeBought = requirement.isSatisfied && Galaxy.canBeBought;
this.distantStart = EternityChallenge(5).isRunning ? 0 : Galaxy.costScalingStart;
this.lockText = Galaxy.lockText;
},
buyGalaxy(bulk) {
requestGalaxyReset(bulk);
Tutorial.turnOffEffect(TUTORIAL_STATE.GALAXY);
},
},
template:
`<div class="c-antimatter-dim-row">
<div class="c-dim-row__label c-dim-row__label--growable" style="height: 6rem;">
{{typeName}} ({{sumText}}):
requires {{formatInt(requirement.amount)}} {{dimName}} Dimensions
<div style="height: 2rem;">{{ hasIncreasedScaling ? costScalingText : "" }}</div>
</div>
<primary-button
:enabled="canBeBought"
class="o-primary-btn--galaxy l-dim-row__button l-dim-row__button--right-offset"
:class="tutorialClass"
@click.exact="buyGalaxy(true)"
@click.shift.exact="buyGalaxy(false)"
>{{buttonText}}</primary-button>
</div>`
});

View File

@ -0,0 +1,134 @@
"use strict";
Vue.component("antimatter-dim-row", {
props: {
floatingText: Array,
tier: Number
},
data() {
return {
isUnlocked: false,
isCapped: false,
multiplier: new Decimal(0),
amount: new Decimal(0),
boughtBefore10: 0,
rateOfChange: new Decimal(0),
singleCost: new Decimal(0),
until10Cost: new Decimal(0),
isAffordable: false,
isAffordableUntil10: false,
isContinuumActive: false,
continuumValue: 0
};
},
computed: {
name() {
return AntimatterDimension(this.tier).shortDisplayName;
},
amountDisplay() {
return this.tier < 8 ? format(this.amount, 2, 0) : formatInt(this.amount);
},
rateOfChangeDisplay() {
return this.tier < 8
? ` (+${format(this.rateOfChange, 2, 2)}%/s)`
: "";
},
cappedTooltip() {
return this.isCapped
? "Further eighth dimension purchases are prohibited, as they are the only way to acquire galaxies"
: null;
},
continuumString() {
return formatFloat(this.continuumValue, 2);
}
},
methods: {
update() {
const tier = this.tier;
const isUnlocked = AntimatterDimension(tier).isAvailableForPurchase;
this.isUnlocked = isUnlocked;
if (!isUnlocked) return;
const dimension = AntimatterDimension(tier);
this.isCapped = tier === 8 && Enslaved.isRunning && dimension.bought >= 1;
this.multiplier.copyFrom(AntimatterDimension(tier).multiplier);
this.amount.copyFrom(dimension.totalAmount);
this.boughtBefore10 = dimension.boughtBefore10;
this.singleCost.copyFrom(dimension.cost);
this.until10Cost.copyFrom(dimension.costUntil10);
if (tier < 8) {
this.rateOfChange.copyFrom(dimension.rateOfChange);
}
this.isAffordable = dimension.isAffordable;
this.isAffordableUntil10 = dimension.isAffordableUntil10;
this.isContinuumActive = Laitela.continuumActive;
if (this.isContinuumActive) this.continuumValue = dimension.continuumValue;
},
buySingle() {
if (this.isContinuumActive) return;
buyOneDimension(this.tier);
if (this.tier === 2) {
Tutorial.turnOffEffect(TUTORIAL_STATE.DIM2);
}
},
buyUntil10() {
if (this.isContinuumActive) return;
buyManyDimension(this.tier);
if (this.tier === 2) {
Tutorial.turnOffEffect(TUTORIAL_STATE.DIM2);
}
},
showCostTitle(value) {
return value.exponent < 1000000;
},
tutorialClass() {
if (this.tier === 1) {
return Tutorial.glowingClass(TUTORIAL_STATE.DIM1, this.isAffordable);
}
if (this.tier === 2) {
return Tutorial.glowingClass(TUTORIAL_STATE.DIM2, this.isAffordable);
}
return {};
}
},
template:
`<div v-show="isUnlocked" class="c-antimatter-dim-row">
<div class="c-dim-row__label c-dim-row__name">
{{name}} Antimatter Dimension {{formatX(multiplier, 1, 1)}}
</div>
<div class="c-dim-row__label c-dim-row__label--growable">
{{amountDisplay}} ({{formatInt(boughtBefore10)}})
<span class="c-dim-row__label--small" v-if="rateOfChange.neq(0)">{{rateOfChangeDisplay}}</span>
</div>
<primary-button
v-if="!isContinuumActive"
:enabled="isAffordable && !isCapped"
class="o-primary-btn--buy-ad o-primary-btn--buy-single-ad l-dim-row__button"
:class="tutorialClass()"
:ach-tooltip="cappedTooltip"
@click="buySingle">
<span v-if="isCapped">Capped</span>
<template v-else>
<span v-if="showCostTitle(singleCost)">Cost: </span>{{format(singleCost)}}
</template>
</primary-button>
<primary-button
:enabled="(isAffordableUntil10 || isContinuumActive) && !isCapped"
class="o-primary-btn--buy-ad o-primary-btn--buy-10-ad l-dim-row__button"
:ach-tooltip="cappedTooltip"
@click="buyUntil10">
<span v-if="isCapped">Capped</span>
<span v-else-if="isContinuumActive">Continuum: {{continuumString}}</span>
<template v-else>
Until {{formatInt(10)}}, <span v-if="showCostTitle(until10Cost)">
Cost: </span>{{format(until10Cost)}}
</template>
</primary-button>
<div
v-for="text in floatingText"
:key="text.key"
class='c-antimatter-dim-row__floating-text'
>{{text.text}}</div>
</div>`,
});

View File

@ -1,6 +1,6 @@
"use strict";
Vue.component("normal-dim-shift-row", {
Vue.component("antimatter-dim-shift-row", {
data() {
return {
requirement: {
@ -10,7 +10,8 @@ Vue.component("normal-dim-shift-row", {
isShift: false,
isBuyable: false,
purchasedBoosts: 0,
freeBoosts: 0
freeBoosts: 0,
lockText: null
};
},
computed: {
@ -18,21 +19,26 @@ Vue.component("normal-dim-shift-row", {
return this.isShift ? "Shift" : "Boost";
},
dimName() {
return DISPLAY_NAMES[this.requirement.tier];
return AntimatterDimension(this.requirement.tier).displayName;
},
buttonText() {
return `Reset the game for a ${this.isShift ? "new Dimension" : "boost"}`;
return this.lockText === null
? `Reset your Dimensions for a ${this.isShift ? "new Dimension" : "boost"}`
: this.lockText;
},
boostCountText() {
const parts = [this.purchasedBoosts];
if (this.freeBoosts !== 0) {
parts.push(this.freeBoosts);
}
const sum = parts.map(shortenSmallInteger).join(" + ");
const sum = parts.map(formatInt).join(" + ");
if (parts.length >= 2) {
return `${sum} = ${shortenSmallInteger(parts.sum())}`;
return `${sum} = ${formatInt(parts.sum())}`;
}
return sum;
},
tutorialClass() {
return Tutorial.glowingClass(TUTORIAL_STATE.DIMSHIFT, this.isBuyable);
}
},
methods: {
@ -40,27 +46,30 @@ Vue.component("normal-dim-shift-row", {
const requirement = DimBoost.requirement;
this.requirement.tier = requirement.tier;
this.requirement.amount = requirement.amount;
this.isBuyable = requirement.isSatisfied;
this.isBuyable = requirement.isSatisfied && DimBoost.canBeBought;
this.isShift = DimBoost.isShift;
this.purchasedBoosts = DimBoost.purchasedBoosts;
this.freeBoosts = DimBoost.freeBoosts;
this.lockText = DimBoost.lockText;
},
softReset() {
softResetBtnClick();
Tutorial.turnOffEffect(TUTORIAL_STATE.DIMSHIFT);
}
},
template:
`<div class="c-normal-dim-row">
<div
class="c-normal-dim-row__label c-normal-dim-row__label--growable"
`<div class="c-antimatter-dim-row">
<div
class="c-dim-row__label c-dim-row__label--growable"
>
Dimension {{name}} ({{boostCountText}}):
requires {{shortenSmallInteger(requirement.amount)}} {{dimName}} Dimensions
requires {{formatInt(requirement.amount)}} {{dimName}} Dimensions
</div>
<primary-button
:enabled="isBuyable"
class="o-primary-btn--dimboost c-normal-dim-row__buy-button c-normal-dim-row__buy-button--right-offset"
class="o-primary-btn--dimboost l-dim-row__button l-dim-row__button--right-offset"
:class=tutorialClass
@click="softReset"
>{{buttonText}}</primary-button>
</div>`
});
});

View File

@ -1,19 +1,23 @@
"use strict";
Vue.component("normal-dim-tab-header", {
Vue.component("antimatter-dim-tab-header", {
data() {
return {
isSacrificeUnlocked: false,
isSacrificeAffordable: false,
currentSacrifice: new Decimal(0),
sacrificeBoost: new Decimal(0),
disabledCondition: ""
disabledCondition: "",
isLarge: false
};
},
computed: {
sacrificeTooltip() {
return `Boosts 8th Dimension by ${formatX(this.sacrificeBoost, 2, 2)}`;
return `Boosts 8th Antimatter Dimension by ${formatX(this.sacrificeBoost, 2, 2)}`;
},
classObject() {
return this.isLarge ? "o-primary-btn--sacrifice--large" : "";
}
},
methods: {
update() {
@ -24,6 +28,7 @@ Vue.component("normal-dim-tab-header", {
this.currentSacrifice.copyFrom(Sacrifice.totalBoost);
this.sacrificeBoost.copyFrom(Sacrifice.nextBoost);
this.disabledCondition = Sacrifice.disabledCondition;
this.isLarge = this.disabledCondition.length > 20;
},
sacrifice() {
sacrificeBtnClick();
@ -33,12 +38,13 @@ Vue.component("normal-dim-tab-header", {
}
},
template:
`<div class="l-normal-dim-tab__header">
`<div class="l-antimatter-dim-tab__header">
<primary-button
v-show="isSacrificeUnlocked"
v-tooltip="sacrificeTooltip"
:enabled="isSacrificeAffordable"
class="o-primary-btn--sacrifice"
:class="classObject"
@click="sacrifice"
>
<span v-if="isSacrificeAffordable">Dimensional Sacrifice ({{ formatX(sacrificeBoost, 2, 2) }})</span>

View File

@ -0,0 +1,55 @@
"use strict";
Vue.component("antimatter-dim-tab-progress-bar", {
data() {
return {
fill: 0,
tooltip: ""
};
},
computed: {
displayPercents() {
return formatPercents(this.fill, 2);
},
progressBarStyle() {
return {
width: Notations.current.name === 'Blind' ? '0%' : `${(this.fill * 100).toFixed(2)}%`
};
}
},
methods: {
update() {
const setProgress = (current, goal, tooltip) => {
this.fill = Math.clampMax(current.pLog10() / Decimal.log10(goal), 1);
this.tooltip = tooltip;
};
const challenge = NormalChallenge.current || InfinityChallenge.current;
if (challenge) {
setProgress(Currency.antimatter.value, challenge.goal, "Percentage to challenge goal");
} else if (!player.break) {
setProgress(Currency.antimatter.value, Decimal.NUMBER_MAX_VALUE, "Percentage to Infinity");
} else if (Enslaved.isCompleted) {
setProgress(player.infinityPoints, Enslaved.tesseractCost, "Percentage to next Tesseract");
} else if (PlayerProgress.dilationUnlocked()) {
setProgress(player.eternityPoints, new Decimal("1e4000"), "Percentage to Reality");
} else if (InfinityDimension(8).isUnlocked) {
setProgress(
player.infinityPoints,
Player.eternityGoal,
EternityChallenge.isRunning ? "Percentage to Eternity Challenge goal" : "Percentage to Eternity"
);
} else {
setProgress(
Currency.antimatter.value,
InfinityDimensions.next().requirement,
"Percentage to next dimension unlock");
}
}
},
template:
`<div class="c-progress-bar">
<div :style="progressBarStyle" class="c-progress-bar__fill">
<span v-tooltip="tooltip" class="c-progress-bar__percents">{{displayPercents}}</span>
</div>
</div>`
});

View File

@ -1,6 +1,6 @@
"use strict";
Vue.component("normal-dim-tab", {
Vue.component("antimatter-dim-tab", {
data() {
return {
isChallengePowerVisible: false,
@ -21,11 +21,11 @@ Vue.component("normal-dim-tab", {
if (isChallengePowerVisible) {
const powerArray = [];
if (isC2Running) powerArray.push(`Production: ${formatPercents(player.chall2Pow, 2, 2)}`);
if (isC3Running) powerArray.push(`First dimension: ${formatX(player.chall3Pow, 2, 2)}`);
if (isIC6Running) powerArray.push(`Matter: /
${shorten(new Decimal(1).timesEffectOf(InfinityChallenge(6)), 2, 2)}`);
if (isIC8Running) powerArray.push(`Production: /
${shorten(new Decimal(1).timesEffectOf(InfinityChallenge(8)).reciprocal(), 2, 2)}`);
if (isC3Running) powerArray.push(`First dimension: ${formatX(player.chall3Pow, 3, 4)}`);
if (isIC6Running) powerArray.push(`Matter: /
${format(new Decimal(1).timesEffectOf(InfinityChallenge(6)), 2, 2)}`);
if (isIC8Running) powerArray.push(`Production: /
${format(new Decimal(1).timesEffectOf(InfinityChallenge(8)).reciprocal(), 2, 2)}`);
this.challengePower = powerArray.join(", ");
}
const challenge = NormalChallenge.current || InfinityChallenge.current;
@ -38,19 +38,19 @@ Vue.component("normal-dim-tab", {
}
},
template:
`<div class="l-old-ui-normal-dim-tab">
`<div class="l-old-ui-antimatter-dim-tab">
<span v-if="isSacrificeUnlocked">Sacrifice multiplier: {{ formatX(currentSacrifice, 2, 2) }}</span>
<normal-dim-tab-header />
<antimatter-dim-tab-header />
<span v-if="isChallengePowerVisible">{{challengePower}}</span>
<div class="l-normal-dim-tab__row-container l-normal-dim-row-container">
<normal-dim-row
<div class="l-dimensions-container">
<antimatter-dim-row
v-for="tier in 8"
:key="tier"
:tier="tier"
:floatingText="$viewModel.tabs.dimensions.normal.floatingText[tier]"
:floatingText="$viewModel.tabs.dimensions.antimatter.floatingText[tier]"
/>
<normal-dim-shift-row />
<normal-dim-galaxy-row />
<antimatter-dim-shift-row />
<antimatter-dim-galaxy-row />
</div>
<primary-button
v-if="isQuickResetAvailable"
@ -58,6 +58,6 @@ Vue.component("normal-dim-tab", {
@click="quickReset"
>Lose a reset, returning to the start of the reset</primary-button>
<div style="flex: 1 0" />
<normal-dim-tab-progress-bar class="l-normal-dim-tab__progress_bar" />
<antimatter-dim-tab-progress-bar class="l-antimatter-dim-tab__progress_bar" />
</div>`
});

View File

@ -1,131 +0,0 @@
"use strict";
Vue.component("dim-production-tab", {
components: {
"number-input": {
props: {
value: Number,
min: Number,
max: Number,
default: Number
},
data() {
return {
inputValue: this.clamp(this.value)
};
},
methods: {
clamp(value) {
return Math.clamp(value, this.min, this.max);
},
handleInput(event) {
const input = parseInt(event.target.value, 10);
const finalValue = isNaN(input) ? this.min : this.clamp(input);
this.inputValue = finalValue;
this.emitInput(finalValue);
}
},
template:
`<input type="number" :value="inputValue" @input="handleInput" />`
}
},
data() {
return {
duration: 0,
updateRate: 0,
enabled: false,
dips: false
};
},
computed: {
chartOptions() {
return this.options.chart;
}
},
watch: {
duration(newValue) {
player.options.chart.duration = newValue;
},
updateRate(newValue) {
player.options.chart.updateRate = newValue;
},
enabled(newValue) {
player.options.chart.on = newValue;
},
dips(newValue) {
player.options.chart.dips = newValue;
}
},
mounted() {
const container = this.$refs.chartContainer;
container.appendChild(dimChartEl);
},
methods: {
update() {
const options = player.options.chart;
this.duration = options.duration;
this.updateRate = options.updateRate;
this.enabled = options.on;
this.dips = options.dips;
},
checkOptionsWarnings() {
const options = player.options.chart;
const updateRate = options.updateRate;
const duration = options.duration;
if (updateRate <= 200 && duration >= 30 && options.warning === 0) {
Modal.message.show("Warning: setting the duration and update rate too high can cause performance issues.");
options.warning = 1;
}
if (duration / updateRate * 1000 >= 1000 && options.warning !== 2) {
Modal.message.show("Warning: you have set the duration and update rate quite high, " +
"make sure you know what you're doing or have a good computer");
options.warning = 2;
}
},
checkToggleWarnings() {
const options = player.options.chart;
if (options.on) return;
if (options.warning < 1) {
Modal.message.show("Warning: the chart can cause performance issues. " +
"Please disable it if you're experiencing lag.");
}
}
},
template:
`<div>
<div class="c-production-header">
<b>seconds of history:</b>
<number-input
v-model="duration"
:min="1"
:max="300"
:default="10"
@input="checkOptionsWarnings"
/>
<b>update rate (in ms):</b>
<number-input
v-model="updateRate"
:min="50"
:max="10000"
:default="1000"
@input="checkOptionsWarnings"
/>
<b>enabled:</b>
<input
v-model="enabled"
class="c-production-header__checkbox"
type="checkbox"
@input="checkToggleWarnings"
/>
<b>dips:</b>
<input
v-model="dips"
class="c-production-header__checkbox"
type="checkbox"
@input="checkToggleWarnings"
/>
</div>
<div ref="chartContainer" />
<b>Exponents of antimatter per second</b>
</div>`
});

View File

@ -8,17 +8,21 @@ Vue.component("infinity-dim-row", {
return {
isUnlocked: false,
multiplier: new Decimal(0),
baseAmount: 0,
amount: new Decimal(0),
hasRateOfChange: false,
purchases: 0,
rateOfChange: new Decimal(0),
isAutobuyerUnlocked: false,
cost: new Decimal(0),
isAvailableForPuchase: false,
isAvailableForPurchase: false,
isCapped: false,
capIP: new Decimal(0),
isAutobuyerOn: false,
isEC8Running: false,
hardcap: HARDCAP_ID_PURCHASES,
hardcap: InfinityDimensions.HARDCAP_PURCHASES,
requirementReached: false,
eternityReached: false,
showCostTitle: false
};
},
watch: {
@ -28,23 +32,34 @@ Vue.component("infinity-dim-row", {
},
computed: {
name() {
return DISPLAY_NAMES[this.tier];
return InfinityDimension(this.tier).shortDisplayName;
},
rateOfChangeDisplay() {
return this.hasRateOfChange
? ` (+${this.shortenRateOfChange(this.rateOfChange)}%/s)`
: "";
return ` (+${format(this.rateOfChange, 2, 2)}%/s)`;
},
costDisplay() {
return this.isCapped ? "Capped!" : `Cost: ${this.shortenCosts(this.cost)} IP`;
const requirement = InfinityDimension(this.tier).requirement;
if (this.isUnlocked) {
if (this.isCapped) return "Capped";
return this.showCostTitle ? `Cost: ${format(this.cost)} IP` : `${format(this.cost)} IP`;
}
if (this.requirementReached) {
return "Unlock";
}
return `Reach ${format(requirement)} AM`;
},
hardcapPurchases() {
return this.shorten(this.hardcap, 1, 1);
return format(this.hardcap, 1, 1);
},
capTooltip() {
return this.isCapped
? `Limited to ${this.hardcapPurchases} upgrades (${this.shortenCosts(this.capIP)} IP)`
: undefined;
? `Cap reached at ${format(this.capIP)} IP`
: `Purchased ${formatInt(this.purchases)} ${pluralize("time", this.purchases)}`;
},
showRow() {
return this.eternityReached || this.isUnlocked || this.requirementReached || this.amount.gt(0);
}
},
methods: {
@ -52,16 +67,17 @@ Vue.component("infinity-dim-row", {
const tier = this.tier;
const dimension = InfinityDimension(tier);
this.isUnlocked = dimension.isUnlocked;
if (!this.isUnlocked) return;
this.multiplier.copyFrom(dimension.multiplier);
this.baseAmount = dimension.baseAmount;
this.purchases = dimension.purchases;
this.amount.copyFrom(dimension.amount);
this.hasRateOfChange = dimension.hasRateOfChange;
if (this.hasRateOfChange) {
this.rateOfChange.copyFrom(dimension.rateOfChange);
}
this.rateOfChange.copyFrom(dimension.rateOfChange);
this.isAutobuyerUnlocked = dimension.isAutobuyerUnlocked;
this.cost.copyFrom(dimension.cost);
this.isAvailableForPuchase = dimension.isAvailableForPuchase;
this.isAvailableForPurchase = dimension.isAvailableForPurchase;
if (!this.isUnlocked) {
this.isAvailableForPurchase = dimension.requirementReached;
}
this.isCapped = dimension.isCapped;
if (this.isCapped) {
this.capIP.copyFrom(dimension.hardcapIPAmount);
@ -69,30 +85,49 @@ Vue.component("infinity-dim-row", {
}
this.isEC8Running = EternityChallenge(8).isRunning;
this.isAutobuyerOn = player.infDimBuyers[this.tier - 1];
this.requirementReached = dimension.requirementReached;
this.eternityReached = PlayerProgress.eternityUnlocked();
this.showCostTitle = this.cost.exponent < 1000000;
},
buyManyInfinityDimension() {
if (!this.isUnlocked) {
InfinityDimension(this.tier).tryUnlock();
return;
}
buyManyInfinityDimension(this.tier);
}
},
buyMaxInfinityDimension() {
if (!this.isUnlocked) return;
buyMaxInfDims(this.tier);
},
},
template:
`<div v-show="isUnlocked" class="c-infinity-dim-row">
<div class="c-infinity-dim-row__label c-infinity-dim-row__name">
{{name}} Infinity Dimension x{{shortenMoney(multiplier)}}
`<div v-show="showRow" class="c-infinity-dim-row"
:class="{ 'c-infinity-dim-row--not-reached': !isUnlocked && !requirementReached }">
<div class="c-dim-row__label c-dim-row__name">
{{name}} Infinity Dimension {{formatX(multiplier, 2, 1)}}
</div>
<div class="c-infinity-dim-row__label c-infinity-dim-row__label--growable">
{{shortenDimensions(amount)}} {{rateOfChangeDisplay}}
<div class="c-dim-row__label c-dim-row__label--growable">
{{format(amount, 2, 0)}}
<span class="c-dim-row__label--small" v-if="rateOfChange.neq(0)">{{rateOfChangeDisplay}}</span>
</div>
<primary-button
v-tooltip="capTooltip"
:enabled="isAvailableForPurchase && !isCapped"
class="o-primary-btn--buy-id l-dim-row__button"
@click="buyManyInfinityDimension"
>{{costDisplay}}</primary-button>
<primary-button-on-off
v-if="isAutobuyerUnlocked && !isEC8Running"
v-model="isAutobuyerOn"
class="o-primary-btn--id-autobuyer l-infinity-dim-row__button"
class="o-primary-btn--id-autobuyer l-dim-row__button"
text="Auto:"
/>
<primary-button
v-tooltip="capTooltip"
:enabled="isAvailableForPuchase"
class="o-primary-btn--buy-id l-infinity-dim-row__button"
@click="buyManyInfinityDimension"
>{{costDisplay}}</primary-button>
v-else
:enabled="isAvailableForPurchase && isUnlocked"
class="o-primary-btn--buy-id-max l-dim-row__button"
@click="buyMaxInfinityDimension"
>Buy Max</primary-button>
</div>`,
});

View File

@ -8,12 +8,13 @@ Vue.component("infinity-dim-tab", {
powerPerSecond: new Decimal(0),
incomeType: "",
isEC8Running: false,
isEnslavedRunning: false,
EC8PurchasesLeft: 0,
isAnyAutobuyerUnlocked: false,
conversionRate: 0,
nextDimCapIncrease: 0,
tesseractCost: new Decimal(0),
totalDimCapIncrease: 0,
totalDimCap: 0,
canBuyTesseract: false,
enslavedCompleted: false
};
@ -28,16 +29,17 @@ Vue.component("infinity-dim-tab", {
} else {
this.dimMultiplier.copyFrom(infinityPower.pow(this.conversionRate).max(1));
}
this.powerPerSecond.copyFrom(InfinityDimension(1).productionPerSecond);
this.powerPerSecond.copyFrom(InfinityDimension(1).productionPerRealSecond);
this.incomeType = EternityChallenge(7).isRunning ? "Seventh Dimensions" : "Infinity Power";
this.isEC8Running = EternityChallenge(8).isRunning;
if (this.isEC8Running) {
this.EC8PurchasesLeft = player.eterc8ids;
}
this.isEnslavedRunning = Enslaved.isRunning;
this.isAnyAutobuyerUnlocked = InfinityDimension(1).isAutobuyerUnlocked;
this.nextDimCapIncrease = Enslaved.nextDimCapIncrease;
this.tesseractCost.copyFrom(Enslaved.tesseractCost);
this.totalDimCapIncrease = player.celestials.enslaved.totalDimCapIncrease;
this.totalDimCap = InfinityDimensions.totalDimCap;
this.canBuyTesseract = Enslaved.canBuyTesseract;
this.enslavedCompleted = Enslaved.isCompleted;
},
@ -51,49 +53,57 @@ Vue.component("infinity-dim-tab", {
Enslaved.buyTesseract();
}
},
template:
`<div class="l-infinity-dim-tab">
<div>
<p>
You have
<span class="c-infinity-dim-description__accent">{{shortenMoney(infinityPower)}}</span> infinity power,
translated to
<span class="c-infinity-dim-description__accent">{{shortenMoney(dimMultiplier)}}</span>x
multiplier on all dimensions (^{{ shorten(conversionRate, 2, 3) }}).
</p>
</div>
<div class="l-infinity-dim-tab__enslaved-reward-container" v-if="enslavedCompleted">
<button
class="c-infinity-dim-tab__tesseract-button"
:class="{ 'c-infinity-dim-tab__tesseract-button--disabled': !canBuyTesseract }"
@click="buyTesseract">
<p>Buy a Tesseract</p>
<p>Increase dimension caps by {{ shorten(nextDimCapIncrease, 2) }}</p>
<p><b>Costs: {{ shorten(tesseractCost, 0, 0) }} IP</b></p>
</button>
<div>Total dimension cap increase: {{ shorten(totalDimCapIncrease, 2) }}</div>
</div>
<div>You are getting {{shortenDimensions(powerPerSecond)}} {{incomeType}} per second.</div>
template: `
<div class="l-infinity-dim-tab">
<div class="c-subtab-option-container">
<primary-button
v-if="!isEC8Running"
class="o-primary-btn--buy-max l-infinity-dim-tab__buy-max"
class="o-primary-btn--subtab-option"
@click="maxAll"
>Max all</primary-button>
<div class="l-infinity-dim-tab__row-container">
<infinity-dim-row
v-for="tier in 8"
:key="tier"
:tier="tier"
/>
</div>
<div
v-if="isEC8Running"
class="l-infinity-dim-tab__ec8-purchases"
>You have {{EC8PurchasesLeft}} {{"purchase" | pluralize(EC8PurchasesLeft)}} left.</div>
<primary-button
v-if="isAnyAutobuyerUnlocked && !isEC8Running"
class="o-primary-btn--id-all-autobuyers l-infinity-dim-tab__all-autobuyers"
class="o-primary-btn--subtab-option"
@click="toggleAllAutobuyers"
>Toggle all ON/OFF</primary-button>
</div>`
});
>Toggle all autobuyers</primary-button>
</div>
<div>
<p>
You have
<span class="c-infinity-dim-description__accent">{{format(infinityPower, 2, 1)}}</span> infinity power,
translated to a
<span class="c-infinity-dim-description__accent">{{formatX(dimMultiplier, 2, 1)}}</span>
multiplier on all Antimatter Dimensions (^{{ format(conversionRate, 2, 3) }}).
</p>
</div>
<div class="l-infinity-dim-tab__enslaved-reward-container" v-if="enslavedCompleted">
<button
class="c-infinity-dim-tab__tesseract-button"
:class="{ 'c-infinity-dim-tab__tesseract-button--disabled': !canBuyTesseract }"
@click="buyTesseract">
<p>Buy a Tesseract</p>
<p>Increase dimension caps by {{ format(nextDimCapIncrease, 2) }}</p>
<p><b>Costs: {{ format(tesseractCost) }} IP</b></p>
</button>
</div>
<div v-if="isEnslavedRunning">
All Infinity Dimensions are limited to a single purchase.
</div>
<div v-else>
All Infinity Dimensions except for the 8th are limited to a maximum of {{format(totalDimCap, 2)}}
purchases each.
</div>
<div>You are getting {{format(powerPerSecond, 2, 0)}} {{incomeType}} per second.</div>
<div class="l-dimensions-container">
<infinity-dim-row
v-for="tier in 8"
:key="tier"
:tier="tier"
/>
</div>
<div
v-if="isEC8Running"
class="l-infinity-dim-tab__ec8-purchases"
>You have {{formatInt(EC8PurchasesLeft)}} {{"purchase" | pluralize(EC8PurchasesLeft)}} left.</div>
</div>`
});

View File

@ -1,100 +0,0 @@
"use strict";
Vue.component("normal-dim-galaxy-row", {
data() {
return {
type: GalaxyType.NORMAL,
galaxies: {
normal: 0,
replicanti: 0,
dilation: 0
},
requirement: {
tier: 1,
amount: 0
},
canBeBought: false,
distantStart: 0,
lockText: null
};
},
computed: {
dimName() {
return DISPLAY_NAMES[this.requirement.tier];
},
buttonText() {
return this.lockText === null
? "Lose all your previous progress, but get a tickspeed boost"
: this.lockText;
},
sumText() {
const parts = [this.galaxies.normal];
if (this.galaxies.replicanti > 0) parts.push(this.galaxies.replicanti);
if (this.galaxies.dilation > 0) parts.push(this.galaxies.dilation);
const sum = parts.map(shortenSmallInteger).join(" + ");
if (parts.length >= 2) {
return `${sum} = ${shortenSmallInteger(parts.sum())}`;
}
return sum;
},
typeName() {
switch (this.type) {
case GalaxyType.NORMAL: return "Antimatter Galaxies";
case GalaxyType.DISTANT: return "Distant Antimatter Galaxies";
case GalaxyType.REMOTE: return "Remote Antimatter Galaxies";
}
return undefined;
},
hasIncreasedScaling() {
return this.type !== GalaxyType.NORMAL;
},
costScalingText() {
switch (this.type) {
case GalaxyType.DISTANT:
return `Each galaxy is more expensive past ${shortenSmallInteger(this.distantStart)} galaxies`;
case GalaxyType.REMOTE:
return "Increased galaxy cost scaling: " +
`Quadratic past ${shortenSmallInteger(this.distantStart)} (distant),
exponential past ${shortenSmallInteger(800)} (remote)`;
}
return undefined;
}
},
methods: {
update() {
this.type = Galaxy.type;
this.galaxies.normal = player.galaxies;
this.galaxies.replicanti = Replicanti.galaxies.total;
this.galaxies.dilation = player.dilation.freeGalaxies;
const requirement = Galaxy.requirement;
this.requirement.amount = requirement.amount;
this.requirement.tier = requirement.tier;
this.canBeBought = requirement.isSatisfied && Galaxy.canBeBought;
this.distantStart = EternityChallenge(5).isRunning ? 0 : Galaxy.costScalingStart;
this.lockText = this.generateLockText();
},
generateLockText() {
if (Galaxy.canBeBought) return null;
if (EternityChallenge(6).isRunning) return "Locked (Eternity Challenge 6)";
if (InfinityChallenge(7).isRunning) return "Locked (Infinity Challenge 7)";
if (NormalChallenge(8).isRunning) return "Locked (8th Dimension Autobuyer Challenge)";
return null;
},
buyGalaxy: bulk => galaxyResetBtnClick(bulk),
},
template:
`<div class="c-normal-dim-row">
<div
class="c-normal-dim-row__label c-normal-dim-row__label--growable"
>{{typeName}} ({{sumText}}):
requires {{shortenSmallInteger(requirement.amount)}} {{dimName}} Dimensions
<div v-if="hasIncreasedScaling">{{costScalingText}}</div>
</div>
<primary-button
:enabled="canBeBought"
class="o-primary-btn--galaxy c-normal-dim-row__buy-button c-normal-dim-row__buy-button--right-offset"
@click.exact="buyGalaxy(true)"
@click.shift.exact="buyGalaxy(false)"
>{{buttonText}}</primary-button>
</div>`
});

View File

@ -1,104 +0,0 @@
"use strict";
Vue.component("normal-dim-row", {
props: {
floatingText: Array,
tier: Number
},
data() {
return {
isUnlocked: false,
isCapped: false,
multiplier: new Decimal(0),
amount: new Decimal(0),
boughtBefore10: 0,
rateOfChange: new Decimal(0),
singleCost: new Decimal(0),
until10Cost: new Decimal(0),
isAffordable: false,
isAffordableUntil10: false,
};
},
computed: {
name() {
return DISPLAY_NAMES[this.tier];
},
amountDisplay() {
return this.tier < 8 ? shorten(this.amount, 2, 0) : shortenSmallInteger(this.amount);
},
rateOfChangeDisplay() {
return this.tier < 8
? ` (+${shorten(this.rateOfChange, 2, 2)}%/s)`
: "";
},
cappedTooltip() {
return this.isCapped
? "Further eighth dimension purchases are prohibited, as they are the only way to acquire galaxies"
: null;
}
},
methods: {
update() {
const tier = this.tier;
const isUnlocked = NormalDimension(tier).isAvailable;
this.isUnlocked = isUnlocked;
if (!isUnlocked) return;
const dimension = NormalDimension(tier);
this.isCapped = tier === 8 && Enslaved.isRunning && dimension.bought >= 1;
this.multiplier.copyFrom(getDimensionFinalMultiplier(tier));
this.amount.copyFrom(dimension.amount);
this.boughtBefore10 = dimension.boughtBefore10;
this.singleCost.copyFrom(dimension.cost);
this.until10Cost.copyFrom(dimension.costUntil10);
if (tier < 8) {
this.rateOfChange.copyFrom(dimension.rateOfChange);
}
this.isAffordable = dimension.isAffordable;
this.isAffordableUntil10 = dimension.isAffordableUntil10;
},
buySingle() {
buyOneDimensionBtnClick(this.tier);
},
buyUntil10() {
buyManyDimensionsBtnClick(this.tier);
},
showCostTitle(value) {
return value.exponent < 1000000;
}
},
template:
`<div v-show="isUnlocked" class="c-normal-dim-row">
<div class="c-normal-dim-row__name c-normal-dim-row__label">
{{name}} Dimension x{{shorten(multiplier, 1, 1)}}
</div>
<div class="c-normal-dim-row__label c-normal-dim-row__label--growable">
{{amountDisplay}} ({{shortenSmallInteger(boughtBefore10)}}){{rateOfChangeDisplay}}
</div>
<primary-button
:enabled="isAffordable"
class="o-primary-btn--buy-nd o-primary-btn--buy-single-nd c-normal-dim-row__buy-button"
:ach-tooltip="cappedTooltip"
@click="buySingle">
<span v-if="isCapped">Capped!</span>
<template v-else>
<span v-if="showCostTitle(singleCost)">Cost: </span>{{shorten(singleCost)}}
</template>
</primary-button>
<primary-button
:enabled="isAffordableUntil10"
class="o-primary-btn--buy-nd o-primary-btn--buy-10-nd c-normal-dim-row__buy-button"
:ach-tooltip="cappedTooltip"
@click="buyUntil10">
<span v-if="isCapped">Capped!</span>
<template v-else>
Until {{shortenSmallInteger(10)}}, <span v-if="showCostTitle(until10Cost)">
Cost: </span>{{shorten(until10Cost)}}
</template>
</primary-button>
<div
v-for="text in floatingText"
:key="text.key"
class='c-normal-dim-row__floating-text'
>{{text.text}}</div>
</div>`,
});

View File

@ -1,48 +0,0 @@
"use strict";
Vue.component("normal-dim-tab-progress-bar", {
data() {
return {
fill: new Decimal(0),
tooltip: ""
};
},
computed: {
percents() {
return `${this.fill.toFixed(2)}%`;
},
progressBarStyle() {
return {
width: this.percents
};
}
},
methods: {
update() {
const setProgress = (current, goal, tooltip) => {
this.fill.copyFrom(Decimal.min(current.pLog10() / Decimal.log10(goal) * 100, 100));
this.tooltip = tooltip;
};
const challenge = NormalChallenge.current || InfinityChallenge.current;
if (challenge) {
setProgress(player.antimatter, challenge.goal, "Percentage to challenge goal");
} else if (!player.break) {
setProgress(player.antimatter, Decimal.MAX_NUMBER, "Percentage to Infinity");
} else if (InfinityDimension(8).isUnlocked) {
setProgress(
player.infinityPoints,
Player.eternityGoal,
EternityChallenge.isRunning ? "Percentage to Eternity Challenge goal" : "Percentage to Eternity"
);
} else {
setProgress(player.antimatter, InfinityDimensions.next().requirement, "Percentage to next dimension unlock");
}
}
},
template:
`<div class="c-progress-bar">
<div :style="progressBarStyle" class="c-progress-bar__fill">
<span v-tooltip="tooltip" class="c-progress-bar__percents">{{percents}}</span>
</div>
</div>`
});

View File

@ -19,15 +19,15 @@ Vue.component("time-dim-row", {
},
computed: {
name() {
return DISPLAY_NAMES[this.tier];
return TimeDimension(this.tier).shortDisplayName;
},
rateOfChangeDisplay() {
return this.tier < 8
? ` (+${this.shortenRateOfChange(this.rateOfChange)}%/s)`
? ` (+${format(this.rateOfChange, 2, 2)}%/s)`
: "";
},
buttonContents() {
return this.isCapped ? "Capped" : `Cost: ${this.shortenDimensions(this.cost)} EP`;
return this.isCapped ? "Capped" : `Cost: ${format(this.cost, 2)} EP`;
}
},
watch: {
@ -53,27 +53,37 @@ Vue.component("time-dim-row", {
this.isAutobuyerOn = player.reality.tdbuyers[this.tier - 1];
},
buyTimeDimension() {
buyTimeDimension(this.tier);
}
buySingleTimeDimension(this.tier);
},
buyMaxTimeDimension() {
buyMaxTimeDimension(this.tier);
},
},
template:
`<div v-show="isUnlocked" class="c-time-dim-row">
<div class="c-time-dim-row__label c-time-dim-row__name">
{{name}} Time Dimension x{{shortenMoney(multiplier)}}
<div class="c-dim-row__label c-dim-row__name">
{{name}} Time Dimension {{formatX(multiplier, 2, 1)}}
</div>
<div class="c-time-dim-row__label c-time-dim-row__label--growable">
{{shortenDimensions(amount)}}{{rateOfChangeDisplay}}
<div class="c-dim-row__label c-dim-row__label--growable">
{{format(amount, 2, 0)}}
<span class="c-dim-row__label--small" v-if="rateOfChange.neq(0)">{{rateOfChangeDisplay}}</span>
</div>
<primary-button
:enabled="isAffordable && !isCapped"
class="o-primary-btn--buy-td l-dim-row__button"
@click="buyTimeDimension"
>{{buttonContents}}</primary-button>
<primary-button-on-off
v-if="areAutobuyersUnlocked"
v-model="isAutobuyerOn"
class="o-primary-btn--td-autobuyer l-time-dim-row__button"
class="o-primary-btn--td-autobuyer l-dim-row__button"
text="Auto:"
/>
<primary-button
:enabled="isAffordable"
class="o-primary-btn--buy-td l-time-dim-row__button"
@click="buyTimeDimension"
>{{buttonContents}}</primary-button>
v-else
:enabled="isAffordable && !isCapped"
class="o-primary-btn--buy-td-max l-dim-row__button"
@click="buyMaxTimeDimension"
>Buy Max</primary-button>
</div>`,
});

View File

@ -4,68 +4,75 @@ Vue.component("time-dim-tab", {
data() {
return {
totalUpgrades: 0,
multPerTickspeed: 0,
tickspeedSoftcap: 0,
timeShards: new Decimal(0),
upgradeThreshold: new Decimal(0),
shardsPerSecond: new Decimal(0),
incomeType: "",
showCostScaleTooltip: false,
areAutobuyersUnlocked: false
};
},
computed: {
e6000Tooltip() {
return `TD costs start increasing faster after ${shorten(new Decimal("1e6000"))}`;
},
costScaleTooltip() {
return this.showCostScaleTooltip ? this.e6000Tooltip : undefined;
}
costIncreases: () => TimeDimension(1).costIncreaseThresholds,
},
methods: {
update() {
this.totalUpgrades = player.totalTickGained;
this.multPerTickspeed = FreeTickspeed.multToNext;
this.tickspeedSoftcap = FreeTickspeed.softcap;
this.timeShards.copyFrom(player.timeShards);
this.upgradeThreshold.copyFrom(player.tickThreshold);
this.shardsPerSecond.copyFrom(TimeDimension(1).productionPerSecond);
this.upgradeThreshold.copyFrom(FreeTickspeed.fromShards(player.timeShards).nextShards);
this.shardsPerSecond.copyFrom(TimeDimension(1).productionPerRealSecond);
this.incomeType = EternityChallenge(7).isRunning ? "Eighth Infinity Dimensions" : "time shards";
this.showCostScaleTooltip = player.eternityPoints.exponent > 6000;
this.areAutobuyersUnlocked = RealityUpgrade(13).isBought;
},
maxAll() {
buyMaxTimeDimensions();
maxAllTimeDimensions();
},
toggleAllAutobuyers() {
toggleAllTimeDims();
}
},
template:
`<div class="l-time-dim-tab l-centered-vertical-tab">
<div>
<p>You've gained {{shortenSmallInteger(totalUpgrades)}} tickspeed upgrades.</p>
<p>
You have
<span class="c-time-dim-description__accent">{{shortenMoney(timeShards)}}</span> time shards.
Next tickspeed upgrade at
<span class="c-time-dim-description__accent">{{shortenMoney(upgradeThreshold)}}</span>
</p>
</div>
<div>You are getting {{shortenDimensions(shardsPerSecond)}} {{incomeType}} per second.</div>
template: `
<div class="l-time-dim-tab l-centered-vertical-tab">
<div class="c-subtab-option-container">
<primary-button
v-tooltip="costScaleTooltip"
class="o-primary-btn--buy-max l-time-dim-tab__buy-max"
class="o-primary-btn--subtab-option"
@click="maxAll"
>Max all</primary-button>
<div class="l-time-dim-tab__row-container">
<time-dim-row
v-for="tier in 8"
:key="tier"
:tier="tier"
:areAutobuyersUnlocked="areAutobuyersUnlocked"
/>
</div>
<primary-button
v-if="areAutobuyersUnlocked"
class="o-primary-btn--td-all-autobuyers l-time-dim-tab__all-autobuyers"
class="o-primary-btn--subtab-option"
@click="toggleAllAutobuyers"
>Toggle all ON/OFF</primary-button>
</div>`
});
>Toggle all autobuyers</primary-button>
</div>
<div>
<p>You've gained {{formatInt(totalUpgrades)}} tickspeed upgrades.</p>
<p>
You have
<span class="c-time-dim-description__accent">{{format(timeShards, 2, 1)}}</span> time shards.
Next tickspeed upgrade at
<span class="c-time-dim-description__accent">{{format(upgradeThreshold, 2, 1)}}.</span>
</p>
</div>
<div>
Each additional upgrade requires {{formatX(multPerTickspeed, 2, 2)}} more time shards. This will start
increasing above {{formatInt(tickspeedSoftcap)}} upgrades.
</div>
<div>You are getting {{format(shardsPerSecond, 2, 0)}} {{incomeType}} per second.</div>
<div class="l-dimensions-container">
<time-dim-row
v-for="tier in 8"
:key="tier"
:tier="tier"
:areAutobuyersUnlocked="areAutobuyersUnlocked"
/>
</div>
<div>
Time Dimension costs jump at {{format(costIncreases[0], 2, 2)}} EP and {{format(costIncreases[1])}} EP,
<br>
and get expensive more quickly past {{format(costIncreases[2])}} EP
</div>
</div>`
});

View File

@ -14,10 +14,10 @@ Vue.component("dilation-button", {
},
methods: {
update() {
this.isUnlocked = TimeStudy.dilation.isBought;
this.isUnlocked = PlayerProgress.dilationUnlocked();
this.isRunning = player.dilation.active;
if (!this.isRunning) return;
this.canEternity = player.infinityPoints.gte(Player.eternityGoal);
this.canEternity = Player.canEternity;
this.hasGain = getTachyonGain().gt(0);
if (this.canEternity && this.hasGain) {
this.tachyonGain.copyFrom(getTachyonGain());
@ -35,19 +35,19 @@ Vue.component("dilation-button", {
<span v-if="!isUnlocked">Purchase the dilation time study to unlock.</span>
<span v-else-if="!isRunning">Dilate time.</span>
<span v-else-if="canEternity && hasGain">
Disable dilation.
Disable Dilation.
<br>
Gain {{shortenMoney(tachyonGain)}} Tachyon Particles.
Gain {{format(tachyonGain, 2, 1)}} Tachyon Particles.
</span>
<span v-else-if="hasGain">
Disable dilation.
Disable Dilation.
<br>
Reach {{shorten(eternityGoal, 1, 0)}} IP to eternity and gain Tachyon Particles.
Reach {{format(eternityGoal, 1, 0)}} IP to eternity and gain Tachyon Particles.
</span>
<span v-else>
Disable dilation.
Disable Dilation.
<br>
Reach {{shortenMoney(requiredForGain)}} antimatter to gain more Tachyon Particles.
Reach {{format(requiredForGain, 2, 1)}} antimatter to gain more Tachyon Particles.
</span>
</button>`
});

View File

@ -11,6 +11,7 @@ Vue.component("dilation-upgrade", {
data() {
return {
isBought: false,
isCapped: false,
isAffordable: false,
isAutoUnlocked: false,
isAutobuyerOn: false,
@ -26,9 +27,10 @@ Vue.component("dilation-upgrade", {
return {
"o-dilation-upgrade": true,
"o-dilation-upgrade--rebuyable": this.isRebuyable,
"o-dilation-upgrade--available": !this.isBought && !this.isCapped && this.isAffordable,
"o-dilation-upgrade--unavailable": !this.isBought && !this.isCapped && !this.isAffordable,
"o-dilation-upgrade--bought": this.isBought,
"o-dilation-upgrade--available": !this.isBought && this.isAffordable,
"o-dilation-upgrade--unavailable": !this.isBought && !this.isAffordable
"o-dilation-upgrade--capped": this.isCapped,
};
}
},
@ -36,6 +38,7 @@ Vue.component("dilation-upgrade", {
update() {
if (this.isRebuyable) {
this.isAffordable = this.upgrade.isAffordable;
this.isCapped = this.upgrade.isCapped;
} else {
this.isBought = this.upgrade.isBought;
if (!this.isBought) {
@ -56,7 +59,7 @@ Vue.component("dilation-upgrade", {
/>
<effect-display br :config="upgrade.config" />
<cost-display br
v-if="!isBought"
v-if="!isBought && !isCapped"
:config="upgrade.config"
singular="Dilated Time"
plural="Dilated Time"

View File

@ -51,21 +51,21 @@ Vue.component("time-dilation-tab", {
`<div class="l-dilation-tab">
<span>
You have
<span class="c-dilation-tab__tachyons">{{shorten(tachyons, 2, 1)}}</span>
<span class="c-dilation-tab__tachyons">{{format(tachyons, 2, 1)}}</span>
Tachyon {{"Particle" | pluralize(tachyons)}}.
</span>
<dilation-button />
<span>
You have
<span class="c-dilation-tab__dilated-time">{{shorten(dilatedTime, 2, 1)}}</span>
<span class="c-dilation-tab__dilated-time">{{format(dilatedTime, 2, 1)}}</span>
Dilated Time.
<span class="c-dilation-tab__dilated-time-income">+{{shorten(dilatedTimeIncome, 2, 1)}}/s</span>
<span class="c-dilation-tab__dilated-time-income">+{{format(dilatedTimeIncome, 2, 1)}}/s</span>
</span>
<span>
Next free galaxy at
<span class="c-dilation-tab__galaxy-threshold">{{shorten(galaxyThreshold, 2, 1)}}</span>
<span class="c-dilation-tab__galaxy-threshold">{{format(galaxyThreshold, 2, 1)}}</span>
Dilated Time, gained total of
<span class="c-dilation-tab__galaxies">{{shortenSmallInteger(galaxies)}}</span>
<span class="c-dilation-tab__galaxies">{{formatInt(galaxies)}}</span>
galaxies
</span>
<div class="l-dilation-upgrades-grid">
@ -95,4 +95,4 @@ Vue.component("time-dilation-tab", {
</div>
<tachyon-particles v-if="animateTachyons" />
</div>`
});
});

View File

@ -0,0 +1,21 @@
"use strict";
Vue.component("eternity-points-header", {
data() {
return {
eternityPoints: new Decimal(0)
};
},
methods: {
update() {
this.eternityPoints.copyFrom(player.eternityPoints.floor());
}
},
template: `
<div class="c-eternity-tab__header">
You have
<span class="c-eternity-tab__eternity-points">{{format(eternityPoints, 2, 0)}}</span>
{{ "Eternity Point" | pluralize(eternityPoints) }}.
</div>
`
});

View File

@ -40,7 +40,7 @@ Vue.component("eternity-milestone", {
template:
`<div class="l-eternity-milestone" v-if="!this.config.invisible">
<span class="o-eternity-milestone__goal">
{{eternities}} {{"Eternity" | pluralize(eternities, "Eternities")}}:
{{formatInt(eternities)}} {{"Eternity" | pluralize(eternities, "Eternities")}}:
</span>
<button :class="rewardClassObject">{{reward}}</button>
</div>`

View File

@ -26,7 +26,7 @@ Vue.component("eternity-milestones-tab", {
},
template:
`<div class="l-eternity-milestone-grid">
<div>You have eternitied {{shorten(eternityCount, 3)}} {{"time" | pluralize(eternityCount)}}.</div>
<div>You have eternitied {{format(eternityCount, 3)}} {{"time" | pluralize(eternityCount)}}.</div>
<div v-for="row in rows" class="l-eternity-milestone-grid__row">
<eternity-milestone
v-for="column in 3"
@ -36,12 +36,13 @@ Vue.component("eternity-milestones-tab", {
/>
</div>
<div>
Offline eternities only generate when your eternity autobuyer is on and set to zero EP.
Offline eternities only generate outside of Eternity Challenges and when
your Eternity autobuyer is turned on and set to zero EP.
</div>
<div>
Offline infinities only generate when your infinity autobuyer is on and set to time mode with
less than 60 seconds.
Offline infinities only generate outside of Eternity Challenges 4 and 12 and when
your infinity autobuyer is turned on and set to time mode with less than {{formatInt(60)}} seconds.
</div>
</div>`
});
});

View File

@ -17,20 +17,19 @@ Vue.component("dilation-time-study", {
},
id() {
return this.study.id;
},
classObject() {
return {
"o-time-study--dilation": this.id !== 6,
"o-time-study--reality": this.id === 6
};
}
},
created() {
if (this.id === 1) {
this.requirement = `Requirement: 5 EC11 and EC12 completions and ${shortenSmallInteger(13000)} total theorems`;
this.requirement = `Requirement: ${formatInt(5)} EC11 and EC12 completions
and ${formatInt(13000)} total Time Theorems`;
}
if (this.id === 6) {
this.requirement = `Requirement: ${shorten("1e4000")} EP and ${shortenSmallInteger(13)} rows of achievements`;
if (player.realities > 0) {
this.requirement = `Requirement: ${format("1e4000")} EP`;
} else {
this.requirement = `Requirement: ${format("1e4000")} EP and ${formatInt(13)} rows of achievements`;
}
this.showRequirement = true;
}
},
@ -44,11 +43,11 @@ Vue.component("dilation-time-study", {
}
},
template:
`<time-study :setup="setup" :showCost="showCost" :class="classObject">
`<time-study :setup="setup" :showCost="showCost">
<description-display :config="study.config" />
<template v-if="showRequirement">
<br>
<span>{{requirement}}</span>
</template>
</time-study>`
});
});

View File

@ -10,7 +10,9 @@ Vue.component("ec-time-study", {
requirement: {
current: new Decimal(0),
total: new Decimal(0)
}
},
completions: 0,
showTotalCompletions: false
};
},
computed: {
@ -28,12 +30,21 @@ Vue.component("ec-time-study", {
},
formatValue() {
return this.config.requirement.formatValue;
},
// Linebreaks added to avoid twitching in scientific notation
needsFirstLinebreak() {
return this.study.id === 7;
},
needsSecondLinebreak() {
return [3, 4, 7].includes(this.study.id);
}
},
methods: {
update() {
const id = this.id;
this.hasRequirement = !Perk.studyECRequirement.isBought && player.etercreq !== id;
this.completions = EternityChallenge(id).completions;
this.showTotalCompletions = !Enslaved.isRunning || this.id !== 1;
if (!this.hasRequirement || id > 10) return;
const requirement = this.requirement;
const study = this.study;
@ -47,15 +58,18 @@ Vue.component("ec-time-study", {
}
},
template:
`<time-study :setup="setup" class="o-time-study--eternity-challenge">
`<time-study :setup="setup">
Eternity Challenge {{id}}
({{formatInt(completions)}}<span v-if="showTotalCompletions">/{{formatInt(5)}}</span>)
<template v-if="hasRequirement">
<br>
Requirement:
<br v-if="needsFirstLinebreak">
<span v-if="id === 12">Use only the Time Dimension path</span>
<span v-else-if="id === 11">Use only the Normal Dimension path</span>
<span v-else-if="id === 11">Use only the Antimatter Dimension path</span>
<span v-else>
{{formatValue(requirement.current)}}/{{formatValue(requirement.total)}}
<br v-if="needsSecondLinebreak">
{{config.requirement.resource}}
</span>
</template>

View File

@ -6,40 +6,23 @@ Vue.component("normal-time-study", {
},
data: () => ({
showCost: true,
showSTCost: false
}),
computed: {
study() {
return this.setup.study;
},
classObject() {
const classObject = {};
classObject[this.pathClass] = true;
return classObject;
},
pathClass() {
switch (this.setup.path) {
case TimeStudyPath.NORMAL_DIM: return "o-time-study--normal-dim";
case TimeStudyPath.INFINITY_DIM: return "o-time-study--infinity-dim";
case TimeStudyPath.TIME_DIM: return "o-time-study--time-dim";
case TimeStudyPath.ACTIVE: return "o-time-study--active";
case TimeStudyPath.PASSIVE: return "o-time-study--passive";
case TimeStudyPath.IDLE: return "o-time-study--idle";
case TimeStudyPath.LIGHT: return "o-time-study--light";
case TimeStudyPath.DARK: return "o-time-study--dark";
default: return "o-time-study--normal";
}
},
hintText() {
const id = this.study.id;
switch (this.setup.path) {
case TimeStudyPath.NORMAL_DIM: return id + " Normal Dims";
case TimeStudyPath.INFINITY_DIM: return id + " Infinity Dims";
case TimeStudyPath.TIME_DIM: return id + " Time Dims";
case TimeStudyPath.ACTIVE: return id + " Active";
case TimeStudyPath.PASSIVE: return id + " Passive";
case TimeStudyPath.IDLE: return id + " Idle";
case TimeStudyPath.LIGHT: return id + " Light";
case TimeStudyPath.DARK: return id + " Dark";
case TIME_STUDY_PATH.ANTIMATTER_DIM: return `${id} Antimatter Dims`;
case TIME_STUDY_PATH.INFINITY_DIM: return `${id} Infinity Dims`;
case TIME_STUDY_PATH.TIME_DIM: return `${id} Time Dims`;
case TIME_STUDY_PATH.ACTIVE: return `${id} Active`;
case TIME_STUDY_PATH.PASSIVE: return `${id} Passive`;
case TIME_STUDY_PATH.IDLE: return `${id} Idle`;
case TIME_STUDY_PATH.LIGHT: return `${id} Light`;
case TIME_STUDY_PATH.DARK: return `${id} Dark`;
}
return id;
}
@ -47,12 +30,20 @@ Vue.component("normal-time-study", {
methods: {
update() {
this.showCost = this.study.id !== 192 || !Enslaved.isRunning;
const canBeBought = typeof this.study.config.requirement === "function"
? this.study.config.requirement()
: TimeStudy(this.study.config.requirement).isBought;
this.showSTCost = !canBeBought && V.has(V_UNLOCKS.V_ACHIEVEMENT_UNLOCK) &&
this.study.config.requirementV !== undefined &&
this.study.config.requirementV() &&
this.study.STCost !== undefined;
},
},
template:
`<time-study :setup="setup" :showCost="showCost" :class="classObject"">
<hint-text class="l-hint-text--time-study">{{hintText}}</hint-text>
`<time-study :setup="setup" :showCost="showCost" :showSTCost="showSTCost">
<hint-text type="studies" class="l-hint-text--time-study">{{hintText}}</hint-text>
<description-display :config="study.config" />
<effect-display br :config="study.config" />
</time-study>`
});
});

View File

@ -36,8 +36,11 @@ Vue.component("secret-time-study", {
"l-time-study": true,
"o-time-study": true,
"o-time-study--bought": true,
"o-time-study--secret": true,
"o-time-study--secret-unlocked": this.isVisible
"o-time-study-normal": true,
"o-time-study-normal--bought": true,
"o-time-study--secret": !this.isEnslaved && !this.isVisible,
"o-time-study--secret-enslaved": this.isEnslaved && !this.isVisible,
"o-time-study--secret-unlocked": this.isVisible,
};
}
},
@ -67,6 +70,7 @@ Vue.component("secret-time-study", {
}
if (this.isEnslaved) {
this.isVisible = true;
EnslavedProgress.secretStudy.giveProgress();
player.timestudy.theorem = player.timestudy.theorem.plus(this.enslavedTT);
}
}
@ -74,11 +78,13 @@ Vue.component("secret-time-study", {
},
template:
`<button :class="classObject" :style="styleObject" @click="handleClick" ref="study">
{{description}}
<br>
{{hide}}
<br>
Cost: {{cost}} Time Theorems
<span>
{{description}}
<br>
{{hide}}
<br>
Cost: {{cost}} Time Theorems
</span>
</button>`
});

View File

@ -49,6 +49,7 @@ class TimeStudyTreeLayout {
const TS = id => TimeStudy(id);
const EC = id => TimeStudy.eternityChallenge(id);
const TrS = id => TriadStudy(id)
/**
* @type {TimeStudyRow[]}
@ -60,7 +61,8 @@ class TimeStudyTreeLayout {
normalRow( TS(33), TS(31), TS(32), null )
];
if (type === StudyTreeLayoutType.ALTERNATIVE_62 || type === StudyTreeLayoutType.ALTERNATIVE_62_181) {
if (type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_62 || type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_62_181 ||
type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_TRIAD_STUDIES) {
this.rows.push(
normalRow( null, TS(41), TS(42), EC(5) ),
normalRow( null, TS(51), TS(62) ),
@ -87,7 +89,8 @@ class TimeStudyTreeLayout {
normalRow( TS(161), TS(162) )
);
if (type === StudyTreeLayoutType.ALTERNATIVE_181 || type === StudyTreeLayoutType.ALTERNATIVE_62_181) {
if (type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_181 || type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_62_181 ||
type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_TRIAD_STUDIES) {
this.rows.push(
normalRow( null, TS(171), EC(2) ),
normalRow( EC(1), TS(181), EC(3) )
@ -105,7 +108,22 @@ class TimeStudyTreeLayout {
normalRow( TS(191), TS(192), TS(193) ),
normalRow( TS(201) ),
normalRow( TS(211), TS(212), TS(213), TS(214) ),
wideRow (TS(221), TS(222), TS(223), TS(224), TS(225), TS(226), TS(227), TS(228)),
wideRow (TS(221), TS(222), TS(223), TS(224), TS(225), TS(226), TS(227), TS(228))
);
if (type === STUDY_TREE_LAYOUT_TYPE.ALTERNATIVE_TRIAD_STUDIES) {
const vLevel = Ra.pets.v.level;
this.rows.push(
normalRow(
vLevel >= 5 ? TrS(1) : null,
vLevel >= 10 ? TrS(2) : null,
vLevel >= 15 ? TrS(3) : null,
vLevel >= 20 ? TrS(4) : null
)
);
}
this.rows.push(
normalRow( TS(231), TS(232), TS(233), TS(234) ),
normalRow( EC(11), EC(12) ),
normalRow( TimeStudy.dilation ),
@ -176,23 +194,22 @@ class TimeStudyTreeLayout {
if (this._instances === undefined) {
this._instances = [];
}
let layout = this._instances[type];
if (layout === undefined) {
layout = new TimeStudyTreeLayout(type);
this._instances[type] = layout;
}
const layout = new TimeStudyTreeLayout(type);
this._instances[type] = layout;
return layout;
}
}
const StudyTreeLayoutType = {
const STUDY_TREE_LAYOUT_TYPE = {
NORMAL: 0,
ALTERNATIVE_62: 1,
ALTERNATIVE_181: 2,
ALTERNATIVE_62_181: 3,
ALTERNATIVE_TRIAD_STUDIES: 4,
get current() {
const alt62 = Perk.bypassEC5Lock.isBought;
const alt181 = Perk.bypassEC1Lock.isBought && Perk.bypassEC2Lock.isBought && Perk.bypassEC3Lock.isBought;
if (Ra.pets.v.level >= 5) return this.ALTERNATIVE_TRIAD_STUDIES;
if (alt62 && alt181) return this.ALTERNATIVE_62_181;
if (alt62) return this.ALTERNATIVE_62;
if (alt181) return this.ALTERNATIVE_181;
@ -205,12 +222,17 @@ Vue.component("time-studies-tab", {
data() {
return {
respec: player.respec,
layoutType: StudyTreeLayoutType.NORMAL,
layoutType: STUDY_TREE_LAYOUT_TYPE.NORMAL,
vLevel: 0
};
},
watch: {
respec(newValue) {
player.respec = newValue;
},
vLevel() {
// When vLevel changes, we recompute the study tree because of triad studies
this.$recompute("layout");
}
},
computed: {
@ -225,7 +247,7 @@ Vue.component("time-studies-tab", {
},
respecClassObject() {
return {
"o-primary-btn--time-study-options": true,
"o-primary-btn--subtab-option": true,
"o-primary-btn--respec-active": this.respec
};
}
@ -233,22 +255,24 @@ Vue.component("time-studies-tab", {
methods: {
update() {
this.respec = player.respec;
this.layoutType = StudyTreeLayoutType.current;
this.layoutType = STUDY_TREE_LAYOUT_TYPE.current;
this.vLevel = Ra.pets.v.level;
},
studyComponent(study) {
switch (study.type) {
case TimeStudyType.NORMAL: return "normal-time-study";
case TimeStudyType.ETERNITY_CHALLENGE: return "ec-time-study";
case TimeStudyType.DILATION: return "dilation-time-study";
case TimeStudyType.TRIAD: return "triad-time-study";
}
throw "Unknown study type";
}
},
template:
`<div class="l-time-studies-tab">
<div class="l-time-study-options">
<div class="c-subtab-option-container">
<primary-button
class="o-primary-btn--time-study-options"
class="o-primary-btn--subtab-option"
onclick="exportStudyTree()"
>Export tree</primary-button>
<primary-button
@ -256,7 +280,7 @@ Vue.component("time-studies-tab", {
@click="respec = !respec"
>Respec time studies on next Eternity</primary-button>
<primary-button
class="o-primary-btn--time-study-options"
class="o-primary-btn--subtab-option"
onclick="Modal.importTree.show()"
>Import tree</primary-button>
</div>
@ -277,6 +301,5 @@ Vue.component("time-studies-tab", {
<secret-time-study-connection :setup="layout.secretStudyConnection" />
</svg>
</div>
<tt-shop class="l-time-studies-tab__tt-shop" />
</div>`
});
});

View File

@ -22,12 +22,12 @@ Vue.component("time-study-connection", {
const to = connection.to;
function pathClassOf(study) {
switch (study.path) {
case TimeStudyPath.NORMAL_DIM: return "o-time-study-connection--normal-dim";
case TimeStudyPath.INFINITY_DIM: return "o-time-study-connection--infinity-dim";
case TimeStudyPath.TIME_DIM: return "o-time-study-connection--time-dim";
case TimeStudyPath.ACTIVE: return "o-time-study-connection--active";
case TimeStudyPath.PASSIVE: return "o-time-study-connection--passive";
case TimeStudyPath.IDLE: return "o-time-study-connection--idle";
case TIME_STUDY_PATH.ANTIMATTER_DIM: return "o-time-study-connection--antimatter-dim";
case TIME_STUDY_PATH.INFINITY_DIM: return "o-time-study-connection--infinity-dim";
case TIME_STUDY_PATH.TIME_DIM: return "o-time-study-connection--time-dim";
case TIME_STUDY_PATH.ACTIVE: return "o-time-study-connection--active";
case TIME_STUDY_PATH.PASSIVE: return "o-time-study-connection--passive";
case TIME_STUDY_PATH.IDLE: return "o-time-study-connection--idle";
default: return undefined;
}
}
@ -97,4 +97,4 @@ class TimeStudyConnectionSetup {
get isBought() {
return this.from.isBought && this.to.isBought;
}
}
}

View File

@ -5,7 +5,8 @@ Vue.component("time-study", {
data() {
return {
isBought: false,
isAvailable: false
isAvailableForPurchase: false,
STCost: 0
};
},
props: {
@ -13,6 +14,10 @@ Vue.component("time-study", {
showCost: {
type: Boolean,
default: true
},
showSTCost: {
type: Boolean,
default: false
}
},
computed: {
@ -28,14 +33,52 @@ Vue.component("time-study", {
classObject() {
return {
"o-time-study": true,
"o-time-study--unavailable": !this.isAvailable && !this.isBought,
"o-time-study--bought": this.isBought,
"l-time-study": true,
"o-time-study--small": this.setup.isSmall,
"l-time-study": true
"o-time-study--unavailable": !this.isAvailableForPurchase && !this.isBought,
"o-time-study--available": this.isAvailableForPurchase && !this.isBought,
"o-time-study--bought": this.isBought,
};
},
pathClass() {
switch (this.study.type) {
case TimeStudyType.NORMAL:
switch (this.setup.path) {
case TIME_STUDY_PATH.ANTIMATTER_DIM: return "o-time-study-antimatter-dim";
case TIME_STUDY_PATH.INFINITY_DIM: return "o-time-study-infinity-dim";
case TIME_STUDY_PATH.TIME_DIM: return "o-time-study-time-dim";
case TIME_STUDY_PATH.ACTIVE: return "o-time-study-active";
case TIME_STUDY_PATH.PASSIVE: return "o-time-study-passive";
case TIME_STUDY_PATH.IDLE: return "o-time-study-idle";
case TIME_STUDY_PATH.LIGHT: return "o-time-study-light";
case TIME_STUDY_PATH.DARK: return "o-time-study-dark";
default: return "o-time-study-normal";
}
break;
case TimeStudyType.ETERNITY_CHALLENGE:
return "o-time-study-eternity-challenge";
case TimeStudyType.DILATION:
if (this.study.id === 6) return "o-time-study-reality"
return "o-time-study-dilation";
case TimeStudyType.TRIAD:
return "o-time-study-triad";
}
},
studyClass() {
let pathClasses = ""
if (!this.isAvailableForPurchase && !this.isBought){
pathClasses += `${this.pathClass}--unavailable`;
}
if (this.isAvailableForPurchase && !this.isBought){
pathClasses += `${this.pathClass}--available`;
}
if (this.isBought){
pathClasses += `${this.pathClass}--bought`;
}
return pathClasses;
},
config() {
return this.study.config;
return {...this.study.config, formatCost: formatInt};
}
},
methods: {
@ -43,28 +86,36 @@ Vue.component("time-study", {
const study = this.study;
this.isBought = study.isBought;
if (!this.isBought) {
this.isAvailable = study.canBeBought && study.isAffordable;
this.isAvailableForPurchase = study.canBeBought && study.isAffordable;
}
this.STCost = this.study.STCost;
},
handleClick() {
this.study.purchase();
},
shiftClick() {
if (this.study.purchaseUntil) this.study.purchaseUntil();
},
}
},
template:
`<button :class="classObject"
`<button :class="[classObject, studyClass]"
:style="styleObject"
@click.exact="handleClick"
@click.shift.exact="shiftClick">
<slot />
<cost-display br
v-if="showCost"
v-if="(showCost && !showSTCost) || STCost === 0"
:config="config"
singular="Time Theorem"
plural="Time Theorems"
/>
<div v-else-if="showSTCost">
Cost: {{ formatInt(STCost) }} {{ "Space Theorem" | pluralize(STCost, "Space Theorems")}}
<span v-if="config.cost">
and {{formatInt(config.cost)}} TT
</span>
</div>
</button>`
});
@ -86,4 +137,4 @@ class TimeStudySetup {
get path() {
return this.study.path;
}
}
}

View File

@ -0,0 +1,26 @@
"use strict";
Vue.component("triad-time-study", {
props: {
setup: Object
},
computed: {
study() {
return this.setup.study;
},
id() {
return this.study.id;
},
config() {
return this.study.config;
},
},
methods: {
},
template:
`<time-study :setup="setup" class="o-time-study--triad" :showSTCost="true">
<hint-text type="studies" class="l-hint-text--time-study">T{{id}}</hint-text>
<description-display :config="study.config" />
<effect-display br :config="study.config" />
</time-study>`
});

View File

@ -41,11 +41,11 @@ Vue.component("ep-multiplier-button", {
template:
`<div class="l-spoon-btn-group">
<button :class="classObject" @click="upgrade.purchase()">
You gain 5 times more EP
Multiply Eternity Points from all sources by {{ formatX(5) }}
<br>
Currently: {{shorten(multiplier, 2, 0)}}x
Currently: {{formatX(multiplier, 2, 0)}}
<br>
Cost: {{shorten(cost, 2, 0)}} EP
Cost: {{format(cost, 2, 0)}} EP
</button>
<primary-button
class="l--spoon-btn-group__little-spoon o-primary-btn--small-spoon"
@ -58,4 +58,4 @@ Vue.component("ep-multiplier-button", {
class="l--spoon-btn-group__little-spoon o-primary-btn--small-spoon"
/>
</div>`
});
});

View File

@ -15,7 +15,8 @@ Vue.component("eternity-upgrades-tab", {
EternityUpgrade.tdMultRealTime,
]
];
}
},
costIncreases: () => EternityUpgrade.epMult.costIncreaseThresholds.map(x => new Decimal(x)),
},
template:
`<div class="l-eternity-upgrades-grid">
@ -28,5 +29,11 @@ Vue.component("eternity-upgrades-tab", {
/>
</div>
<ep-multiplier-button />
<div>
The cost for the EP multiplier jumps at {{format(costIncreases[0])}} EP,
{{format(costIncreases[1], 2, 2)}} EP, and {{format(costIncreases[2])}} EP.
<br>
It gets expensive more quickly past {{format(costIncreases[3])}} EP.
</div>
</div>`
});

View File

@ -0,0 +1,56 @@
"use strict";
Vue.component("failable-ec-text", {
data() {
return {
currentResource: new Decimal(0),
maximumResource: new Decimal(0),
currentEternityChallengeId: 0
};
},
computed: {
textStyle() {
if (this.maximumResource.eq(0)) return {};
const ratio = this.currentResource.div(this.maximumResource).toNumber();
// Goes from green to yellow to red. If theme is light, use a slightly lighter yellow
// by not allowing full red and gree at the same time.
const darkTheme = Theme.current().isDark && Theme.current().name !== "S6";
// Setting this constant to 2 will give green - yellow - red, setting it to 1
// will give a straight line between green and red in colorspace, intermediate values
// will give intermediate results.
const c = darkTheme ? 2 : 1.5;
const rgb = [
Math.round(Math.min(c * ratio, 1) * 255),
Math.round(Math.min(c * (1 - ratio), 1) * 255),
0
];
return { color: `rgb(${rgb.join(",")})` };
},
text() {
if (this.currentEternityChallengeId === 4) {
return `${formatInt(this.currentResource)} / ${formatInt(this.maximumResource)} Infinities used`;
}
// We're always either in EC4 or EC12 when displaying this text.
return `${TimeSpan.fromSeconds(this.currentResource.toNumber()).toString()} /
${TimeSpan.fromSeconds(this.maximumResource.toNumber()).toString()} time spent`;
}
},
methods: {
update() {
if (EternityChallenge.current && [4, 12].includes(EternityChallenge.current.id)) {
this.currentEternityChallengeId = EternityChallenge.current.id;
if (this.currentEternityChallengeId === 4) {
this.currentResource.copyFrom(player.infinitied);
} else {
this.currentResource = new Decimal(Time.thisEternity.totalSeconds);
}
this.maximumResource = new Decimal(EternityChallenge.current.config.restriction(
EternityChallenge.current.completions));
}
},
},
template:
`<span> - <span :style="textStyle">{{text}}</span></span>`
});

View File

@ -21,16 +21,19 @@ Vue.component("game-ui", {
}
},
template: `
<div v-if="view.initialized" id="ui" class="c-game-ui">
<component :is="uiLayout">
<component :is="page" />
</component>
<modal-popup v-if="view.modal.current" />
<modal-glyph-selection v-if="view.modal.glyphSelection" />
<modal-progress-bar v-if="view.modal.progressBar" />
<link v-if="view.theme !== 'Normal'" type="text/css" rel="stylesheet" :href="themeCss">
<div id="ui-container" style="display: flex; justify-content: center;">
<div v-if="view.initialized" id="ui" class="c-game-ui">
<component :is="uiLayout">
<component :is="page" />
</component>
<modal-popup v-if="view.modal.current" :modal="view.modal.current"/>
<modal-glyph-selection v-if="view.modal.glyphSelection" />
<modal-progress-bar v-if="view.modal.progressBar" />
<link v-if="view.theme !== 'Normal'" type="text/css" rel="stylesheet" :href="themeCss">
<help-me />
</div>
<div id="notification-container" class="l-notification-container" />
<help-me />
<tt-shop v-if="view.subtab === 'studies'" class="l-time-studies-tab__tt-shop" />
</div>
`
});

View File

@ -8,21 +8,36 @@ Vue.component("autobuyer-box", {
},
data() {
return {
interval: 0
interval: 0,
hasMaxedInterval: false,
bulk: 0,
bulkUnlocked: false,
bulkUnlimited: false,
};
},
computed: {
intervalDisplay() {
return TimeSpan.fromMilliseconds(this.interval).totalSeconds.toFixed(2);
return format(TimeSpan.fromMilliseconds(this.interval).totalSeconds, 2, 2);
}
},
methods: {
update() {
this.interval = this.autobuyer.interval;
this.hasMaxedInterval = this.autobuyer.hasMaxedInterval;
this.bulk = this.autobuyer.bulk;
// If it's undefined, the autobuyer isn't the dimboost autobuyer
// and we don't have to worry about bulk being unlocked.
this.bulkUnlocked = this.autobuyer.isBulkBuyUnlocked !== false;
this.bulkUnlimited = this.autobuyer.hasUnlimitedBulk;
}
},
template:
`<div class="c-autobuyer-box__small-text">Current interval: {{intervalDisplay}} seconds</div>`
`<div class="c-autobuyer-box__small-text">
Current interval: {{intervalDisplay}} seconds
<span v-if="hasMaxedInterval && bulkUnlocked && bulk">
<br>Current bulk: {{bulkUnlimited ? "Unlimited" : formatX(bulk, 2)}}
</span>
</div>`
}
},
props: {
@ -37,7 +52,11 @@ Vue.component("autobuyer-box", {
return {
isUnlocked: false,
isActive: false,
globalToggle: false
globalToggle: false,
canBeBought: false,
antimatterCost: new Decimal(0),
isBought: false,
antimatter: new Decimal(0)
};
},
watch: {
@ -51,27 +70,63 @@ Vue.component("autobuyer-box", {
this.isUnlocked = autobuyer.isUnlocked;
this.isActive = autobuyer.isActive;
this.globalToggle = player.options.autobuyersOn;
this.canBeBought = this.autobuyer.canBeBought;
this.antimatterCost = this.autobuyer.antimatterCost;
this.isBought = this.autobuyer.isBought;
this.antimatter.copyFrom(Currency.antimatter);
},
toggle() {
if (!this.globalToggle) return;
this.isActive = !this.isActive;
},
purchase() {
this.autobuyer.purchase();
}
},
computed: {
canBuy() {
return this.antimatter.gte(this.antimatterCost);
},
autobuyerBuyBoxClass() {
return {
"c-autobuyer-buy-box--purchaseable": this.canBuy
};
},
autobuyerToggleClass() {
return this.isActive ? "fas fa-check" : "fas fa-times";
},
autobuyerStateClass() {
return {
"o-autobuyer-toggle-checkbox__label": true,
"o-autobuyer-toggle-checkbox__label--active": this.isActive,
"o-autobuyer-toggle-checkbox__label--disabled": !this.globalToggle
};
},
},
template:
`<div v-if="isUnlocked" class="c-autobuyer-box l-autobuyer-box">
<div class="l-autobuyer-box__header">{{name}}</div>
<slot name="beforeInterval" />
<interval-label v-if="showInterval" :autobuyer="autobuyer"/>
<div class="l-autobuyer-box__content">
<slot />
`<div v-if="isUnlocked || isBought" class="c-autobuyer-box-row">
<div class="l-autobuyer-box__header">
{{name}}
<interval-label v-if="showInterval" :autobuyer="autobuyer"/>
</div>
<div class="o-autobuyer-toggle-checkbox l-autobuyer-box__footer" @click="toggle">
<span class="o-autobuyer-toggle-checkbox__label">Is active:</span>
<div class="c-autobuyer-box-row__intervalSlot"><slot name="intervalSlot" /></div>
<div class="c-autobuyer-box-row__toggleSlot"><slot name="toggleSlot" /></div>
<div class="c-autobuyer-box-row__prioritySlot"><slot name="prioritySlot" /></div>
<div class="c-autobuyer-box-row__optionSlot"><slot name="optionSlot" /></div>
<div class="l-autobuyer-box__footer" @click="toggle">
<label
:for="name"
:class="autobuyerStateClass">
<span :class="autobuyerToggleClass"></span>
</label>
<input
:checked="isActive && globalToggle"
:disabled="!globalToggle"
:name="name"
type="checkbox"
/>
</div>
</div>
<div v-else-if="canBeBought" @click="purchase" class="c-autobuyer-buy-box" :class="autobuyerBuyBoxClass">
Buy the {{ name }} for {{ format(antimatterCost) }} antimatter
</div>`
});

View File

@ -93,7 +93,14 @@ const AutobuyerInputFunctions = {
copyValue: value => new Decimal(value),
tryParse: input => {
try {
const decimal = Decimal.fromString(input.replace(",", ""));
let decimal;
if (/^e\d*[.]?\d+$/u.test(input.replace(",", ""))) {
// Logarithm Notation
decimal = Decimal.pow10(parseFloat(input.replace(",", "").slice(1)));
} else {
// Scientific notation
decimal = Decimal.fromString(input.replace(",", ""));
}
return isNaN(decimal.mantissa) || isNaN(decimal.exponent) ? undefined : decimal;
} catch (e) {
return undefined;

View File

@ -7,13 +7,15 @@ Vue.component("autobuyer-interval-button", {
data() {
return {
cost: 0,
isMaxed: false
isMaxed: false,
isUnlocked: false
};
},
methods: {
update() {
this.cost = this.autobuyer.cost;
this.isMaxed = this.autobuyer.hasMaxedInterval;
this.isUnlocked = this.autobuyer.isUnlocked;
},
upgradeInterval() {
this.autobuyer.upgradeInterval();
@ -21,8 +23,13 @@ Vue.component("autobuyer-interval-button", {
},
template:
`<button
v-if="!isMaxed"
v-if="!isMaxed && isUnlocked"
class="o-autobuyer-btn l-autobuyer-box__button"
@click="upgradeInterval"
>40% smaller interval<br>Cost: {{shortenDimensions(cost)}} IP</button>`
});
>{{formatPercents(0.4)}} smaller interval<br>Cost: {{format(cost, 2, 0)}} IP</button>
<button
v-else-if="!isMaxed"
class="o-autobuyer-btn l-autobuyer-box__button">
Complete the challenge to upgrade interval
</button>`
});

View File

@ -21,7 +21,7 @@ Vue.component("autobuyer-priority-selector", {
},
template:
`<div>
<span>Priority:</span>
<div>Priority:</div>
<select @change="handleChange">
<option v-for="i in 9" :value="i" :selected="i === value">{{i}}</option>
</select>

View File

@ -5,8 +5,8 @@ Vue.component("autobuyer-toggles", {
return {
autobuyersOn: false,
bulkOn: false,
isAutoRealityUnlocked: false,
autoRealityMode: AutoRealityMode.RM,
showContinuum: false,
disableContinuum: false,
};
},
watch: {
@ -15,45 +15,56 @@ Vue.component("autobuyer-toggles", {
},
bulkOn(newValue) {
player.options.bulkOn = newValue;
}
},
computed: {
autoRealityModeDisplay() {
switch (this.autoRealityMode) {
case AutoRealityMode.RM: return "reality machines";
case AutoRealityMode.GLYPH: return "glyph level";
case AutoRealityMode.EITHER: return "either";
case AutoRealityMode.BOTH: return "both";
}
throw new Error("Unknown auto reality mode");
},
disableContinuum(newValue) {
player.options.disableContinuum = newValue;
}
},
methods: {
update() {
this.autobuyersOn = player.options.autobuyersOn;
this.bulkOn = player.options.bulkOn;
this.isAutoRealityUnlocked = Autobuyer.reality.isUnlocked;
this.autoRealityMode = Autobuyer.reality.mode;
this.showContinuum = Ra.has(RA_UNLOCKS.RA_LAITELA_UNLOCK);
this.disableContinuum = player.options.disableContinuum;
},
toggleAllAutobuyers() {
const allAutobuyersDisabled = Autobuyers.unlocked.every(autobuyer => !autobuyer.isActive);
if (allAutobuyersDisabled) {
for (const autobuyer of Autobuyers.unlocked) {
autobuyer.isActive = true;
}
} else {
for (const autobuyer of Autobuyers.unlocked) {
autobuyer.isActive = false;
}
}
}
},
template:
`<div class="l-autobuyer-toggles">
`<div class="c-subtab-option-container">
<primary-button-on-off-custom
v-model="autobuyersOn"
on="Disable autobuyers"
off="Enable autobuyers"
class="o-primary-btn--autobuyer-toggle"
class="o-primary-btn--subtab-option"
/>
<primary-button
class="o-primary-btn--subtab-option"
@click="toggleAllAutobuyers()">
Toggle all autobuyers
</primary-button>
<primary-button-on-off-custom
v-model="bulkOn"
on="Disable bulk buy"
off="Enable bulk buy"
class="o-primary-btn--autobuyer-toggle"
class="o-primary-btn--subtab-option"
/>
<primary-button-on-off-custom
v-if="showContinuum"
v-model="disableContinuum"
on="Enable Continuum"
off="Disable Continuum"
class="o-primary-btn--subtab-option"
/>
<primary-button
v-if="isAutoRealityUnlocked"
class="o-primary-btn--autobuyer-toggle"
onclick="Autobuyer.reality.toggleMode();"
>Auto reality mode: {{autoRealityModeDisplay}}</primary-button>
</div>`
});

View File

@ -1,32 +1,29 @@
"use strict";
Vue.component("autobuyers-tab", {
data: () => ({
hasContinuum: false
}),
methods: {
update() {
this.hasContinuum = Laitela.continuumActive;
}
},
template:
`<div class="l-autobuyers-tab">
<autobuyer-toggles class="l-autobuyers-tab__toggles" />
<div class="l-autobuyer-grid">
<div class="l-autobuyer-grid__row">
<reality-autobuyer-box />
<eternity-autobuyer-box />
</div>
<div class="l-autobuyer-grid__row">
<dimboost-autobuyer-box />
<galaxy-autobuyer-box />
<big-crunch-autobuyer-box />
</div>
<div class="l-autobuyer-grid__row">
<dimension-autobuyer-box v-for="column in 3" :key="column" :tier="column"/>
</div>
<div class="l-autobuyer-grid__row">
<dimension-autobuyer-box v-for="column in 3" :key="column + 3" :tier="column + 3"/>
</div>
<div class="l-autobuyer-grid__row">
<dimension-autobuyer-box v-for="column in 2" :key="column + 6" :tier="column + 6"/>
<tickspeed-autobuyer-box />
</div>
<div class="l-autobuyer-grid__row">
<sacrifice-autobuyer-box />
</div>
</div>
<autobuyer-toggles />
<reality-autobuyer-box />
<eternity-autobuyer-box />
<big-crunch-autobuyer-box />
<galaxy-autobuyer-box />
<dimboost-autobuyer-box />
<dimension-autobuyer-box v-if="!hasContinuum" v-for="tier in 8" :key="tier" :tier="tier"/>
<tickspeed-autobuyer-box v-if="!hasContinuum" />
<sacrifice-autobuyer-box />
<template v-if="hasContinuum">
Continuum makes Antimatter Dimension and tickspeed autobuyers obsolete, as you now automatically have a
<br>
certain amount of simulated Antimatter Dimension and tickspeed purchases based on your antimatter.
</template>
</div>`
});

View File

@ -4,16 +4,16 @@ Vue.component("big-crunch-autobuyer-box", {
data() {
return {
postBreak: false,
mode: AutoCrunchMode.AMOUNT,
mode: AUTO_CRUNCH_MODE.AMOUNT,
hasAdditionalModes: false
};
},
computed: {
autobuyer: () => Autobuyer.bigCrunch,
modes: () => [
AutoCrunchMode.AMOUNT,
AutoCrunchMode.TIME,
AutoCrunchMode.X_LAST
AUTO_CRUNCH_MODE.AMOUNT,
AUTO_CRUNCH_MODE.TIME,
AUTO_CRUNCH_MODE.X_LAST
]
},
methods: {
@ -24,21 +24,21 @@ Vue.component("big-crunch-autobuyer-box", {
},
modeProps(mode) {
switch (mode) {
case AutoCrunchMode.AMOUNT: return {
case AUTO_CRUNCH_MODE.AMOUNT: return {
title: "Big Crunch at X IP",
input: {
property: "amount",
type: "decimal"
},
};
case AutoCrunchMode.TIME: return {
case AUTO_CRUNCH_MODE.TIME: return {
title: "Seconds between Crunches",
input: {
property: "time",
type: "float"
},
};
case AutoCrunchMode.X_LAST: return {
case AUTO_CRUNCH_MODE.X_LAST: return {
title: "X times last Crunch",
input: {
property: "xLast",
@ -56,25 +56,29 @@ Vue.component("big-crunch-autobuyer-box", {
},
template:
`<autobuyer-box :autobuyer="autobuyer" :showInterval="!postBreak" name="Automatic Big Crunch">
<autobuyer-interval-button slot="beforeInterval" :autobuyer="autobuyer" />
<div v-if="postBreak">
<select
v-if="hasAdditionalModes"
class="c-autobuyer-box__mode-select l-autobuyer-box__mode-select"
@change="changeMode"
>
<option
v-for="optionMode in modes"
:value="optionMode"
:selected="mode === optionMode"
>{{modeProps(optionMode).title}}</option>
</select>
<span v-else>{{modeProps(mode).title}}:</span>
<autobuyer-input
:autobuyer="autobuyer"
:key="mode"
v-bind="modeProps(mode).input"
/>
</div>
<autobuyer-interval-button slot="intervalSlot" :autobuyer="autobuyer" />
<template v-if="postBreak">
<template slot="intervalSlot">
<select
v-if="hasAdditionalModes"
class="c-autobuyer-box__mode-select"
@change="changeMode"
>
<option
v-for="optionMode in modes"
:value="optionMode"
:selected="mode === optionMode"
>{{modeProps(optionMode).title}}</option>
</select>
<span v-else>{{modeProps(mode).title}}:</span>
</template>
<template slot="toggleSlot">
<autobuyer-input
:autobuyer="autobuyer"
:key="mode"
v-bind="modeProps(mode).input"
/>
</template>
</template>
</autobuyer-box>`
});

View File

@ -3,56 +3,67 @@
Vue.component("dimboost-autobuyer-box", {
data() {
return {
limitDimBoosts: false,
isBulkBuyUnlocked: false,
isBuyMaxUnlocked: false
};
},
watch: {
limitDimBoosts(newValue) {
this.autobuyer.limitDimBoosts = newValue;
}
},
computed: {
autobuyer: () => Autobuyer.dimboost
},
methods: {
update() {
const autobuyer = this.autobuyer;
this.isBulkBuyUnlocked = autobuyer.isBulkBuyUnlocked;
this.isBuyMaxUnlocked = autobuyer.isBuyMaxUnlocked;
this.isBulkBuyUnlocked = this.autobuyer.isBulkBuyUnlocked;
this.isBuyMaxUnlocked = this.autobuyer.isBuyMaxUnlocked;
this.limitDimBoosts = this.autobuyer.limitDimBoosts;
}
},
template:
`<autobuyer-box :autobuyer="autobuyer" :showInterval="!isBuyMaxUnlocked" name="Automatic DimBoosts">
<div v-if="isBuyMaxUnlocked">
<span>Buy max every X seconds:</span>
<autobuyer-interval-button slot="intervalSlot" :autobuyer="autobuyer" />
<template :slot=" isBuyMaxUnlocked ? 'toggleSlot' : 'intervalSlot' " style="margin-top: 1.2rem;">
<div class="o-autobuyer-toggle-checkbox c-autobuyer-box__small-text" @click="limitDimBoosts = !limitDimBoosts"
style="margin-top: 1.2rem;">
<input type="checkbox" :checked="limitDimBoosts"/>
<span>Limit dimboosts to:</span>
</div>
<autobuyer-input
:autobuyer="autobuyer"
type="float"
property="buyMaxInterval"
:autobuyer="autobuyer"
type="int"
property="maxDimBoosts"
/>
</div>
<template v-else>
<autobuyer-interval-button slot="beforeInterval" :autobuyer="autobuyer" />
<div>
<span class="c-autobuyer-box__small-text">Limit DimBoosts to:</span>
<autobuyer-input
:autobuyer="autobuyer"
type="int"
property="maxDimBoosts"
/>
</div>
<div>
<span class="c-autobuyer-box__small-text">Galaxies required to always DimBoost:</span>
<autobuyer-input
:autobuyer="autobuyer"
type="int"
property="galaxies"
/>
</div>
<div v-if="isBulkBuyUnlocked">
<span class="c-autobuyer-box__small-text">Bulk DimBoost Amount:</span>
<autobuyer-input
:autobuyer="autobuyer"
type="int"
property="bulk"
/>
</template>
<template :slot=" isBuyMaxUnlocked ? 'prioritySlot' : 'toggleSlot' ">
<div class="c-autobuyer-box__small-text" style="height: 3rem;">
Galaxies required to always DimBoost,
ignoring the limit:
</div>
<autobuyer-input
:autobuyer="autobuyer"
type="int"
property="galaxies"
/>
</template>
<template v-if="isBuyMaxUnlocked" slot="intervalSlot">
<div class="c-autobuyer-box__small-text" style="margin-top: 1.2rem;">Activates every X seconds:</div>
<autobuyer-input
:autobuyer="autobuyer"
type="float"
property="buyMaxInterval"
/>
</template>
<template v-else-if="isBulkBuyUnlocked" slot="prioritySlot">
<div class="c-autobuyer-box__small-text" style="margin-top: 1.2rem;">Bulk DimBoost Amount:</div>
<autobuyer-input
:autobuyer="autobuyer"
type="int"
property="bulk"
/>
</template>
</autobuyer-box>`
});

View File

@ -10,6 +10,8 @@ Vue.component("dimension-autobuyer-box", {
return {
hasMaxedInterval: false,
hasMaxedBulk: false,
isUnlocked: false,
bulkUnlimited: false,
bulk: 1,
cost: 1
};
@ -20,14 +22,16 @@ Vue.component("dimension-autobuyer-box", {
if (!this.hasMaxedBulk) {
bulk = Math.min(bulk * 2, 1e100);
}
return `${shortenDimensions(bulk)}x bulk purchase`;
return `${formatX(bulk, 2, 0)} bulk purchase`;
}
},
methods: {
update() {
const autobuyer = this.autobuyer;
this.hasMaxedInterval = autobuyer.hasMaxedInterval;
this.isUnlocked = autobuyer.isUnlocked;
this.hasMaxedBulk = autobuyer.hasMaxedBulk;
this.bulkUnlimited = autobuyer.hasUnlimitedBulk;
this.bulk = autobuyer.bulk;
this.cost = autobuyer.cost;
},
@ -37,15 +41,20 @@ Vue.component("dimension-autobuyer-box", {
},
template:
`<button
v-if="hasMaxedInterval"
v-if="hasMaxedInterval && !bulkUnlimited && isUnlocked"
class="o-autobuyer-btn"
@click="upgradeBulk"
>
<span>{{bulkDisplay}}</span>
<template v-if="!hasMaxedBulk">
<br>
<span>Cost: {{shortenDimensions(cost)}} IP</span>
<span>Cost: {{format(cost, 2, 0)}} IP</span>
</template>
</button>
<button
v-else-if="hasMaxedInterval && !bulkUnlimited"
class="o-autobuyer-btn l-autobuyer-box__button">
Complete the challenge to upgrade bulk
</button>`
}
},
@ -54,7 +63,7 @@ Vue.component("dimension-autobuyer-box", {
},
data() {
return {
mode: AutobuyerMode.BUY_SINGLE
mode: AUTOBUYER_MODE.BUY_SINGLE
};
},
computed: {
@ -62,12 +71,12 @@ Vue.component("dimension-autobuyer-box", {
return Autobuyer.dimension(this.tier);
},
name() {
return `${DISPLAY_NAMES[this.tier]} Dimension Autobuyer`;
return `${AntimatterDimension(this.tier).displayName} Dimension Autobuyer`;
},
modeDisplay() {
switch (this.mode) {
case AutobuyerMode.BUY_SINGLE: return "Buys singles";
case AutobuyerMode.BUY_10: return "Buys max";
case AUTOBUYER_MODE.BUY_SINGLE: return "Buys singles";
case AUTOBUYER_MODE.BUY_10: return "Buys max";
}
throw "Unknown dimension autobuyer mode";
}
@ -83,12 +92,15 @@ Vue.component("dimension-autobuyer-box", {
},
template:
`<autobuyer-box :autobuyer="autobuyer" :name="name" showInterval>
<template slot="beforeInterval">
<template slot="intervalSlot">
<bulk-button :autobuyer="autobuyer" />
<autobuyer-interval-button :autobuyer="autobuyer" />
</template>
<template slot="toggleSlot">
<button class="o-autobuyer-btn" @click="toggleMode">{{modeDisplay}}</button>
</template>
<div class="l-autobuyer-box__fill" />
<autobuyer-priority-selector :autobuyer="autobuyer" class="l-autobuyer-box__priority-selector" />
<template slot="prioritySlot">
<autobuyer-priority-selector :autobuyer="autobuyer" class="l-autobuyer-box__priority-selector" />
</template>
</autobuyer-box>`
});

Some files were not shown because too many files have changed in this diff Show More