Implemented resetting layers and associated features/fixes

This commit is contained in:
thepaperpilot 2021-06-15 23:07:32 -05:00
parent 232e628daa
commit 95cc29664b
20 changed files with 451 additions and 252 deletions

View file

@ -20,7 +20,7 @@
<button @click="buyable.sellOne" v-if="buyable.sellOne !== undefined && buyable.canSellOne !== false" <button @click="buyable.sellOne" v-if="buyable.sellOne !== undefined && buyable.canSellOne !== false"
:class="{ 'buyable-button': true, can: buyable.unlocked, locked: !buyable.unlocked, feature: true }" :class="{ 'buyable-button': true, can: buyable.unlocked, locked: !buyable.unlocked, feature: true }"
:style="{ 'background-color': buyable.canSellOne ? layerColor : '' }"> :style="{ 'background-color': buyable.canSellOne ? layerColor : '' }">
Sell All Sell One
</button> </button>
</div> </div>
</div> </div>

View file

@ -1,8 +1,15 @@
<template> <template>
<div v-if="shown" :style="style" :class="{ challenge: true, [challengeClass]: true }"> <div v-if="shown" :style="style"
:class="{
feature: true,
challenge: true,
resetNotify: challenge.active,
notify: challenge.active && challenge.canComplete,
done: challenge.completed,
maxed: challenge.maxed
}">
<div v-if="title"><component :is="title" /></div> <div v-if="title"><component :is="title" /></div>
<button :class="{ [layer || tab.layer]: true, longUpg: true, can: true }" :style="{ backgroundColor: buttonColor }" <button :style="{ backgroundColor: challenge.maxed ? null : buttonColor }" @click="toggle">
@click="toggle">
{{ buttonText }} {{ buttonText }}
</button> </button>
<component v-if="fullDisplay" :is="fullDisplay" /> <component v-if="fullDisplay" :is="fullDisplay" />
@ -32,15 +39,6 @@ export default {
shown() { shown() {
return this.challenge.unlocked && !(player.hideChallenges && this.challenge.maxes); return this.challenge.unlocked && !(player.hideChallenges && this.challenge.maxes);
}, },
challengeClass() {
if (this.challenge.canComplete) {
return "canComplete";
}
if (this.challenge.completed) {
return "done";
}
return "locked";
},
style() { style() {
return [ return [
layers[this.layer || this.tab.layer].componentStyles?.challenge, layers[this.layer || this.tab.layer].componentStyles?.challenge,
@ -48,8 +46,11 @@ export default {
]; ];
}, },
title() { title() {
if (this.challenge.title) { if (this.challenge.titleDisplay) {
return coerceComponent(this.challenge.titleDisplay, 'h3'); return coerceComponent(this.challenge.titleDisplay, 'div');
}
if (this.challenge.name) {
return coerceComponent(this.challenge.name, 'h3');
} }
return null; return null;
}, },
@ -82,6 +83,30 @@ export default {
<style scoped> <style scoped>
.challenge { .challenge {
margin: var(--feature-margin); background-color: var(--locked);
width: 250px;
min-height: 250px;
color: black;
font-size: 15px;
display: flex;
flex-flow: column;
align-items: center;
}
.challenge.done {
background-color: var(--bought);
}
.challenge button {
min-height: 50px;
width: 120px;
border-radius: var(--border-radius);
cursor: pointer;
box-shadow: none !important;
background: transparent;
}
.challenge.maxed button {
cursor: unset;
} }
</style> </style>

View file

@ -32,14 +32,11 @@ export default {
return coerceComponent(`format(${this.goal}) ${this.currencyDisplayName || 'points'}`); return coerceComponent(`format(${this.goal}) ${this.currencyDisplayName || 'points'}`);
}, },
rewardDescription() { rewardDescription() {
return coerceComponent(this.challenge.goalDescription); return coerceComponent(this.challenge.rewardDescription);
}, },
rewardDisplay() { rewardDisplay() {
if (this.challenge.rewardDisplay) { if (this.challenge.rewardDisplay) {
return coerceComponent(this.challenge.rewardDisplay); return coerceComponent(`Currently: ${this.challenge.rewardDisplay}`);
}
if (this.challenge.rewardEffect) {
return coerceComponent(`Currently: ${this.challenge.rewardEffect}`);
} }
return null; return null;
}, },

View file

@ -58,7 +58,13 @@ export default {
return `${prefix} ${baseAmount} / ${nextAt} ${baseResource}`; return `${prefix} ${baseAmount} / ${nextAt} ${baseResource}`;
} else { } else {
return `Next at ${formatWhole(layers[this.layer || this.tab.layer].baseAmount)} ${layers[this.layer || this.tab.layer].baseResource}`; let amount;
if (layers[this.layer || this.tab.layer].roundUpCost) {
amount = formatWhole(layers[this.layer || this.tab.layer].nextAt);
} else {
amount = format(layers[this.layer || this.tab.layer].nextAt);
}
return `Next at ${amount} ${layers[this.layer || this.tab.layer].baseResource}`;
} }
} }
return ""; return "";

View file

