diff --git a/package-lock.json b/package-lock.json
index 385729e..6011771 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,11 +9,13 @@
"version": "0.1.0",
"dependencies": {
"core-js": "^3.6.5",
+ "lodash.clonedeep": "^4.5.0",
"portal-vue": "^2.1.7",
"vue": "^2.6.11",
"vue-frag": "^1.1.5",
"vue-reactive-provide": "^0.3.0",
"vue-select": "^3.11.2",
+ "vue-sortable": "github:Netbel/vue-sortable#master-fix",
"vue-textarea-autosize": "^1.1.1",
"vue-transition-expand": "^0.1.0",
"vue2-perfect-scrollbar": "^1.5.0",
@@ -28,6 +30,7 @@
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
+ "raw-loader": "^4.0.2",
"vue-template-compiler": "^2.6.11"
}
},
@@ -8429,6 +8432,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+ },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -10812,6 +10820,58 @@
"node": ">= 0.8"
}
},
+ "node_modules/raw-loader": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+ "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/raw-loader/node_modules/loader-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+ "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/raw-loader/node_modules/schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -11853,6 +11913,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/sortablejs": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.13.0.tgz",
+ "integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg=="
+ },
"node_modules/source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -12015,11 +12080,6 @@
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
- "bin": {
- "sshpk-conv": "bin/sshpk-conv",
- "sshpk-sign": "bin/sshpk-sign",
- "sshpk-verify": "bin/sshpk-verify"
- },
"engines": {
"node": ">=0.10.0"
}
@@ -13506,6 +13566,14 @@
"vue": "2.x"
}
},
+ "node_modules/vue-sortable": {
+ "version": "0.1.3",
+ "resolved": "git+ssh://git@github.com/Netbel/vue-sortable.git#f4d4870ace71ea59bd79252eb2ec1cf6bfb02fe7",
+ "license": "MIT",
+ "dependencies": {
+ "sortablejs": "^1.4.2"
+ }
+ },
"node_modules/vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
@@ -21535,6 +21603,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
},
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
+ },
"lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -23516,6 +23589,40 @@
"unpipe": "1.0.0"
}
},
+ "raw-loader": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+ "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "dependencies": {
+ "loader-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+ "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
+ "schema-utils": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
+ "integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.6",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
+ }
+ },
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -24397,6 +24504,11 @@
}
}
},
+ "sortablejs": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.13.0.tgz",
+ "integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg=="
+ },
"source-list-map": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
@@ -25730,6 +25842,13 @@
"integrity": "sha512-pIOcY8ajWNSwg8Ns4eHVr5ZWwqKCSZeQRymTnlUI8i+3QiQXF6JIM4lylK6mVfbccs4S6vOyxB7zmJBpp7tDUg==",
"requires": {}
},
+ "vue-sortable": {
+ "version": "git+ssh://git@github.com/Netbel/vue-sortable.git#f4d4870ace71ea59bd79252eb2ec1cf6bfb02fe7",
+ "from": "vue-sortable@github:Netbel/vue-sortable#master-fix",
+ "requires": {
+ "sortablejs": "^1.4.2"
+ }
+ },
"vue-style-loader": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
diff --git a/package.json b/package.json
index aa9e473..b2116d7 100644
--- a/package.json
+++ b/package.json
@@ -9,11 +9,13 @@
},
"dependencies": {
"core-js": "^3.6.5",
+ "lodash.clonedeep": "^4.5.0",
"portal-vue": "^2.1.7",
"vue": "^2.6.11",
"vue-frag": "^1.1.5",
"vue-reactive-provide": "^0.3.0",
"vue-select": "^3.11.2",
+ "vue-sortable": "github:Netbel/vue-sortable#master-fix",
"vue-textarea-autosize": "^1.1.1",
"vue-transition-expand": "^0.1.0",
"vue2-perfect-scrollbar": "^1.5.0",
@@ -28,6 +30,7 @@
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
+ "raw-loader": "^4.0.2",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
diff --git a/public/index.html b/public/index.html
index 7d1a7fa..5a9799b 100644
--- a/public/index.html
+++ b/public/index.html
@@ -6,6 +6,7 @@
+
<%= htmlWebpackPlugin.options.title %>
diff --git a/saves/.placehold b/saves/.placehold
new file mode 100644
index 0000000..e69de29
diff --git a/saves/safff.txt b/saves/safff.txt
new file mode 100644
index 0000000..7a1451d
--- /dev/null
+++ b/saves/safff.txt
@@ -0,0 +1 @@
+eyJpZCI6InRtdC14LTEwNSIsIm5hbWUiOiJEZWZhdWx0IFNhZmZmZiAtIHNvbWV0aGluZyBlbHNlIiwidGFicyI6WyJtYWluIiwiYyJdLCJ0aW1lIjoxNjI0MjQ1MjYxMDg3LCJhdXRvc2F2ZSI6dHJ1ZSwib2ZmbGluZVByb2QiOnRydWUsInRpbWVQbGF5ZWQiOiIzNDQ4LjYxNTc4MTcwOTAxIiwia2VlcEdvaW5nIjpmYWxzZSwibGFzdFRlblRpY2tzIjpbMC4wNTEsMC4wNSwwLjA0OSwwLjA1LDAuMDUsMC4wNTEsMC4wNDksMC4wNSwwLjA1LDAuMDUxXSwic2hvd1RQUyI6dHJ1ZSwibXNEaXNwbGF5IjoiYWxsIiwiaGlkZUNoYWxsZW5nZXMiOmZhbHNlLCJ0aGVtZSI6InBhcGVyIiwic3VidGFicyI6e30sIm1pbmltaXplZCI6e30sIm1vZElEIjoidG10LXgiLCJtb2RWZXJzaW9uIjoiMC4wIiwicG9pbnRzIjoiMzMwMC4zNzc3NzM4NTkwNTUiLCJtYWluIjp7InVwZ3JhZGVzIjpbXSwiYWNoaWV2ZW1lbnRzIjpbXSwibWlsZXN0b25lcyI6W10sImluZm9ib3hlcyI6e319LCJmIjp7InVwZ3JhZGVzIjpbXSwiYWNoaWV2ZW1lbnRzIjpbXSwibWlsZXN0b25lcyI6W10sImluZm9ib3hlcyI6e30sImNsaWNrYWJsZXMiOnsiMTEiOiJTdGFydCJ9LCJ1bmxvY2tlZCI6ZmFsc2UsInBvaW50cyI6IjAiLCJib29wIjpmYWxzZX0sImMiOnsidXBncmFkZXMiOlsiMTEiXSwiYWNoaWV2ZW1lbnRzIjpbXSwibWlsZXN0b25lcyI6W10sImluZm9ib3hlcyI6e30sImJ1eWFibGVzIjp7IjExIjoiMCJ9LCJjaGFsbGVuZ2VzIjp7IjExIjoiMCJ9LCJ1bmxvY2tlZCI6dHJ1ZSwicG9pbnRzIjoiMCIsImJlc3QiOiIxIiwidG90YWwiOiIwIiwiYmVlcCI6ZmFsc2UsInRoaW5neSI6InBvaW50eSIsIm90aGVyVGhpbmd5IjoxMCwic3BlbnRPbkJ1eWFibGVzIjoiMCJ9LCJhIjp7InVwZ3JhZGVzIjpbXSwiYWNoaWV2ZW1lbnRzIjpbIjExIl0sIm1pbGVzdG9uZXMiOltdLCJpbmZvYm94ZXMiOnt9LCJ1bmxvY2tlZCI6dHJ1ZSwicG9pbnRzIjoiMCJ9LCJnIjp7InVwZ3JhZGVzIjpbXSwiYWNoaWV2ZW1lbnRzIjpbXSwibWlsZXN0b25lcyI6W10sImluZm9ib3hlcyI6e319LCJoIjp7InVwZ3JhZGVzIjpbXSwiYWNoaWV2ZW1lbnRzIjpbXSwibWlsZXN0b25lcyI6W10sImluZm9ib3hlcyI6e319LCJzcG9vayI6eyJ1cGdyYWRlcyI6W10sImFjaGlldmVtZW50cyI6W10sIm1pbGVzdG9uZXMiOltdLCJpbmZvYm94ZXMiOnt9fSwib29tcHNNYWciOjAsImxhc3RQb2ludHMiOiIzMzAwLjM3Nzc3Mzg1OTA1NSJ9
\ No newline at end of file
diff --git a/src/components/features/Challenge.vue b/src/components/features/Challenge.vue
index 758ef3e..87a334c 100644
--- a/src/components/features/Challenge.vue
+++ b/src/components/features/Challenge.vue
@@ -1,5 +1,5 @@
-
+
Are you sure?
+
+
+
+
+
+
+
+
diff --git a/src/components/fields/FeedbackButton.vue b/src/components/fields/FeedbackButton.vue
new file mode 100644
index 0000000..5e530c2
--- /dev/null
+++ b/src/components/fields/FeedbackButton.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
diff --git a/src/components/fields/Select.vue b/src/components/fields/Select.vue
index a62d77e..2f5ae53 100644
--- a/src/components/fields/Select.vue
+++ b/src/components/fields/Select.vue
@@ -30,20 +30,14 @@ export default {
};
-
-
diff --git a/src/components/fields/Slider.vue b/src/components/fields/Slider.vue
index 91060e3..5eceda1 100644
--- a/src/components/fields/Slider.vue
+++ b/src/components/fields/Slider.vue
@@ -22,11 +22,4 @@ export default {
diff --git a/src/components/fields/Text.vue b/src/components/fields/Text.vue
index 72443b3..5e66020 100644
--- a/src/components/fields/Text.vue
+++ b/src/components/fields/Text.vue
@@ -1,9 +1,13 @@
-
- {{ title }}
- $emit('change', value)" />
- $emit('change', e.target.value)" :placeholder="placeholder" />
-
+
diff --git a/src/components/fields/fields.css b/src/components/fields/fields.css
index 7eab1fa..d3164ca 100644
--- a/src/components/fields/fields.css
+++ b/src/components/fields/fields.css
@@ -5,4 +5,9 @@
margin: 10px 0;
user-select: none;
justify-content: space-between;
+ align-items: center;
+}
+
+.field > * {
+ margin: 0;
}
diff --git a/src/components/index.js b/src/components/index.js
index b640729..78f08e0 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -30,6 +30,8 @@ import RespecButton from './features/RespecButton';
import Upgrade from './features/Upgrade';
import Upgrades from './features/Upgrades';
/* fields */
+import DangerButton from './fields/DangerButton';
+import FeedbackButton from './fields/FeedbackButton';
import Select from './fields/Select';
import Slider from './fields/Slider';
import Text from './fields/Text';
@@ -48,6 +50,8 @@ import Nav from './system/Nav';
import Options from './system/Options';
import Resource from './system/Resource';
import Row from './system/Row';
+import Save from './system/Save';
+import SavesManager from './system/SavesManager';
import Spacer from './system/Spacer';
import Sticky from './system/Sticky';
import Subtab from './system/Subtab';
@@ -70,6 +74,7 @@ import PerfectScrollbar from 'vue2-perfect-scrollbar';
import 'vue2-perfect-scrollbar/dist/vue2-perfect-scrollbar.css';
import VueTextareaAutosize from 'vue-textarea-autosize';
import PortalVue from 'portal-vue';
+import Sortable from 'vue-sortable';
/* features */
Vue.component(Achievement.name, Achievement);
@@ -98,6 +103,8 @@ Vue.component(RespecButton.name, RespecButton);
Vue.component(Upgrade.name, Upgrade);
Vue.component(Upgrades.name, Upgrades);
/* fields */
+Vue.component(DangerButton.name, DangerButton);
+Vue.component(FeedbackButton.name, FeedbackButton);
Vue.component(Select.name, Select);
Vue.component(Slider.name, Slider);
Vue.component(Text.name, Text);
@@ -116,6 +123,8 @@ Vue.component(Nav.name, Nav);
Vue.component(Options.name, Options);
Vue.component(Resource.name, Resource);
Vue.component(Row.name, Row);
+Vue.component(Save.name, Save);
+Vue.component(SavesManager.name, SavesManager);
Vue.component(Spacer.name, Spacer);
Vue.component(Sticky.name, Sticky);
Vue.component(Subtab.name, Subtab);
@@ -136,3 +145,4 @@ Vue.use(TransitionExpand);
Vue.use(PerfectScrollbar);
Vue.use(VueTextareaAutosize);
Vue.use(PortalVue);
+Vue.use(Sortable);
diff --git a/src/components/system/Nav.vue b/src/components/system/Nav.vue
index 6b043b9..983807b 100644
--- a/src/components/system/Nav.vue
+++ b/src/components/system/Nav.vue
@@ -17,6 +17,7 @@
i
+
@@ -32,10 +33,12 @@
i
+
v{{ version }}
+
@@ -54,6 +57,7 @@ export default {
discordLink: modInfo.discordLink,
version: modInfo.versionNumber,
showInfo: false,
+ showSaves: false,
showOptions: false,
showChangelog: false
}
@@ -119,6 +123,7 @@ export default {
width: 200px;
transition: right .25s ease;
background: var(--secondary-background);
+ z-index: 1;
}
.discord.overlay .discord-links {
diff --git a/src/components/system/Options.vue b/src/components/system/Options.vue
index bc51637..ca4e1ff 100644
--- a/src/components/system/Options.vue
+++ b/src/components/system/Options.vue
@@ -1,15 +1,9 @@
-
+
-
-
-
-
-
-
@@ -56,45 +50,13 @@ export default {
},
setMSDisplay(msDisplay) {
player.msDisplay = msDisplay;
- },
- save() {
- console.warn("Not yet implemented!");
- },
- hardReset() {
- console.warn("Not yet implemented!");
- },
- exportSave() {
- console.warn("Not yet implemented!");
- },
- importSave() {
- console.warn("Not yet implemented!");
}
}
};
diff --git a/src/components/system/Save.vue b/src/components/system/Save.vue
new file mode 100644
index 0000000..c539a85
--- /dev/null
+++ b/src/components/system/Save.vue
@@ -0,0 +1,171 @@
+
+
+
drag_handle
+
+
+ content_paste
+
+
+
+
+ delete
+
+
+
+
+
+
+
+
+
v{{ save.modVersion }}
+
Last played {{ dateFormat.format(time) }}
+
+
+
+
+
+ Error: Failed to load save with id {{ save.id }}
+
+
+
+
+
+
+
+
+
diff --git a/src/components/system/SavesManager.vue b/src/components/system/SavesManager.vue
new file mode 100644
index 0000000..8bc5bcd
--- /dev/null
+++ b/src/components/system/SavesManager.vue
@@ -0,0 +1,218 @@
+
+
+
+
Saves Manager
+
+
+ editSave(save.id, name)" @duplicate="duplicateSave(save.id)" @delete="deleteSave(save.id)" />
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/tree/Branches.vue b/src/components/tree/Branches.vue
index 9247c72..bac73d3 100644
--- a/src/components/tree/Branches.vue
+++ b/src/components/tree/Branches.vue
@@ -29,15 +29,9 @@ export default {
};
},
mounted() {
- this.$nextTick(() => {
- if (this.$refs.resizeListener == undefined) {
- this.mounted();
- } else {
- // ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
- this.resizeObserver.observe(this.$refs.resizeListener);
- this.updateNodes();
- }
- });
+ // ResizeListener exists because ResizeObserver's don't work when told to observe an SVG element
+ this.resizeObserver.observe(this.$refs.resizeListener);
+ this.updateNodes();
},
provide() {
return {
@@ -77,7 +71,7 @@ export default {
});
},
unregisterNode(id) {
- delete this.nodes[id];
+ Vue.delete(this.nodes, id);
},
registerBranch(start, options) {
const end = typeof options === 'string' ? options : options.target;
diff --git a/src/data/layers/aca/c.js b/src/data/layers/aca/c.js
index 68fd418..1a99884 100644
--- a/src/data/layers/aca/c.js
+++ b/src/data/layers/aca/c.js
@@ -311,7 +311,7 @@ export default {
Name your points!
-
player.c.thingy = value" />
+ player.c.thingy = value" :field="false" />
I have {{ format(player.points) }} {{ player.c.thingy }} points!
diff --git a/src/data/mod.js b/src/data/mod.js
index 15b0fe9..b412ecc 100644
--- a/src/data/mod.js
+++ b/src/data/mod.js
@@ -54,7 +54,7 @@ const main = {
name: "Tree"
};
-export const initialLayers = [ main, f, c, a, g, h, spook ];
+export const getInitialLayers = () => [ main, f, c, a, g, h, spook ];
export function getStartingData() {
return {
@@ -80,7 +80,7 @@ export function update(delta) {
}
/* eslint-disable-next-line no-unused-vars */
-export function fixOldSave(oldVersion) {
+export function fixOldSave(oldVersion, playerData) {
}
document.title = modInfo.title;
diff --git a/src/data/modInfo.json b/src/data/modInfo.json
index e006906..8a6d164 100644
--- a/src/data/modInfo.json
+++ b/src/data/modInfo.json
@@ -2,7 +2,7 @@
"title": "The Modding Tree X",
"id": "tmt-x",
"author": "thepaperpilot",
- "discordName": "TMT-X",
+ "discordName": "The Paper Pilot Community",
"discordLink": "https://discord.gg/WzejVAx",
"versionNumber": "0.0",
diff --git a/src/main.css b/src/main.css
index acbbf35..ca0f002 100644
--- a/src/main.css
+++ b/src/main.css
@@ -48,6 +48,14 @@ a:hover,
-3px 0 12px var(--link);
}
+.button:disabled {
+ opacity: .5;
+ cursor: not-allowed;
+}
+.button:disabled:hover {
+ text-shadow: none;
+}
+
ul {
list-style-type: none;
}
diff --git a/src/main.js b/src/main.js
index f93c405..cc05adf 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import App from './App';
import store from './store';
-import { addLayer} from './store/layers';
+import { load } from './util/save';
import { setVue } from './util/vue';
import { startGameLoop } from './store/game';
import './components/index';
@@ -10,9 +10,7 @@ import './components/index';
Vue.config.productionTip = false;
requestAnimationFrame(async () => {
- // Add layers on second frame so dependencies can resolve
- const { initialLayers } = await import('./data/mod');
- initialLayers.forEach(addLayer);
+ await load();
// Create Vue
const vue = window.vue = new Vue({
diff --git a/src/store/index.js b/src/store/index.js
index 6a71852..b0d43c0 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -1,6 +1,6 @@
import Vue from 'vue'
import Vuex from 'vuex'
-import { getInitialStore } from '../util/load';
+import { getInitialStore } from '../util/save';
import { getters } from '../data/mod';
Vue.use(Vuex);
diff --git a/src/store/layers.js b/src/store/layers.js
index cd7b995..22a83ef 100644
--- a/src/store/layers.js
+++ b/src/store/layers.js
@@ -1,3 +1,5 @@
+import Vue from 'vue';
+import clone from 'lodash.clonedeep';
import { isFunction, isPlainObject } from '../util/common';
import { createProxy, createGridProxy, player } from './proxies';
import Decimal from '../util/bignum';
@@ -21,13 +23,16 @@ export function addLayer(layer) {
return;
}
}
- if (layer.type === "static" && (layer.base == undefined || Decimal.lte(layer.base, 1))) {
- layer.base = 2;
- }
+
+ // Clone object to prevent modifying the original
+ layer = clone(layer);
// Set default property values
layer = Object.assign({}, defaultLayerProperties, layer);
layer.layer = layer.id;
+ if (layer.type === "static" && (layer.base == undefined || Decimal.lte(layer.base, 1))) {
+ layer.base = 2;
+ }
const getters = {};
@@ -149,6 +154,9 @@ export function addLayer(layer) {
if (layer.challenges[id].onExit != undefined) {
layer.challenges[id].onExit.forceCached = false;
}
+ layer.challenges[id].shown = function() {
+ return this.unlocked !== false && (player.hideChallenges === false || !this.maxed);
+ }
layer.challenges[id].completed = function() {
return !layer.deactivated && player[layer.id].challenges[id]?.gt(0);
}
@@ -411,7 +419,7 @@ export function removeLayer(layer) {
// Un-set hotkeys
if (layers[layer].hotkeys) {
for (let id in layers[layer].hotkeys) {
- delete hotkeys[id];
+ Vue.delete(hotkeys, id);
}
}
diff --git a/src/store/proxies.js b/src/store/proxies.js
index 2261101..bd9466c 100644
--- a/src/store/proxies.js
+++ b/src/store/proxies.js
@@ -57,6 +57,10 @@ const playerHandler = {
}
}
return true;
+ },
+ deleteProperty(target, prop) {
+ Vue.delete(target, prop);
+ return true;
}
};
export const player = window.player = new Proxy(store.state, playerHandler);
diff --git a/src/util/load.js b/src/util/load.js
deleted file mode 100644
index 115d477..0000000
--- a/src/util/load.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import modInfo from '../data/modInfo';
-import { getStartingData, initialLayers } from '../data/mod';
-import { getStartingBuyables, getStartingClickables, getStartingChallenges } from './layers';
-import Decimal from './bignum';
-
-export function getInitialStore() {
- return {
- tabs: modInfo.initialTabs.slice(),
- time: Date.now(),
- autosave: true,
- offlineProd: true,
- timePlayed: new Decimal(0),
- keepGoing: false,
- lastTenTicks: [],
- showTPS: true,
- msDisplay: "all",
- hideChallenges: false,
- theme: "paper",
- subtabs: {},
- minimized: {},
- ...getStartingData(),
- ...initialLayers.reduce((acc, layer) => {
- acc[layer.id] = {
- upgrades: [],
- achievements: [],
- milestones: [],
- infoboxes: {},
- buyables: getStartingBuyables(layer),
- clickables: getStartingClickables(layer),
- challenges: getStartingChallenges(layer),
- ...layer.startData?.()
- };
- return acc;
- }, {}),
-
- // Values that don't get saved
- hasNaN: false,
- NaNProperty: "",
- NaNReceiver: null,
- NaNPrevious: null
- }
-}
diff --git a/src/util/save.js b/src/util/save.js
new file mode 100644
index 0000000..97f29ea
--- /dev/null
+++ b/src/util/save.js
@@ -0,0 +1,169 @@
+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';
+
+export const NOT_IMPORTING = false;
+export const IMPORTING = true;
+export const IMPORTING_FAILED = "FAILED";
+export const IMPORTING_WRONG_ID = "WRONG_ID";
+export const IMPORTING_FORCE = "FORCE";
+
+export function getInitialStore(playerData = {}) {
+ playerData = applyPlayerData({
+ id: `${modInfo.id}-0`,
+ name: "Default Save",
+ tabs: modInfo.initialTabs.slice(),
+ time: Date.now(),
+ autosave: true,
+ offlineProd: true,
+ timePlayed: new Decimal(0),
+ keepGoing: false,
+ lastTenTicks: [],
+ showTPS: true,
+ msDisplay: "all",
+ hideChallenges: false,
+ theme: "paper",
+ subtabs: {},
+ minimized: {},
+ modID: modInfo.id,
+ modVersion: modInfo.versionNumber,
+ ...getStartingData(),
+
+ // Values that don't get loaded/saved
+ hasNaN: false,
+ NaNProperty: "",
+ NaNReceiver: null,
+ NaNPrevious: null,
+ importing: NOT_IMPORTING,
+ 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() {
+ /* eslint-disable-next-line no-unused-vars */
+ let { hasNaN, NaNProperty, NaNReceiver, NaNPrevious, importing, saveToImport, saveToExport, ...playerData } = player;
+ player.saveToExport = btoa(unescape(encodeURIComponent(JSON.stringify(playerData))));
+
+ localStorage.setItem(player.id, player.saveToExport);
+}
+
+export async function load() {
+ try {
+ let modData = localStorage.getItem(modInfo.id);
+ if (modData == null) {
+ await loadSave(newSave());
+ return;
+ }
+ modData = JSON.parse(decodeURIComponent(escape(atob(modData))));
+ if (modData?.active == null) {
+ await loadSave(newSave());
+ return;
+ }
+ const save = localStorage.getItem(modData.active);
+ const playerData = JSON.parse(decodeURIComponent(escape(atob(save))));
+ if (playerData.modID !== modInfo.id) {
+ await loadSave(newSave());
+ return;
+ }
+ await loadSave(playerData);
+ } catch (e) {
+ await loadSave(newSave());
+ }
+}
+
+export async function newSave() {
+ const id = getUniqueID();
+ const playerData = getInitialStore({ id });
+ localStorage.setItem(id, btoa(unescape(encodeURIComponent(JSON.stringify(playerData)))));
+
+ if (!localStorage.getItem(modInfo.id)) {
+ const modData = { active: id, saves: [ id ] };
+ localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
+ } else {
+ const modData = JSON.parse(decodeURIComponent(escape(atob(localStorage.getItem(modInfo.id)))));
+ modData.saves.push(id);
+ localStorage.setItem(modInfo.id, btoa(unescape(encodeURIComponent(JSON.stringify(modData)))));
+ }
+
+ return playerData;
+}
+
+export function getUniqueID() {
+ let id, i = 0;
+ do {
+ id = `${modInfo.id}-${i++}`;
+ } while (localStorage.getItem(id));
+ return id;
+}
+
+export async function loadSave(playerData) {
+ const { layers, removeLayer, addLayer } = await import('../store/layers');
+
+ for (let layer in layers) {
+ removeLayer(layer);
+ }
+ getInitialLayers(playerData).forEach(addLayer);
+
+ playerData = getInitialStore(playerData);
+ if (playerData.offlineProd) {
+ if (playerData.offTime === undefined)
+ playerData.offTime = { remain: 0 };
+ playerData.offTime.remain += (Date.now() - playerData.time) / 1000;
+ }
+ playerData.time = Date.now();
+ if (playerData.modVersion !== modInfo.versionNumber) {
+ fixOldSave(playerData.modVersion, playerData);
+ }
+
+ Object.assign(player, playerData);
+ for (let prop in player) {
+ if (!(prop in playerData)) {
+ delete player[prop];
+ }
+ }
+}
+
+function applyPlayerData(target, source) {
+ 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]);
+ } else {
+ target[prop] = source[prop];
+ }
+ }
+ return target;
+}
+
+setInterval(() => {
+ if (player.autosave) {
+ save();
+ }
+}, 1000);
+window.onbeforeunload = () => {
+ if (player.autosave) {
+ save();
+ }
+};
diff --git a/vue.config.js b/vue.config.js
index a074770..7d30f61 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -1,6 +1,4 @@
module.exports = {
- publicPath: process.env.NODE_ENV === 'production'
- ? '/The-Modding-Tree-X'
- : '/',
- runtimeCompiler: true
+ publicPath: process.env.NODE_ENV === 'production' ? '/The-Modding-Tree-X' : '/',
+ runtimeCompiler: true
};