diff --git a/src/components/features/Buyable.vue b/src/components/features/Buyable.vue index 1615ddb..e25f289 100644 --- a/src/components/features/Buyable.vue +++ b/src/components/features/Buyable.vue @@ -2,7 +2,7 @@ <div v-if="buyable.unlocked" style="display: grid"> <button :style="style" @click="buyable.buy" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start" :class="{ feature: true, [layer || tab.layer]: true, buyable: true, can: buyable.canBuy, locked: !buyable.canAfford, bought }" - @touchend="stop" @touchcancel="stop"> + @touchend="stop" @touchcancel="stop" :disabled="!buyable.canBuy"> <div v-if="title"> <component :is="title" /> </div> diff --git a/src/components/features/Clickable.vue b/src/components/features/Clickable.vue index 315643f..cedb6f9 100644 --- a/src/components/features/Clickable.vue +++ b/src/components/features/Clickable.vue @@ -2,7 +2,7 @@ <div v-if="clickable.unlocked"> <button :class="{ feature: true, [layer || tab.layer]: true, can: clickable.canClick, locked: !clickable.canClick }" :style="style" @click="clickable.click" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start" - @touchend="stop" @touchcancel="stop"> + @touchend="stop" @touchcancel="stop" :disabled="!clickable.canClick"> <div v-if="titleDisplay"> <component :is="titleDisplay" /> </div> diff --git a/src/components/features/Gridable.vue b/src/components/features/Gridable.vue index 517d7b9..c0f7902 100644 --- a/src/components/features/Gridable.vue +++ b/src/components/features/Gridable.vue @@ -1,7 +1,7 @@ <template> - <button v-if="gridable.unlocked" :class="{ feature: true, tile: true, can: gridable.canClick, locked: !gridable.canClick}" - :style="style" @click="gridable.click" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start" - @touchend="stop" @touchcancel="stop"> + <button v-if="gridable.unlocked" :class="{ feature: true, tile: true, can: canClick, locked: !canClick}" + :style="style" @click="gridable.click" @mousedown="start" @mouseleave="stop" @mouseup="stop" @touchstart="start" + @touchend="stop" @touchcancel="stop" :disabled="!canClick"> <div v-if="title"><component :is="title" /></div> <component :is="display" style="white-space: pre-line;" /> <branch-node :branches="gridable.branches" :id="id" featureType="gridable" /> @@ -33,9 +33,12 @@ export default { gridable() { return layers[this.layer || this.tab.layer].grids[this.id][this.cell]; }, + canClick() { + return this.gridable.canClick; + }, style() { return [ - this.gridable.canClick ? { 'background-color': layers[this.layer || this.tab.layer].color } : {}, + this.canClick ? { 'background-color': layers[this.layer || this.tab.layer].color } : {}, layers[this.layer || this.tab.layer].componentStyles?.gridable, this.gridable.style ]; diff --git a/src/components/features/Upgrade.vue b/src/components/features/Upgrade.vue index 3cd1260..3d46488 100644 --- a/src/components/features/Upgrade.vue +++ b/src/components/features/Upgrade.vue @@ -7,7 +7,7 @@ can: upgrade.canAfford && !upgrade.bought, locked: !upgrade.canAfford && !upgrade.bought, bought: upgrade.bought - }"> + }" :disabled="!upgrade.canAfford && !upgrade.bought"> <component v-if="fullDisplay" :is="fullDisplay" /> <default-upgrade-display v-else :id="id" /> <branch-node :branches="upgrade.branches" :id="id" featureType="upgrade" /> diff --git a/src/components/tree/BranchNode.vue b/src/components/tree/BranchNode.vue index f7c4a48..0c4fe17 100644 --- a/src/components/tree/BranchNode.vue +++ b/src/components/tree/BranchNode.vue @@ -55,7 +55,7 @@ export default { if (typeof branch === 'string') { return branch.includes('@') ? branch : `${this.featureType}@${branch}`; } - if (!branch.target.includes('@')) { + if (!branch.target?.includes('@')) { return { ...branch, target: `${branch.featureType || this.featureType}@${branch.target}` }; } return branch; diff --git a/src/components/tree/TreeNode.vue b/src/components/tree/TreeNode.vue index cd9be89..0ced67f 100644 --- a/src/components/tree/TreeNode.vue +++ b/src/components/tree/TreeNode.vue @@ -12,7 +12,7 @@ small }"> <LayerProvider :index="tab.index" :layer="id"> - <button v-if="layer.shown" @click="clickTab" :style="style"> + <button v-if="layer.shown" @click="clickTab" :style="style" :disabled="!unlocked"> <component :is="display" /> <branch-node :branches="layer.branches" :id="id" featureType="tree-node" /> </button> diff --git a/src/store/layers.js b/src/store/layers.js index 22a83ef..3758181 100644 --- a/src/store/layers.js +++ b/src/store/layers.js @@ -1,16 +1,19 @@ import Vue from 'vue'; import clone from 'lodash.clonedeep'; import { isFunction, isPlainObject } from '../util/common'; -import { createProxy, createGridProxy, player } from './proxies'; +import { createProxy, createGridProxy, player as playerProxy } from './proxies'; import Decimal from '../util/bignum'; import store from './index'; import { noCache, getStartingBuyables, getStartingClickables, getStartingChallenges, defaultLayerProperties } from '../util/layers'; +import { applyPlayerData } from '../util/save'; export const layers = {}; export const hotkeys = []; window.layers = layers; -export function addLayer(layer) { +export function addLayer(layer, player = null) { + player = player || playerProxy; + // Check for required properties if (!('id' in layer)) { console.error(`Cannot add layer without a "id" property!`, layer); @@ -27,6 +30,18 @@ export function addLayer(layer) { // Clone object to prevent modifying the original layer = clone(layer); + player[layer.id] = applyPlayerData({ + upgrades: [], + achievements: [], + milestones: [], + infoboxes: {}, + buyables: getStartingBuyables(layer), + clickables: getStartingClickables(layer), + challenges: getStartingChallenges(layer), + grids: {}, + ...layer.startData?.() + }, player[layer.id]); + // Set default property values layer = Object.assign({}, defaultLayerProperties, layer); layer.layer = layer.id; @@ -53,9 +68,6 @@ export function addLayer(layer) { } } if (layer.upgrades) { - if (player[layer.id].upgrades == undefined) { - player[layer.id].upgrades = []; - } for (let id in layer.upgrades) { if (isPlainObject(layer.upgrades[id])) { layer.upgrades[id].bought = function() { @@ -128,9 +140,6 @@ export function addLayer(layer) { } } if (layer.achievements) { - if (player[layer.id].achievements == undefined) { - player[layer.id].achievements = []; - } for (let id in layer.achievements) { if (isPlainObject(layer.achievements[id])) { layer.achievements[id].earned = function() { @@ -140,9 +149,6 @@ export function addLayer(layer) { } } if (layer.challenges) { - if (player[layer.id].challenges == undefined) { - player[layer.id].challenges = getStartingChallenges(layer); - } for (let id in layer.challenges) { if (isPlainObject(layer.challenges[id])) { if (layer.challenges[id].onComplete != undefined) { @@ -229,9 +235,6 @@ export function addLayer(layer) { } } if (layer.buyables) { - if (player[layer.id].buyables == undefined) { - player[layer.id].buyables = getStartingBuyables(layer); - } if (layer.buyables.reset == undefined) { layer.buyables.reset = noCache(function() { player[this.layer].buyables = getStartingBuyables(layer); @@ -272,9 +275,6 @@ export function addLayer(layer) { } } if (layer.clickables) { - if (player[layer.id].clickables == undefined) { - player[layer.id].clickables = getStartingClickables(layer); - } for (let id in layer.clickables) { if (isPlainObject(layer.clickables[id])) { layer.clickables[id].state = function() { @@ -287,9 +287,6 @@ export function addLayer(layer) { } } if (layer.milestones) { - if (player[layer.id].milestones == undefined) { - player[layer.id].milestones = []; - } for (let id in layer.milestones) { if (isPlainObject(layer.milestones[id])) { layer.milestones[id].shown = function() { @@ -319,10 +316,10 @@ export function addLayer(layer) { } } if (layer.grids) { - if (player[layer.id].grids == undefined) { - player[layer.id].grids = {}; - } for (let id in layer.grids) { + if (player[layer.id].grids[id] == undefined) { + player[layer.id].grids[id] = {}; + } if (isPlainObject(layer.grids[id])) { if (player[layer.id].grids[id] == undefined) { player[layer.id].grids[id] = {}; diff --git a/src/util/save.js b/src/util/save.js index 97f29ea..6de7618 100644 --- a/src/util/save.js +++ b/src/util/save.js @@ -1,6 +1,5 @@ import modInfo from '../data/modInfo'; import { getStartingData, getInitialLayers, fixOldSave } from '../data/mod'; -import { getStartingBuyables, getStartingClickables, getStartingChallenges } from './layers'; import { player } from '../store/proxies'; import Decimal from './bignum'; @@ -11,7 +10,7 @@ export const IMPORTING_WRONG_ID = "WRONG_ID"; export const IMPORTING_FORCE = "FORCE"; export function getInitialStore(playerData = {}) { - playerData = applyPlayerData({ + return applyPlayerData({ id: `${modInfo.id}-0`, name: "Default Save", tabs: modInfo.initialTabs.slice(), @@ -40,22 +39,6 @@ export function getInitialStore(playerData = {}) { saveToImport: "", saveToExport: "" }, playerData); - - Object.assign(playerData, getInitialLayers(playerData).reduce((acc, layer) => { - acc[layer.id] = applyPlayerData({ - upgrades: [], - achievements: [], - milestones: [], - infoboxes: {}, - buyables: getStartingBuyables(layer), - clickables: getStartingClickables(layer), - challenges: getStartingChallenges(layer), - ...layer.startData?.() - }, playerData[layer.id]); - return acc; - }, {})); - - return playerData; } export function save() { @@ -121,7 +104,7 @@ export async function loadSave(playerData) { for (let layer in layers) { removeLayer(layer); } - getInitialLayers(playerData).forEach(addLayer); + getInitialLayers(playerData).forEach(layer => addLayer(layer, playerData)); playerData = getInitialStore(playerData); if (playerData.offlineProd) { @@ -136,24 +119,31 @@ export async function loadSave(playerData) { Object.assign(player, playerData); for (let prop in player) { - if (!(prop in playerData)) { + if (!(prop in playerData) && !(prop in layers)) { delete player[prop]; } } } -function applyPlayerData(target, source) { +export function applyPlayerData(target, source, destructive = false) { for (let prop in source) { if (target[prop] == null) { target[prop] = source[prop]; } else if (target[prop] instanceof Decimal) { target[prop] = new Decimal(source[prop]); } else if (Array.isArray(target[prop]) || typeof target[prop] === 'object') { - target[prop] = applyPlayerData(target[prop], source[prop]); + target[prop] = applyPlayerData(target[prop], source[prop], destructive); } else { target[prop] = source[prop]; } } + if (destructive) { + for (let prop in target) { + if (!(prop in source)) { + delete target[prop]; + } + } + } return target; }