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/components/PlayerLink.vue b/src/components/PlayerLink.vue
index 51e93e902..e714845f4 100644
--- a/src/components/PlayerLink.vue
+++ b/src/components/PlayerLink.vue
@@ -2,28 +2,33 @@
-
-
Share live initiative list
-
- Let your players follow a live initiative list.
-
+
+
Let your players follow a live initiative list.
-
- Make sure your campaign is set to Public, or your followers won't be able to see it.
- Then click the GO LIVE icon in your campaign to share the initiative of the encounter that is active. You can stay live for your entire session, whenever you're not running an encounter, followers won't see what you're doing.
+ Make sure your campaign is set to Public, or your followers won't
+ be able to see it. Then click the GO LIVE icon in your
+ campaign to share the initiative of the encounter that is active. You can stay live for your
+ entire session, whenever you're not running an encounter, followers won't see what you're
+ doing.
\ No newline at end of file
+}
+
diff --git a/src/components/combat/Targets.vue b/src/components/combat/Targets.vue
index e8ff0266a..86b60b493 100644
--- a/src/components/combat/Targets.vue
+++ b/src/components/combat/Targets.vue
@@ -1,54 +1,66 @@
-