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"
:class="{ 'buyable-button': true, can: buyable.unlocked, locked: !buyable.unlocked, feature: true }"
:style="{ 'background-color': buyable.canSellOne ? layerColor : '' }">
Sell All
Sell One
</button>
</div>
</div>

View file

@ -1,8 +1,15 @@
<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>
<button :class="{ [layer || tab.layer]: true, longUpg: true, can: true }" :style="{ backgroundColor: buttonColor }"
@click="toggle">
<button :style="{ backgroundColor: challenge.maxed ? null : buttonColor }" @click="toggle">
{{ buttonText }}
</button>
<component v-if="fullDisplay" :is="fullDisplay" />
@ -32,15 +39,6 @@ export default {
shown() {
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() {
return [
layers[this.layer || this.tab.layer].componentStyles?.challenge,
@ -48,8 +46,11 @@ export default {
];
},
title() {
if (this.challenge.title) {
return coerceComponent(this.challenge.titleDisplay, 'h3');
if (this.challenge.titleDisplay) {
return coerceComponent(this.challenge.titleDisplay, 'div');
}
if (this.challenge.name) {
return coerceComponent(this.challenge.name, 'h3');
}
return null;
},
@ -82,6 +83,30 @@ export default {
<style scoped>
.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>

View file

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

View file

@ -58,7 +58,13 @@ export default {
return `${prefix} ${baseAmount} / ${nextAt} ${baseResource}`;
} 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 "";

View file

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

View file

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

View file

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

View file

@ -1,7 +1,9 @@
<template>
<div class="field">
<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>
</template>
@ -9,7 +11,7 @@
import './fields.css';
export default {
name: 'TextField',
name: 'Slider',
props: {
title: String,
value: Number,
@ -20,4 +22,11 @@ export default {
</script>
<style scoped>
input {
margin-right: 0;
}
.value {
margin-left: 10px;
}
</style>

View file

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

View file

@ -1,12 +1,21 @@
<template>
<div v-if="microtabs" class="microtabs">
<LayerProvider :layer="layer || tab.layer" :index="tab.index">
<component :is="display" />
<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>
<script>
import { layers } from '../../store/layers';
import { player } from '../../store/proxies';
import { coerceComponent } from '../../util/vue';
import themes from '../../data/themes';
export default {
name: 'microtab',
@ -17,13 +26,50 @@ export default {
id: String
},
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() {
const family = layers[this.layer || this.tab.layer].microtabs[this.family];
return coerceComponent((this.id !== undefined ? family[this.id] : family.activeMicrotab).display);
return coerceComponent(this.activeMicrotab.display);
}
},
methods: {
selectMicrotab(tab) {
player.subtabs[this.layer || this.tab.layer][this.family] = tab;
}
}
};
</script>
<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>

View file

@ -7,7 +7,7 @@
<LayerProvider :layer="tab" :index="index" v-if="tab in components && components[tab]">
<component :is="components[tab]" />
</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]" />
<component :is="tab" :index="index" v-else />
</div>

View file

@ -8,7 +8,7 @@
</span>
<modal :show="showModal" @close="closeModal">
<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>
</div>
</template>

View file

@ -73,7 +73,7 @@ export default {
done() {return player[this.layer].best.gte(4)},
effectDisplay: "You can toggle beep and boop (which do nothing)",
optionsDisplay: `
<div>
<div style="display: flex; justify-content: center">
<Toggle :value="player.c.beep" @change="value => player.c.beep = value" />
<Toggle :value="player.f.boop" @change="value => player.f.boop = value" />
</div>
@ -160,21 +160,23 @@ export default {
},
buyables: {
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
this.reset;
this.reset();
resetLayer(this.layer, true) // Force a reset
},
respecText: "Respec Thingies", // Text on Respec button, optional
respecMessage: "Are you sure? Respeccing these doesn't accomplish much.",
11: {
title: "Exhancers", // Optional, displayed at the top in a larger font
cost(x) { // cost for buying xth buyable, can be an object if there are multiple currencies
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)
let cost = Decimal.pow(2, x.pow(1.5))
return cost.floor()
},
effect(x) { // Effects of owning x of the items, x is a decimal
effect() { // Effects of owning x of the items, x is a decimal
let x = this.amount;
let eff = {}
if (x.gte(0)) eff.first = Decimal.pow(25, x.pow(1.1))
else eff.first = Decimal.pow(1/25, x.times(-1).pow(1.1))
@ -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.
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() {
}, // Do any automation inherent to this layer if appropriate
resetsNothing() {return false},
@ -229,15 +230,13 @@ export default {
microtabs: {
stuff: {
first: {
content: ["upgrades", ["display-text", function() {return "confirmed"}]]
display: `<div v-frag>
<upgrades />
<div>confirmed</div>
</div>`
},
second: {
embedLayer: "f",
content: [["upgrade", 11],
["row", [["upgrade", 11], "blank", "blank", ["upgrade", 11],]],
["display-text", function() {return "double confirmed"}]]
embedLayer: "f"
},
},
otherStuff: {
@ -377,7 +376,7 @@ export default {
<div v-frag>
<h1> C O N F I R M E D </h1>
<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>
<Slider :value="player.c.otherThingy" @change="value => player.c.otherThingy = value" :min="1" :max="30" />
</div>

View file

@ -33,7 +33,6 @@ export default {
//directMult() {return new Decimal(player.c.otherThingy)},
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.
tooltipLocked() { // Optional, tooltip displays when the layer is locked

View file

@ -13,7 +13,7 @@ const g = {
symbol: "TH",
branches: ["c"],
color: '#6d3678',
layerShown: true,
shown: true,
canClick() {return player.points.gte(10)},
tooltip: "Thanos your points",
onClick() {
@ -23,8 +23,7 @@ const g = {
};
const h = {
id: "h",
branches: ["g", { target: 'flatBoi', featureType: 'bar', endOffset: { x: () => -50 + 100 * layers.c.bars.flatBoi.progress.toNumber() } }],
layerShown: true,
branches: ["g", () => ({ target: 'flatBoi', featureType: 'bar', endOffset: { x: -50 + 100 * layers.c.bars.flatBoi.progress.toNumber() } })],
tooltip() {return "Restore your points to " + player.c.otherThingy},
row: "side",
canClick() {return player.points.lt(player.c.otherThingy)},
@ -49,6 +48,7 @@ const main = {
<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)
</div>
<spacer />
<tree :append="true" />
</div>`,
name: "Tree"

View file

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

View file

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

View file

@ -2,7 +2,7 @@ import { isFunction, isPlainObject } from '../util/common';
import { createProxy, createGridProxy, player } from './proxies';
import Decimal from '../util/bignum';
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 hotkeys = [];
@ -28,19 +28,15 @@ export function addLayer(layer) {
// Set default property values
layer = Object.assign({}, defaultLayerProperties, layer);
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 = {};
// Process each feature
for (let property of uncachedProperties) {
if (layer[property]) {
layer[property].forceCached = false;
}
}
for (let property of gridProperties) {
if (layer[property]) {
setRowCol(layer[property]);
@ -144,8 +140,17 @@ export function addLayer(layer) {
}
for (let id in layer.challenges) {
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() {
return !layer.deactivated && !!player[layer.id].challenges[id];
return !layer.deactivated && player[layer.id].challenges[id]?.gt(0);
}
layer.challenges[id].completions = function() {
return player[layer.id].challenges[id];
@ -185,7 +190,7 @@ export function addLayer(layer) {
if (layer.challenges[id].completionLimit == undefined) {
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;
if (exiting) {
if (this.canComplete && !this.maxed) {
@ -199,18 +204,21 @@ export function addLayer(layer) {
}
player[layer.id].activeChallenge = null;
this.onExit?.();
resetLayer(layer.id, true);
layer.reset(true);
} else if (!exiting && this.canStart) {
resetLayer(layer.id, true);
layer.reset(true);
player[layer.id].activeChallenge = id;
this.onEnter?.();
}
}
});
if (layer.challenges[id].canStart == undefined) {
layer.challenges[id].canStart = true;
}
}
}
layer.activeChallenge = function() {
return Object.values(this.challenges).find(challenge => challenge.active);
}
}
if (layer.buyables) {
if (player[layer.id].buyables == undefined) {
@ -220,6 +228,8 @@ export function addLayer(layer) {
layer.buyables.reset = noCache(function() {
player[this.layer].buyables = getStartingBuyables(layer);
});
} else {
layer.buyables.reset.forceCached = false;
}
for (let id in layer.buyables) {
if (isPlainObject(layer.buyables[id])) {
@ -366,11 +376,18 @@ export function addLayer(layer) {
}
}
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]];
}
// 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);
}
export const defaultLayerProperties = {
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 uncachedProperties = [ 'startData', 'onClick', 'update', 'reset', 'hardReset' ];
const gridProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables' ];
const featureProperties = [ 'upgrades', 'achievements', 'challenges', 'buyables', 'clickables', 'milestones', 'bars',
'infoboxes', 'grids', 'hotkeys', 'subtabs' ];

View file

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

View file

@ -1,13 +1,14 @@
import Decimal from './bignum';
import { isPlainObject } from './common';
import { layers } from '../store/layers';
import { player } from '../store/proxies';
export function resetLayer(layer, force = false) {
layers[layer].reset(force);
}
export function resetLayerData(layer, keep = []) {
layers[layer].resetData(keep);
export function hardReset(layer, keep = []) {
layers[layer].hardReset(keep);
}
export function cache(func) {
@ -46,3 +47,248 @@ export function getStartingChallenges(layer) {
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);
}
}
};