@ -1,5 +1,5 @@
<template> <template>
<div v-if="milestone.shown" :style="style" :class="{ feature: true, milestone: !milestone.earned, milestoneDone: milestone.earned }"> <div v-if="milestone.shown" :style="style" :class="{ feature: true, milestone: true, done: milestone.earned }">
<div v-if="requirementDisplay"><component :is="requirementDisplay" /></div> <div v-if="requirementDisplay"><component :is="requirementDisplay" /></div>
<div v-if="effectDisplay"><component :is="effectDisplay" /></div> <div v-if="effectDisplay"><component :is="effectDisplay" /></div>
<component v-if="optionsDisplay" :is="optionsDisplay" /> <component v-if="optionsDisplay" :is="optionsDisplay" />
@ -42,7 +42,7 @@ export default {
return null; return null;
}, },
optionsDisplay() { optionsDisplay() {
if (this.milestone.optionsDisplay) { if (this.milestone.optionsDisplay && this.milestone.earned) {
return coerceComponent(this.milestone.optionsDisplay, 'div'); return coerceComponent(this.milestone.optionsDisplay, 'div');
} }
return null; return null;
@ -62,5 +62,11 @@ export default {
border-width: 4px; border-width: 4px;
border-radius: 5px; border-radius: 5px;
color: rgba(0, 0, 0, 0.5); color: rgba(0, 0, 0, 0.5);
margin: 0;
}
.milestone.done {
background-color: var(--bought);
cursor: default;
} }
</style> </style>

View file

@ -10,6 +10,7 @@
import { layers } from '../../store/layers'; import { layers } from '../../store/layers';
import { resetLayer } from '../../util/layers'; import { resetLayer } from '../../util/layers';
import { coerceComponent } from '../../util/vue'; import { coerceComponent } from '../../util/vue';
import './features.css'
export default { export default {
name: 'prestige-button', name: 'prestige-button',

View file

@ -1,7 +1,7 @@
.feature, .feature,
.feature button { .feature button {
position: relative; position: relative;
padding-top: 5px; padding: 5px;
border-radius: var(--border-radius); border-radius: var(--border-radius);
border: 2px solid rgba(0, 0, 0, 0.125); border: 2px solid rgba(0, 0, 0, 0.125);
margin: var(--feature-margin); margin: var(--feature-margin);

View file

@ -1,7 +1,9 @@
<template> <template>
<div class="field"> <div class="field">
<span class="field-title" v-if="title">{{ title }}</span> <span class="field-title" v-if="title">{{ title }}</span>
<input type="range" :value="value" @input="e => $emit('change', e.target.value)" :min="min" :max="max" /> <tooltip :text="`${value}`">
<input type="range" :value="value" @input="e => $emit('change', parseInt(e.target.value))" :min="min" :max="max" />
</tooltip>
</div> </div>
</template> </template>
@ -9,7 +11,7 @@
import './fields.css'; import './fields.css';
export default { export default {
name: 'TextField', name: 'Slider',
props: { props: {
title: String, title: String,
value: Number, value: Number,
@ -20,4 +22,11 @@ export default {
</script> </script>
<style scoped> <style scoped>
input {
margin-right: 0;
}
.value {
margin-left: 10px;
}
</style> </style>

View file

@ -13,7 +13,7 @@
<default-layer-tab v-else /> <default-layer-tab v-else />
</branches> </branches>
</div> </div>
<button v-if="!disableMinimize" class="minimize" @click="toggleMinimized"></button> <button v-if="minimizable" class="minimize" @click="toggleMinimized"></button>
</div> </div>
</LayerProvider> </LayerProvider>
</template> </template>
@ -32,7 +32,7 @@ export default {
layer: String, layer: String,
index: Number, index: Number,
forceFirstTab: Boolean, forceFirstTab: Boolean,
disableMinimize: Boolean, minimizable: Boolean,
tab: Function tab: Function
}, },
data() { data() {
@ -40,7 +40,7 @@ export default {
}, },
computed: { computed: {
minimized() { minimized() {
return !this.disableMinimize && player.minimized[this.layer]; return this.minimizable && player.minimized[this.layer];
}, },
name() { name() {
return layers[this.layer].name; return layers[this.layer].name;
@ -91,6 +91,9 @@ export default {
}, },
watch: { watch: {
minimized(newValue) { minimized(newValue) {
if (this.tab == undefined) {
return;
}
const tab = this.tab(); const tab = this.tab();
if (tab != undefined) { if (tab != undefined) {
if (newValue) { if (newValue) {
@ -108,6 +111,9 @@ export default {
} }
}, },
mounted() { mounted() {
if (this.tab == undefined) {
return;
}
const tab = this.tab(); const tab = this.tab();
if (tab != undefined) { if (tab != undefined) {
if (this.minimized) { if (this.minimized) {
@ -152,7 +158,7 @@ export default {
} }
.layer-tab:not(.minimized) { .layer-tab:not(.minimized) {
padding-top: 50px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
min-height: 100%; min-height: 100%;
flex-grow: 1; flex-grow: 1;
@ -160,6 +166,10 @@ export default {
position: relative; position: relative;
} }
.inner-tab > .layer-container > .layer-tab:not(.minimized) {
padding-top: 50px;
}
.layer-tab.minimized { .layer-tab.minimized {
position: absolute; position: absolute;
top: 0; top: 0;
@ -207,6 +217,11 @@ export default {
padding-left: 14px; padding-left: 14px;
} }
.subtabs.floating {
justify-content: center;
margin-top: -25px;
}
.modal-body .layer-tab { .modal-body .layer-tab {
padding-bottom: 0; padding-bottom: 0;
} }

View file

@ -1,12 +1,21 @@
<template> <template>
<LayerProvider :layer="layer || tab.layer" :index="tab.index"> <div v-if="microtabs" class="microtabs">
<component :is="display" /> <LayerProvider :layer="layer || tab.layer" :index="tab.index">
</LayerProvider> <div v-if="microtabs" class="tabs" :class="{ floating }">
<tab-button v-for="(microtab, id) in microtabs" @selectTab="selectMicrotab(id)" :key="id"
:activeTab="id === activeMicrotab.id" :options="microtab" :text="id" />
</div>
<layer-tab v-if="embed" :layer="embed" />
<component v-else :is="display" />
</LayerProvider>
</div>
</template> </template>
<script> <script>
import { layers } from '../../store/layers'; import { layers } from '../../store/layers';
import { player } from '../../store/proxies';
import { coerceComponent } from '../../util/vue'; import { coerceComponent } from '../../util/vue';
import themes from '../../data/themes';
export default { export default {
name: 'microtab', name: 'microtab',
@ -17,13 +26,50 @@ export default {
id: String id: String
}, },
computed: { computed: {
floating() {
return themes[player.theme].floatingTabs;
},
tabFamily() {
return layers[this.layer || this.tab.layer].microtabs[this.family];
},
microtabs() {
return Object.keys(this.tabFamily)
.filter(microtab => microtab !== 'activeMicrotab' && this.tabFamily[microtab].unlocked !== false)
.reduce((acc, curr) => {
acc[curr] = this.tabFamily[curr];
return acc;
}, {});
},
activeMicrotab() {
return this.id != undefined ? this.tabFamily[this.id] : this.tabFamily.activeMicrotab;
},
embed() {
return this.activeMicrotab.embedLayer;
},
display() { display() {
const family = layers[this.layer || this.tab.layer].microtabs[this.family]; return coerceComponent(this.activeMicrotab.display);
return coerceComponent((this.id !== undefined ? family[this.id] : family.activeMicrotab).display); }
},
methods: {
selectMicrotab(tab) {
player.subtabs[this.layer || this.tab.layer][this.family] = tab;
} }
} }
}; };
</script> </script>
<style scoped> <style scoped>
.microtabs {
margin: var(--feature-margin) -11px;
position: relative;
border: solid 4px var(--separator);
}
.tabs:not(.floating) {
text-align: left;
border-bottom: inherit;
border-width: 4px;
box-sizing: border-box;
height: 50px;
}
</style> </style>

View file

@ -7,7 +7,7 @@
<LayerProvider :layer="tab" :index="index" v-if="tab in components && components[tab]"> <LayerProvider :layer="tab" :index="index" v-if="tab in components && components[tab]">
<component :is="components[tab]" /> <component :is="components[tab]" />
</LayerProvider> </LayerProvider>
<layer-tab :layer="tab" :index="index" v-else-if="tab in components" <layer-tab :layer="tab" :index="index" v-else-if="tab in components" :minimizable="true"
:tab="() => $refs[`tab-${index}`] && $refs[`tab-${index}`][0]" /> :tab="() => $refs[`tab-${index}`] && $refs[`tab-${index}`][0]" />
<component :is="tab" :index="index" v-else /> <component :is="tab" :index="index" v-else />
</div> </div>

View file

@ -8,7 +8,7 @@
</span> </span>
<modal :show="showModal" @close="closeModal"> <modal :show="showModal" @close="closeModal">
<div slot="header"><h2 v-if="modalHeader">{{ modalHeader }}</h2></div> <div slot="header"><h2 v-if="modalHeader">{{ modalHeader }}</h2></div>
<layer-tab slot="body" v-if="modal" :layer="modal" :index="tab.index" :forceFirstTab="true" :disableMinimize="true" /> <layer-tab slot="body" v-if="modal" :layer="modal" :index="tab.index" :forceFirstTab="true" />
</modal> </modal>
</div> </div>
</template> </template>

View file

@ -73,7 +73,7 @@ export default {
done() {return player[this.layer].best.gte(4)}, done() {return player[this.layer].best.gte(4)},
effectDisplay: "You can toggle beep and boop (which do nothing)", effectDisplay: "You can toggle beep and boop (which do nothing)",
optionsDisplay: ` optionsDisplay: `
<div> <div style="display: flex; justify-content: center">
<Toggle :value="player.c.beep" @change="value => player.c.beep = value" /> <Toggle :value="player.c.beep" @change="value => player.c.beep = value" />
<Toggle :value="player.f.boop" @change="value => player.f.boop = value" /> <Toggle :value="player.f.boop" @change="value => player.f.boop = value" />
</div> </div>
@ -160,21 +160,23 @@ export default {
}, },
buyables: { buyables: {
showRespec: true, showRespec: true,
respec() { // Optional, reset things and give back your currency. Having this function makes a respec button appear reset() { // Optional, reset things and give back your currency. Having this function makes a respec button appear
player[this.layer].points = player[this.layer].points.add(player[this.layer].spentOnBuyables) // A built-in thing to keep track of this but only keeps a single value player[this.layer].points = player[this.layer].points.add(player[this.layer].spentOnBuyables) // A built-in thing to keep track of this but only keeps a single value
this.reset; this.reset();
resetLayer(this.layer, true) // Force a reset resetLayer(this.layer, true) // Force a reset
}, },
respecText: "Respec Thingies", // Text on Respec button, optional respecText: "Respec Thingies", // Text on Respec button, optional
respecMessage: "Are you sure? Respeccing these doesn't accomplish much.", respecMessage: "Are you sure? Respeccing these doesn't accomplish much.",
11: { 11: {
title: "Exhancers", // Optional, displayed at the top in a larger font title: "Exhancers", // Optional, displayed at the top in a larger font
cost(x) { // cost for buying xth buyable, can be an object if there are multiple currencies cost() { // cost for buying xth buyable, can be an object if there are multiple currencies
let x = this.amount;
if (x.gte(25)) x = x.pow(2).div(25) if (x.gte(25)) x = x.pow(2).div(25)
let cost = Decimal.pow(2, x.pow(1.5)) let cost = Decimal.pow(2, x.pow(1.5))
return cost.floor() return cost.floor()
}, },
effect(x) { // Effects of owning x of the items, x is a decimal effect() { // Effects of owning x of the items, x is a decimal
let x = this.amount;
let eff = {} let eff = {}
if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1)) if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1))
else eff.first = Decimal.pow(1/25, x.times(-1).pow(1.1)) else eff.first = Decimal.pow(1/25, x.times(-1).pow(1.1))
@ -212,7 +214,6 @@ export default {
doReset(resettingLayer){ // Triggers when this layer is being reset, along with the layer doing the resetting. Not triggered by lower layers resetting, but is by layers on the same row. doReset(resettingLayer){ // Triggers when this layer is being reset, along with the layer doing the resetting. Not triggered by lower layers resetting, but is by layers on the same row.
if(layers[resettingLayer].row > this.row) resetLayerData(this.layer, ["points"]) // This is actually the default behavior if(layers[resettingLayer].row > this.row) resetLayerData(this.layer, ["points"]) // This is actually the default behavior
}, },
layerShown() {return true}, // Condition for when layer appears on the tree
automate() { automate() {
}, // Do any automation inherent to this layer if appropriate }, // Do any automation inherent to this layer if appropriate
resetsNothing() {return false}, resetsNothing() {return false},
@ -229,15 +230,13 @@ export default {
microtabs: { microtabs: {
stuff: { stuff: {
first: { first: {
content: ["upgrades", ["display-text", function() {return "confirmed"}]] display: `<div v-frag>
<upgrades />
<div>confirmed</div>
</div>`
}, },
second: { second: {
embedLayer: "f", embedLayer: "f"
content: [["upgrade", 11],
["row", [["upgrade", 11], "blank", "blank", ["upgrade", 11],]],
["display-text", function() {return "double confirmed"}]]
}, },
}, },
otherStuff: { otherStuff: {
@ -377,7 +376,7 @@ export default {
<div v-frag> <div v-frag>
<h1> C O N F I R M E D </h1> <h1> C O N F I R M E D </h1>
<spacer /> <spacer />
<microtab family="stuff" style="width: 600px; height: 350px; background-color: brown; border-style: solid" /> <microtab family="stuff" style="width: 660px; height: 370px; background-color: brown; border: solid white; margin: auto" />
<div>Adjust how many points H gives you!</div> <div>Adjust how many points H gives you!</div>
<Slider :value="player.c.otherThingy" @change="value => player.c.otherThingy = value" :min="1" :max="30" /> <Slider :value="player.c.otherThingy" @change="value => player.c.otherThingy = value" :min="1" :max="30" />
</div> </div>

View file

@ -33,7 +33,6 @@ export default {
//directMult() {return new Decimal(player.c.otherThingy)}, //directMult() {return new Decimal(player.c.otherThingy)},
row: 1, row: 1,
layerShown() {return true},
branches: [{ target: "c", 'stroke-width': '25px', 'stroke': 'blue', style: 'filter: blur(5px)' }], // When this layer appears, a branch will appear from this layer to any layers here. Each entry can be a pair consisting of a layer id and a color. branches: [{ target: "c", 'stroke-width': '25px', 'stroke': 'blue', style: 'filter: blur(5px)' }], // When this layer appears, a branch will appear from this layer to any layers here. Each entry can be a pair consisting of a layer id and a color.
tooltipLocked() { // Optional, tooltip displays when the layer is locked tooltipLocked() { // Optional, tooltip displays when the layer is locked

View file

@ -13,7 +13,7 @@ const g = {
symbol: "TH", symbol: "TH",
branches: ["c"], branches: ["c"],
color: '#6d3678', color: '#6d3678',
layerShown: true, shown: true,
canClick() {return player.points.gte(10)}, canClick() {return player.points.gte(10)},
tooltip: "Thanos your points", tooltip: "Thanos your points",
onClick() { onClick() {
@ -23,8 +23,7 @@ const g = {
}; };
const h = { const h = {
id: "h", id: "h",
branches: ["g", { target: 'flatBoi', featureType: 'bar', endOffset: { x: () => -50 + 100 * layers.c.bars.flatBoi.progress.toNumber() } }], branches: ["g", () => ({ target: 'flatBoi', featureType: 'bar', endOffset: { x: -50 + 100 * layers.c.bars.flatBoi.progress.toNumber() } })],
layerShown: true,
tooltip() {return "Restore your points to " + player.c.otherThingy}, tooltip() {return "Restore your points to " + player.c.otherThingy},
row: "side", row: "side",
canClick() {return player.points.lt(player.c.otherThingy)}, canClick() {return player.points.lt(player.c.otherThingy)},
@ -49,6 +48,7 @@ const main = {
<div v-if="Decimal.gt($store.getters.pointGain, 0)"> <div v-if="Decimal.gt($store.getters.pointGain, 0)">
({{ player.oompsMag != 0 ? format(player.oomps) + " OOM" + (player.oompsMag < 0 ? "^OOM" : player.oompsMag > 1 ? "^" + player.oompsMag : "") + "s" : formatSmall($store.getters.pointGain) }}/sec) ({{ player.oompsMag != 0 ? format(player.oomps) + " OOM" + (player.oompsMag < 0 ? "^OOM" : player.oompsMag > 1 ? "^" + player.oompsMag : "") + "s" : formatSmall($store.getters.pointGain) }}/sec)
</div> </div>
<spacer />
<tree :append="true" /> <tree :append="true" />
</div>`, </div>`,
name: "Tree" name: "Tree"

View file

@ -26,6 +26,10 @@ h1, h2, h3, b, input {
display: inline; display: inline;
} }
button {
color: black;
}
a, a,
.button, .button,
.link { .link {

View file

@ -17,11 +17,13 @@ function updateOOMPS(diff) {
if (player.points != undefined) { if (player.points != undefined) {
player.oompsMag = 0; player.oompsMag = 0;
if (player.points.lte(new Decimal(1e100))) { if (player.points.lte(new Decimal(1e100))) {
player.lastPoints = player.points;
return; return;
} }
let curr = new Decimal(player.points); let curr = player.points;
let prev = player.lastPoints || new Decimal(0); let prev = player.lastPoints || new Decimal(0);
player.lastPoints = curr;
if (curr.gt(prev)) { if (curr.gt(prev)) {
if (curr.gte("10^^8")) { if (curr.gte("10^^8")) {
curr = curr.slog(1e10); curr = curr.slog(1e10);

View file

@ -2,7 +2,7 @@ import { isFunction, isPlainObject } from '../util/common';
import { createProxy, createGridProxy, player } from './proxies'; import { createProxy, createGridProxy, player } from './proxies';
import Decimal from '../util/bignum'; import Decimal from '../util/bignum';
import store from './index'; import store from './index';
import { resetLayer, noCache, getStartingBuyables, getStartingClickables, getStartingChallenges } from '../util/layers'; import { noCache, getStartingBuyables, getStartingClickables, getStartingChallenges, defaultLayerProperties } from '../util/layers';
export const layers = {}; export const layers = {};
export const hotkeys = []; export const hotkeys = [];
@ -28,19 +28,15 @@ export function addLayer(layer) {
// Set default property values // Set default property values
layer = Object.assign({}, defaultLayerProperties, layer); layer = Object.assign({}, defaultLayerProperties, layer);
layer.layer = layer.id; layer.layer = layer.id;
if (layer.shown == undefined) {
layer.shown = true;
}
if (layer.onClick != undefined) {
layer.onClick.forceCached = false;
}
if (layer.update != undefined) {
layer.update.forceCached = false;
}
const getters = {}; const getters = {};
// Process each feature // Process each feature
for (let property of uncachedProperties) {
if (layer[property]) {
layer[property].forceCached = false;
}
}
for (let property of gridProperties) { for (let property of gridProperties) {
if (layer[property]) { if (layer[property]) {
setRowCol(layer[property]); setRowCol(layer[property]);
@ -144,8 +140,17 @@ export function addLayer(layer) {
} }
for (let id in layer.challenges) { for (let id in layer.challenges) {
if (isPlainObject(layer.challenges[id])) { if (isPlainObject(layer.challenges[id])) {
if (layer.challenges[id].onComplete != undefined) {
layer.challenges[id].onComplete.forceCached = false;
}
if (layer.challenges[id].onEnter != undefined) {
layer.challenges[id].onEnter.forceCached = false;
}
if (layer.challenges[id].onExit != undefined) {
layer.challenges[id].onExit.forceCached = false;
}
layer.challenges[id].completed = function() { layer.challenges[id].completed = function() {
return !layer.deactivated && !!player[layer.id].challenges[id]; return !layer.deactivated && player[layer.id].challenges[id]?.gt(0);
} }
layer.challenges[id].completions = function() { layer.challenges[id].completions = function() {
return player[layer.id].challenges[id]; return player[layer.id].challenges[id];
@ -185,7 +190,7 @@ export function addLayer(layer) {
if (layer.challenges[id].completionLimit == undefined) { if (layer.challenges[id].completionLimit == undefined) {
layer.challenges[id].completionLimit = new Decimal(1); layer.challenges[id].completionLimit = new Decimal(1);
} }
layer.challenges[id].toggle = function() { layer.challenges[id].toggle = noCache(function() {
let exiting = player[layer.id].activeChallenge === id; let exiting = player[layer.id].activeChallenge === id;
if (exiting) { if (exiting) {
if (this.canComplete && !this.maxed) { if (this.canComplete && !this.maxed) {
@ -199,18 +204,21 @@ export function addLayer(layer) {
} }
player[layer.id].activeChallenge = null; player[layer.id].activeChallenge = null;
this.onExit?.(); this.onExit?.();
resetLayer(layer.id, true); layer.reset(true);
} else if (!exiting && this.canStart) { } else if (!exiting && this.canStart) {
resetLayer(layer.id, true); layer.reset(true);
player[layer.id].activeChallenge = id; player[layer.id].activeChallenge = id;
this.onEnter?.(); this.onEnter?.();
} }
} });
if (layer.challenges[id].canStart == undefined) { if (layer.challenges[id].canStart == undefined) {
layer.challenges[id].canStart = true; layer.challenges[id].canStart = true;
} }
} }
} }
layer.activeChallenge = function() {
return Object.values(this.challenges).find(challenge => challenge.active);
}
} }
if (layer.buyables) { if (layer.buyables) {
if (player[layer.id].buyables == undefined) { if (player[layer.id].buyables == undefined) {
@ -220,6 +228,8 @@ export function addLayer(layer) {
layer.buyables.reset = noCache(function() { layer.buyables.reset = noCache(function() {
player[this.layer].buyables = getStartingBuyables(layer); player[this.layer].buyables = getStartingBuyables(layer);
}); });
} else {
layer.buyables.reset.forceCached = false;
} }
for (let id in layer.buyables) { for (let id in layer.buyables) {
if (isPlainObject(layer.buyables[id])) { if (isPlainObject(layer.buyables[id])) {
@ -366,11 +376,18 @@ export function addLayer(layer) {
} }
} }
layer.microtabs[family].activeMicrotab = function() { layer.microtabs[family].activeMicrotab = function() {
if (this[player.subtabs[layer.id][family]]?.unlocked !== false) { if (this[player.subtabs[layer.id][family]] && this[player.subtabs[layer.id][family]].unlocked !== false) {
return this[player.subtabs[layer.id][family]]; return this[player.subtabs[layer.id][family]];
} }
// Default to first unlocked tab // Default to first unlocked tab
return Object.values(this).find(microtab => microtab.unlocked !== false); return this[Object.keys(this).find(microtab => microtab !== 'activeMicrotab' && this[microtab].unlocked !== false)];
}
}
}
if (layer.hotkeys) {
for (let id in layer.hotkeys) {
if (layer.hotkeys[id].onPress) {
layer.hotkeys[id].onPress.forceCached = false;
} }
} }
} }
@ -409,180 +426,7 @@ export function reloadLayer(layer) {
addLayer(layer); addLayer(layer);
} }
export const defaultLayerProperties = { const uncachedProperties = [ 'startData', 'onClick', 'update', 'reset', 'hardReset' ];
type: "none",
layerShown: true,
glowColor: "red",
minWidth: 640,
displayRow() {
return this.row;
},
symbol() {
return this.id;
},
unlocked() {
if (player[this.id].unlocked) {
return true;
}
if (this.type !== "none" && this.canReset && this.layerShown) {
return true;
}
return false;
},
trueGlowColor() {
if (this.subtabs) {
for (let subtab of Object.values(this.subtabs)) {
if (subtab.notify) {
return subtab.glowColor || "red";
}
}
}
if (this.microtabs) {
for (let microtab of Object.values(this.microtabs)) {
if (microtab.notify) {
return microtab.glowColor || "red";
}
}
}
return this.glowColor || "red";
},
resetGain() {
if (this.type === "none" || this.type === "custom") {
return new Decimal(0);
}
if (this.gainExp?.eq(0)) {
return new Decimal(0);
}
if (this.baseAmount.lt(this.requires)) {
return new Decimal(0);
}
if (this.type === "static") {
if (!this.canBuyMax) {
return new Decimal(1);
}
let gain = this.baseAmount.div(this.requires).div(this.gainMult || 1).max(1).log(this.base)
.times(this.gainExp || 1).pow(Decimal.pow(this.exponent || 1, -1));
gain = gain.times(this.directMult || 1);
return gain.floor().sub(player[this.layer].points).add(1).max(1);
}
if (this.type === "normal") {
let gain = this.baseAmount.div(this.requires).pow(this.exponent || 1).times(this.gainMult || 1)
.pow(this.gainExp || 1);
if (this.softcap && gain.gte(this.softcap)) {
gain = gain.pow(this.softcapPower).times(this.softcap.pow(Decimal.sub(1, this.softcapPower)));
}
gain = gain.times(this.directMult || 1);
return gain.floor().max(0);
}
// Unknown prestige type
return new Decimal(0);
},
nextAt() {
if (this.type === "none" || this.type === "custom") {
return new Decimal(Infinity);
}
if (this.gainMult?.lte(0) || this.gainExp?.lte(0)) {
return new Decimal(Infinity);
}
if (this.type === "static") {
const amount = player[this.layer].points.div(this.directMult || 1);
const extraCost = Decimal.pow(this.base, amount.pow(this.exponent || 1).div(this.gainExp || 1))
.times(this.gainMult || 1);
let cost = extraCost.times(this.requires).max(this.requires);
if (this.roundUpCost) {
cost = cost.ceil();
}
return cost;
}
if (this.type === "normal") {
let next = this.resetGain.div(this.directMult || 1);
if (this.softcap && next.gte(this.softcap)) {
next = next.div(this.softcap.pow(Decimal.sub(1, this.softcapPower)))
.pow(Decimal.div(1, this.softcapPower));
}
next = next.root(this.gainExp || 1).div(this.gainMult || 1).root(this.exponent || 1)
.times(this.requires).max(this.requires);
if (this.roundUpCost) {
next = next.ceil();
}
return next;
}
// Unknown prestige type
return new Decimal(0);
},
nextAtMax() {
if (!this.canBuyMax || this.type !== "static") {
return this.nextAt;
}
const amount = player[this.layer].points.plus(this.resetGain).div(this.directMult || 1);
const extraCost = Decimal.pow(this.base, amount.pow(this.exponent || 1).div(this.gainExp || 1))
.times(this.gainMult || 1);
let cost = extraCost.times(this.requires).max(this.requires);
if (this.roundUpCost) {
cost = cost.ceil();
}
return cost;
},
canReset() {
if (this.type === "normal") {
return this.baseAmount.gte(this.requires);
}
if (this.type === "static") {
return this.baseAmount.gte(this.nextAt);
}
return false;
},
notify() {
if (this.upgrades) {
if (Object.values(this.upgrades).some(upgrade => upgrade.canAfford && !upgrade.bought && upgrade.unlocked)) {
return true;
}
}
if (player[this.layer].activeChallenge && this.challenges[player[this.layer].activeChallenge].canComplete) {
return true;
}
if (this.subtabs) {
if (Object.values(this.subtabs).some(subtab => subtab.notify)) {
return true;
}
}
if (this.microtabs) {
if (Object.values(this.microtabs).some(subtab => subtab.notify)) {
return true;
}
}
return false;
},
resetNotify() {
if (this.subtabs) {
if (Object.values(this.subtabs).some(subtab => subtab.prestigeNotify)) {
return true;
}
}
if (this.microtabs) {
if (Object.values(this.microtabs).some(microtab => microtab.prestigeNotify)) {
return true;
}
}
if (this.autoPrestige || this.passiveGeneration) {
return false;
}
if (this.type === "static") {
return this.canReset;
}
if (this.type === "normal") {
return this.canReset && this.resetGain.gte(player[this.layer].points.div(10));
}
return false;
},
reset(force = false) {
console.warn("Not yet implemented!", force);
},
resetData(keep = []) {
console.warn("Not yet implemented!", keep);
}
};
const gridProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables' ]; const gridProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables' ];
const featureProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables', 'milestones', 'bars', const featureProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables', 'milestones', 'bars',
'infoboxes', 'grids', 'hotkeys', 'subtabs' ]; 'infoboxes', 'grids', 'hotkeys', 'subtabs' ];

View file

@ -20,7 +20,7 @@ const playerHandler = {
return true; return true;
} }
if (typeof target[key] == "undefined") { if (target[key] == undefined) {
return; return;
} }
@ -95,7 +95,7 @@ function getHandler(prefix) {
return true; return true;
} }
if (typeof target[key] == "undefined") { if (target[key] == undefined) {
return; return;
} }

View file

@ -1,13 +1,14 @@
import Decimal from './bignum'; import Decimal from './bignum';
import { isPlainObject } from './common'; import { isPlainObject } from './common';
import { layers } from '../store/layers'; import { layers } from '../store/layers';
import { player } from '../store/proxies';
export function resetLayer(layer, force = false) { export function resetLayer(layer, force = false) {
layers[layer].reset(force); layers[layer].reset(force);
} }
export function resetLayerData(layer, keep = []) { export function hardReset(layer, keep = []) {
layers[layer].resetData(keep); layers[layer].hardReset(keep);
} }
export function cache(func) { export function cache(func) {
@ -46,3 +47,248 @@ export function getStartingChallenges(layer) {
return acc; return acc;
}, {}); }, {});
} }
export function resetLayerData(layer, keep = []) {
keep.push('unlocked', 'forceTooltip', 'noRespecConfirm');
const keptData = keep.reduce((acc, curr) => {
acc[curr] = player[layer][curr];
return acc;
}, {});
player.upgrades = [];
player.achievements = [];
player.milestones = [];
player.infoboxes = {};
player[layer].buyables = getStartingBuyables(layers[layer]);
player[layer].clickables = getStartingClickables(layers[layer]);
player[layer].challenges = getStartingChallenges(layers[layer]);
Object.assign(player[layer], layers[layer].startData?.());
for (let item in keptData) {
player[layer][item] = keptData[item];
}
}
export function resetRow(row, ignore) {
Object.values(layers).filter(layer => layer.row === row && layer.layer !== ignore).forEach(layer => layer.hardReset());
}
export const defaultLayerProperties = {
type: "none",
shown: true,
layerShown: true,
glowColor: "red",
minWidth: 640,
displayRow() {
return this.row;
},
symbol() {
return this.id;
},
unlocked() {
if (player[this.id].unlocked) {
return true;
}
if (this.type !== "none" && this.canReset && this.layerShown) {
return true;
}
return false;
},
trueGlowColor() {
if (this.subtabs) {
for (let subtab of Object.values(this.subtabs)) {
if (subtab.notify) {
return subtab.glowColor || "red";
}
}
}
if (this.microtabs) {
for (let microtab of Object.values(this.microtabs)) {
if (microtab.notify) {
return microtab.glowColor || "red";
}
}
}
return this.glowColor || "red";
},
resetGain() {
if (this.type === "none" || this.type === "custom") {
return new Decimal(0);
}
if (this.gainExp?.eq(0)) {
return new Decimal(0);
}
if (this.baseAmount.lt(this.requires)) {
return new Decimal(0);
}
if (this.type === "static") {
if (!this.canBuyMax) {
return new Decimal(1);
}
let gain = this.baseAmount.div(this.requires).div(this.gainMult || 1).max(1).log(this.base)
.times(this.gainExp || 1).pow(Decimal.pow(this.exponent || 1, -1));
gain = gain.times(this.directMult || 1);
return gain.floor().sub(player[this.layer].points).add(1).max(1);
}
if (this.type === "normal") {
let gain = this.baseAmount.div(this.requires).pow(this.exponent || 1).times(this.gainMult || 1)
.pow(this.gainExp || 1);
if (this.softcap && gain.gte(this.softcap)) {
gain = gain.pow(this.softcapPower).times(this.softcap.pow(Decimal.sub(1, this.softcapPower)));
}
gain = gain.times(this.directMult || 1);
return gain.floor().max(0);
}
// Unknown prestige type
return new Decimal(0);
},
nextAt() {
if (this.type === "none" || this.type === "custom") {
return new Decimal(Infinity);
}
if (this.gainMult?.lte(0) || this.gainExp?.lte(0)) {
return new Decimal(Infinity);
}
if (this.type === "static") {
const amount = player[this.layer].points.div(this.directMult || 1);
const extraCost = Decimal.pow(this.base, amount.pow(this.exponent || 1).div(this.gainExp || 1))
.times(this.gainMult || 1);
let cost = extraCost.times(this.requires).max(this.requires);
if (this.roundUpCost) {
cost = cost.ceil();
}
return cost;
}
if (this.type === "normal") {
let next = this.resetGain.add(1).div(this.directMult || 1);
if (this.softcap && next.gte(this.softcap)) {
next = next.div(this.softcap.pow(Decimal.sub(1, this.softcapPower)))
.pow(Decimal.div(1, this.softcapPower));
}
next = next.root(this.gainExp || 1).div(this.gainMult || 1).root(this.exponent || 1)
.times(this.requires).max(this.requires);
if (this.roundUpCost) {
next = next.ceil();
}
return next;
}
// Unknown prestige type
return new Decimal(0);
},
nextAtMax() {
if (!this.canBuyMax || this.type !== "static") {
return this.nextAt;
}
const amount = player[this.layer].points.plus(this.resetGain).div(this.directMult || 1);
const extraCost = Decimal.pow(this.base, amount.pow(this.exponent || 1).div(this.gainExp || 1))
.times(this.gainMult || 1);
let cost = extraCost.times(this.requires).max(this.requires);
if (this.roundUpCost) {
cost = cost.ceil();
}
return cost;
},
canReset() {
if (this.type === "normal") {
return this.baseAmount.gte(this.requires);
}
if (this.type === "static") {
return this.baseAmount.gte(this.nextAt);
}
return false;
},
notify() {
if (this.upgrades) {
if (Object.values(this.upgrades).some(upgrade => upgrade.canAfford && !upgrade.bought && upgrade.unlocked)) {
return true;
}
}
if (this.activeChallenge?.canComplete) {
return true;
}
if (this.subtabs) {
if (Object.values(this.subtabs).some(subtab => subtab.notify)) {
return true;
}
}
if (this.microtabs) {
if (Object.values(this.microtabs).some(subtab => subtab.notify)) {
return true;
}
}
return false;
},
resetNotify() {
if (this.subtabs) {
if (Object.values(this.subtabs).some(subtab => subtab.prestigeNotify)) {
return true;
}
}
if (this.microtabs) {
if (Object.values(this.microtabs).some(microtab => microtab.prestigeNotify)) {
return true;
}
}
if (this.autoPrestige || this.passiveGeneration) {
return false;
}
if (this.type === "static") {
return this.canReset;
}
if (this.type === "normal") {
return this.canReset && this.resetGain.gte(player[this.layer].points.div(10));
}
return false;
},
reset(force = false) {
if (this.type === 'none') {
return;
}
if (!force) {
if (!this.canReset) {
return;
}
this.onPrestige?.(this.resetGain);
if (player[this.layer].points != undefined) {
player[this.layer].points = player[this.layer].points.add(this.resetGain);
}
if (!player[this.layer].unlocked) {
player[this.layer].unlocked = true;
if (this.increaseUnlockOrder) {
for (let layer in this.increaseUnlockOrder) {
player[layer].unlockOrder = (player[layer].unlockOrder || 0) + 1;
}
}
}
}
if (this.resetsNothing) {
return;
}
Object.values(layers).forEach(layer => {
if (this.row >= layer.row && (!force || this !== layer)) {
this.activeChallenge?.toggle();
}
});
player.points = new Decimal(0);
for (let row = this.row; row >= 0; row--) {
resetRow(row, this.layer);
}
resetRow('side', this.layer);
if (player[this.layer].resetTime != undefined) {
player[this.layer].resetTime = 0;
}
},
hardReset(keep = []) {
if (!isNaN(this.row)) {
resetLayerData(this.layer, keep);
}
}
};