+
+
Found
-
- {{ imports[type].length }} {{ type === "unique" ? "new" : "duplicate" }}
+
+ {{ imports[import_type].length }} {{ import_type === "unique" ? "new" : "duplicate" }}
{{ type_label }}
+
+
+ Found
+ {{ Object.values(custom_spells).length }}
+ Custom Spells
+
+
+
+
+
+
+ Insufficient spell slots. You're trying to import
+ {{ Object.keys(this.custom_spells).length }}
+ spells,
+ but have only {{ availableSpellSlots }} slots available.
+
+
+
+ Get more slots
+
+
+
+
Insufficient slots. You're trying to import
{{ importTotal }} {{ type_label }},
@@ -256,6 +319,9 @@ export default {
overwrite: true,
parsing: false,
parsed: false,
+ map_old_to_custom: {},
+ custom_spells: {},
+ import_custom_spells: true,
failed_imports: [],
importing: undefined,
imported: 0,
@@ -310,6 +376,11 @@ export default {
? Infinity
: this.tier.benefits[this.type] - this.content_count;
},
+ availableSpellSlots() {
+ return this.tier.benefits["spells"] === "infinite"
+ ? Infinity
+ : this.tier.benefits["spells"] - this.spell_count;
+ },
importTotal() {
return !this.overwrite
? this.selected.unique.length + this.selected.duplicate.length
@@ -321,14 +392,21 @@ export default {
},
methods: {
...mapActions("npcs", ["add_npc", "edit_npc", "get_npcs", "get_npc"]),
- ...mapActions("spells", ["add_spell", "edit_spell", "get_spells", "get_spell"]),
+ ...mapActions("spells", [
+ "add_spell",
+ "edit_spell",
+ "get_spells",
+ "get_spell",
+ "get_spell_id_by_name",
+ "reserve_spell_id",
+ ]),
async getItem(id) {
return this.type === "npcs"
? this.get_npc({ uid: this.uid, id })
: this.get_spell({ uid: this.uid, id });
},
async addItem(item) {
- this.type === "npcs" ? await this.add_npc(item) : await this.add_spell(item);
+ this.type === "npcs" ? await this.add_npc(item) : await this.add_spell({ spell: item });
},
async editItem(id, item) {
this.type === "npcs"
@@ -370,8 +448,8 @@ export default {
let checkable_item = item;
// Parse versatile to options for NPCs
if (this.type === "npcs") {
- checkable_item = this.removeCustomSpells(item);
- checkable_item = this.versatileToOptions(item);
+ checkable_item = await this.parseCustomSpells(item);
+ checkable_item = this.versatileToOptions(checkable_item);
}
const valid = ajv.validate(this.schema, checkable_item);
@@ -410,6 +488,22 @@ export default {
this.parsed = true;
},
async importData() {
+ // First check if there are custom spells from imported NPCs that need to be added.
+ if (
+ this.import_custom_spells &&
+ this.custom_spells &&
+ Object.keys(this.custom_spells).length <= this.availableSpellSlots
+ ) {
+ for (const [key, spell] of Object.entries(this.custom_spells)) {
+ try {
+ // TODO: use parse function to filter out spells
+ await this.add_spell({ spell: spell, predefined_key: key });
+ } catch (error) {
+ this.failed_imports.push(spell);
+ }
+ }
+ }
+
if (this.importTotal <= this.availableSlots) {
this.importing = this.selected.unique.length + this.selected.duplicate.length;
for (const item of this.selected.unique) {
@@ -532,19 +626,44 @@ export default {
}
return npc;
},
- removeCustomSpells(npc) {
- delete npc.custom_spells;
- for (const spell_list_type of ["caster_spells", "innate_spells"]) {
- if (npc[spell_list_type]) {
- const spell_list = Object.assign({}, npc[spell_list_type]);
- for (const [spell_key, spell] of Object.entries(spell_list)) {
- if (spell.custom) {
- delete npc[spell_list_type][spell_key];
+ async parseCustomSpells(npc) {
+ if (npc.custom_spells) {
+ for (const [old_key, spell] of Object.entries(npc.custom_spells)) {
+ // Check if there already is a spell with name
+ delete spell.key;
+ delete spell.updated;
+ delete spell.created;
+ const valid = ajv.validate(spellSchema, spell);
+ if (!valid) {
+ spell.errors = ajv.errors;
+ }
+ // Check if spell is already known to importer
+ if (!Object.keys(this.map_old_to_custom).includes(old_key)) {
+ let spell_id = await this.get_spell_id_by_name({ name: spell.name });
+ if (!spell_id) {
+ // Generate a id for the spell so when we can link the spell in NPC to a future spell
+ const new_spell_id = await this.reserve_spell_id();
+ spell_id = new_spell_id;
+ this.$set(this.custom_spells, spell_id, spell);
+ }
+
+ this.map_old_to_custom[old_key] = spell_id;
+ }
+ }
+
+ for (const spell_list_type of ["caster_spells", "innate_spells"]) {
+ if (npc[spell_list_type]) {
+ const spell_list = Object.assign({}, npc[spell_list_type]);
+ for (const [spell_key, spell] of Object.entries(spell_list)) {
+ if (spell.custom && spell_key in this.map_old_to_custom) {
+ npc[spell_list_type][this.map_old_to_custom[spell_key]] = { ...spell };
+ delete npc[spell_list_type][spell_key];
+ }
}
}
}
+ delete npc.custom_spells;
}
-
return npc;
},
},
@@ -555,4 +674,7 @@ export default {
.q-expansion-item {
background-color: $neutral-9;
}
+.no-table-margin::v-deep table {
+ margin-bottom: 0;
+}
diff --git a/src/services/spells.js b/src/services/spells.js
index 24f8b390..9802c94d 100644
--- a/src/services/spells.js
+++ b/src/services/spells.js
@@ -60,6 +60,20 @@ export class SpellServices {
}
}
+ async getSearchSpellByName(uid, name) {
+ try {
+ const spell = await SEARCH_SPELLS_REF.child(uid)
+ .child("results")
+ .orderByChild("name")
+ .equalTo(name)
+ .get();
+
+ return spell.val();
+ } catch (error) {
+ throw error;
+ }
+ }
+
/**
* Adds an spell to the 'custom_spells' ref and the 'search_custom_spells' ref.
* Also updates the count metadata in 'search_custom_spells'
@@ -70,18 +84,25 @@ export class SpellServices {
* @param {Object} search_spell Compressed spell
* @returns Key of the newly added spell
*/
- async addSpell(uid, spell, search_spell) {
+ async addSpell(uid, spell, search_spell, predefined_key = undefined) {
try {
spell.name = spell.name.toLowerCase();
- const newSpell = await SPELLS_REF.child(uid).push(spell);
spell.created = firebase.database.ServerValue.TIMESTAMP;
spell.updated = firebase.database.ServerValue.TIMESTAMP;
+ let spell_key = predefined_key;
+ if (predefined_key) {
+ await SPELLS_REF.child(uid).child(predefined_key).set(spell);
+ } else {
+ const newSpell = await SPELLS_REF.child(uid).push(spell);
+ spell_key = newSpell.key;
+ }
+
// Update search_spells
- SEARCH_SPELLS_REF.child(`${uid}/results/${newSpell.key}`).set(search_spell);
+ SEARCH_SPELLS_REF.child(`${uid}/results/${spell_key}`).set(search_spell);
- return newSpell.key;
+ return spell_key;
} catch (error) {
throw error;
}
@@ -128,6 +149,19 @@ export class SpellServices {
}
}
+ /**
+ * Reserve an ID for a spell that might be stored in the future
+ * Useful when you don't know yet if you want to store a spell, but want to be able to link to it
+ * from different db entries
+ *
+ * E.g. New NPC has Custom Spells that need to be stored, but you store both NPC and Spell at same time.
+ *
+ * @param {String} uid ID of active user
+ */
+ async reserveSpellId(uid) {
+ return (await SPELLS_REF.child(uid).push()).key;
+ }
+
/**
* Update spell_count in the search table of search_spells
*
diff --git a/src/store/modules/user.js b/src/store/modules/user.js
index 4e69c853..cae907eb 100644
--- a/src/store/modules/user.js
+++ b/src/store/modules/user.js
@@ -390,7 +390,7 @@ const user_actions = {
await dispatch("npcs/clear_npc_store", {}, { root: true });
await dispatch("reminders/clear_reminder_store", {}, { root: true });
await dispatch("items/clear_item_store", {}, { root: true });
- await dispatch("items/clear_spell_store", {}, { root: true });
+ await dispatch("spells/clear_spell_store", {}, { root: true });
await commit("CLEAR_USER", undefined);
// Sign out from firebase
diff --git a/src/store/modules/userContent/spells.js b/src/store/modules/userContent/spells.js
index 76dbc9d3..0a43be4b 100644
--- a/src/store/modules/userContent/spells.js
+++ b/src/store/modules/userContent/spells.js
@@ -106,6 +106,22 @@ const spell_actions = {
return spell;
},
+ async get_spell_id_by_name({ rootGetters, dispatch }, { name }) {
+ const uid = rootGetters.user ? rootGetters.user.uid : undefined;
+ if (uid) {
+ const services = await dispatch("get_spell_services");
+ try {
+ const spells = await services.getSearchSpellByName(uid, name);
+ if (spells === null) {
+ return null;
+ }
+ return Object.keys(spells)[0];
+ } catch (error) {
+ throw error;
+ }
+ }
+ },
+
/**
* Adds a newly created SPELL for a user
* A user can only add SPELLS for themselves so we use the uid from the store
@@ -113,7 +129,7 @@ const spell_actions = {
* @param {object} spell
* @returns {string} the id of the newly added spell
*/
- async add_spell({ rootGetters, commit, dispatch }, spell) {
+ async add_spell({ rootGetters, commit, dispatch }, { spell, predefined_key }) {
const uid = rootGetters.user ? rootGetters.user.uid : undefined;
const available_slots = rootGetters.tier.benefits.spells;
@@ -125,14 +141,13 @@ const spell_actions = {
return "Not enough slots";
}
try {
+ console.log(spell);
const search_spell = convert_spell(spell);
- const id = await services.addSpell(uid, spell, search_spell);
+ const id = await services.addSpell(uid, spell, search_spell, predefined_key);
commit("SET_SPELL", { id, search_spell });
commit("SET_CACHED_SPELL", { uid, id, spell });
- const new_count = await services.updateSpellCount(uid, 1);
- commit("SET_SPELL_COUNT", new_count);
- dispatch("checkEncumbrance", "", { root: true });
+ await dispatch("update_spell_count", 1);
return id;
} catch (error) {
throw error;
@@ -179,10 +194,40 @@ const spell_actions = {
commit("REMOVE_SPELL", id);
commit("REMOVE_CACHED_SPELL", { uid, id });
- const new_count = await services.updateSpellCount(uid, -1);
+ await dispatch("update_spell_count", -1);
+ return;
+ } catch (error) {
+ throw error;
+ }
+ }
+ },
+
+ /**
+ * Update spell count
+ */
+ async update_spell_count({ rootGetters, dispatch, commit }, value) {
+ const uid = rootGetters.user ? rootGetters.user.uid : undefined;
+ if (uid) {
+ const services = await dispatch("get_spell_services");
+ try {
+ const new_count = await services.updateSpellCount(uid, value);
commit("SET_SPELL_COUNT", new_count);
dispatch("checkEncumbrance", "", { root: true });
- return;
+ } catch (error) {
+ throw error;
+ }
+ }
+ },
+
+ /**
+ * Reserve Spell id for future usage
+ */
+ async reserve_spell_id({ rootGetters, dispatch }) {
+ const uid = rootGetters.user ? rootGetters.user.uid : undefined;
+ if (uid) {
+ const services = await dispatch("get_spell_services");
+ try {
+ return await services.reserveSpellId(uid);
} catch (error) {
throw error;
}
diff --git a/src/views/UserContent/Npcs/Npcs.vue b/src/views/UserContent/Npcs/Npcs.vue
index fadbdc8c..81a955d0 100644
--- a/src/views/UserContent/Npcs/Npcs.vue
+++ b/src/views/UserContent/Npcs/Npcs.vue
@@ -249,7 +249,7 @@ export default {
const all_npcs = await this.get_full_npcs();
for (const key in all_npcs) {
all_npcs[key].harmless_key = key;
- this.addCustomSpellToExport(all_npcs[key]);
+ await this.addCustomSpellToExport(all_npcs[key]);
}
const json_export = Object.values(all_npcs);
diff --git a/src/views/UserContent/Spells/EditSpell.vue b/src/views/UserContent/Spells/EditSpell.vue
index d42d8e43..353adf7e 100644
--- a/src/views/UserContent/Spells/EditSpell.vue
+++ b/src/views/UserContent/Spells/EditSpell.vue
@@ -163,7 +163,7 @@ export default {
},
addSpell() {
console.log(this.spell);
- this.add_spell(this.spell)
+ this.add_spell({ spell: this.spell })
.then((key) => {
// Set the spellId, so we know there is an existing spell
// even though we are on the AddSpell route, this we won't create multiple when hitting save again