From 99c14099dca4473233f8c079596a7a563f4193db Mon Sep 17 00:00:00 2001 From: ThaumRystra <9525416+ThaumRystra@users.noreply.github.com> Date: Wed, 5 Jun 2024 15:10:22 +0200 Subject: [PATCH] Began adding creature templates to libraries --- .../methods/insertPropertyFromLibraryNode.js | 4 +- .../applyCreatureTemplateProperty.ts | 29 ++ .../engine/action/applyProperties/index.ts | 2 + .../api/properties/CreatureTemplates.ts | 40 ++ .../computedOnlyPropertySchemasIndex.js | 16 +- .../computedPropertySchemasIndex.js | 10 +- .../api/properties/propertySchemasIndex.js | 22 +- app/imports/api/tabletop/Tabletops.ts | 1 + .../addCreaturesFromLibraryToTabletop.ts | 105 ++++ .../ui/dialogStack/DialogComponentIndex.js | 2 + .../properties/forms/CreatureTemplateForm.vue | 53 ++ .../forms/shared/propertyFormIndex.js | 2 + .../viewers/CreatureTemplateViewer.vue | 17 + .../viewers/shared/propertyViewerIndex.js | 2 + .../ui/tabletop/CreatureFromLibraryDialog.vue | 458 ++++++++++++++++++ .../client/ui/tabletop/TabletopComponent.vue | 25 +- .../SelectedCreatureBar.vue | 4 +- app/imports/constants/PROPERTIES.js | 8 + 18 files changed, 774 insertions(+), 26 deletions(-) create mode 100644 app/imports/api/engine/action/applyProperties/applyCreatureTemplateProperty.ts create mode 100644 app/imports/api/properties/CreatureTemplates.ts create mode 100644 app/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop.ts create mode 100644 app/imports/client/ui/properties/forms/CreatureTemplateForm.vue create mode 100644 app/imports/client/ui/properties/viewers/CreatureTemplateViewer.vue create mode 100644 app/imports/client/ui/tabletop/CreatureFromLibraryDialog.vue diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js index 87fc681a..b4d833c8 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -117,7 +117,7 @@ function insertPropertyFromNode(nodeId, root, parentId) { return node; } -function storeLibraryNodeReferences(nodes) { +export function storeLibraryNodeReferences(nodes) { nodes.forEach(node => { if (node.libraryNodeId) return; node.libraryNodeId = node._id; @@ -126,7 +126,7 @@ function storeLibraryNodeReferences(nodes) { // Covert node references into actual nodes // TODO: check permissions for each library a reference node references -function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) { +export function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0) { depth += 1; // New nodes added this function let newNodes = []; diff --git a/app/imports/api/engine/action/applyProperties/applyCreatureTemplateProperty.ts b/app/imports/api/engine/action/applyProperties/applyCreatureTemplateProperty.ts new file mode 100644 index 00000000..baea93cf --- /dev/null +++ b/app/imports/api/engine/action/applyProperties/applyCreatureTemplateProperty.ts @@ -0,0 +1,29 @@ +import { EngineAction } from '/imports/api/engine/action/EngineActions'; +import { PropTask } from '/imports/api/engine/action/tasks/Task'; +import recalculateInlineCalculations from '/imports/api/engine/action/functions/recalculateInlineCalculations'; +import getPropertyTitle from '/imports/api/utility/getPropertyTitle'; + +export default async function applyCreatureTemplateProperty( + task: PropTask, action: EngineAction, result, userInput +): Promise { + const prop = task.prop; + //Log the Creature that is about to be summoned + let logValue = prop.description?.value + if (prop.description?.text) { + recalculateInlineCalculations(prop.description, action, 'reduce', userInput); + logValue = prop.description?.value; + } + // There are no targets for creature templates + // Creatures are always summoned as children of the action's creature + result.appendLog({ + name: getPropertyTitle(prop), + value: logValue + }, []); + + result.appendLog({ + name: 'Warning', + value: 'Creature summoning is not yet implemented...' + }, []); + + return; +} diff --git a/app/imports/api/engine/action/applyProperties/index.ts b/app/imports/api/engine/action/applyProperties/index.ts index b835db2b..458c5bb6 100644 --- a/app/imports/api/engine/action/applyProperties/index.ts +++ b/app/imports/api/engine/action/applyProperties/index.ts @@ -8,6 +8,7 @@ import adjustment from './applyAdjustmentProperty'; import branch from './applyBranchProperty'; import buff from './applyBuffProperty'; import buffRemover from './applyBuffRemoverProperty'; +import creature from './applyCreatureTemplateProperty'; import damage from './applyDamageProperty'; import folder from './applyFolderProperty'; import note from './applyNoteProperty'; @@ -24,6 +25,7 @@ const applyPropertyByType: { branch, buff, buffRemover, + creature, damage, folder, note, diff --git a/app/imports/api/properties/CreatureTemplates.ts b/app/imports/api/properties/CreatureTemplates.ts new file mode 100644 index 00000000..7f48edf0 --- /dev/null +++ b/app/imports/api/properties/CreatureTemplates.ts @@ -0,0 +1,40 @@ +import SimpleSchema from 'simpl-schema'; +import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS'; +import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema'; + +// Creature templates represent creatures that don't yet exist +// Used to store creatures in the library, or as templates for another creature to summon +const CreatureTemplateSchema = createPropertySchema({ + name: { + type: String, + max: STORAGE_LIMITS.name, + optional: true, + }, + description: { + type: 'inlineCalculationFieldToCompute', + optional: true, + }, + picture: { + type: String, + optional: true, + max: STORAGE_LIMITS.url, + }, + avatarPicture: { + type: String, + optional: true, + max: STORAGE_LIMITS.url, + }, +}); + +const ComputedOnlyCreatureTemplateSchema = createPropertySchema({ + description: { + type: 'computedOnlyInlineCalculationField', + optional: true, + }, +}); + +const ComputedCreatureTemplateSchema = new SimpleSchema({}) + .extend(CreatureTemplateSchema) + .extend(ComputedOnlyCreatureTemplateSchema); + +export { CreatureTemplateSchema, ComputedCreatureTemplateSchema, ComputedOnlyCreatureTemplateSchema }; diff --git a/app/imports/api/properties/computedOnlyPropertySchemasIndex.js b/app/imports/api/properties/computedOnlyPropertySchemasIndex.js index a70304ed..3017d84a 100644 --- a/app/imports/api/properties/computedOnlyPropertySchemasIndex.js +++ b/app/imports/api/properties/computedOnlyPropertySchemasIndex.js @@ -2,15 +2,16 @@ import SimpleSchema from 'simpl-schema'; import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions'; import { ComputedOnlyAdjustmentSchema } from '/imports/api/properties/Adjustments'; import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes'; -import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs'; -import { ComputedOnlyBuffRemoverSchema } from '/imports/api/properties/BuffRemovers'; import { ComputedOnlyBranchSchema } from '/imports/api/properties/Branches'; -import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes'; +import { ComputedOnlyBuffRemoverSchema } from '/imports/api/properties/BuffRemovers'; +import { ComputedOnlyBuffSchema } from '/imports/api/properties/Buffs'; import { ComputedOnlyClassLevelSchema } from '/imports/api/properties/ClassLevels'; +import { ComputedOnlyClassSchema } from '/imports/api/properties/Classes'; import { ComputedOnlyConstantSchema } from '/imports/api/properties/Constants'; import { ComputedOnlyContainerSchema } from '/imports/api/properties/Containers'; -import { ComputedOnlyDamageSchema } from '/imports/api/properties/Damages'; +import { ComputedOnlyCreatureTemplateSchema } from '/imports/api/properties/CreatureTemplates'; import { ComputedOnlyDamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers'; +import { ComputedOnlyDamageSchema } from '/imports/api/properties/Damages'; import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects'; import { ComputedOnlyFeatureSchema } from '/imports/api/properties/Features'; import { ComputedOnlyFolderSchema } from '/imports/api/properties/Folders'; @@ -23,8 +24,8 @@ import { ComputedOnlyRollSchema } from '/imports/api/properties/Rolls'; import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows'; import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills'; import { ComputedOnlySlotSchema } from '/imports/api/properties/Slots'; -import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells'; import { ComputedOnlySpellListSchema } from '/imports/api/properties/SpellLists'; +import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells'; import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles'; import { ComputedOnlyTriggerSchema } from '/imports/api/properties/Triggers'; @@ -32,13 +33,14 @@ const propertySchemasIndex = { action: ComputedOnlyActionSchema, adjustment: ComputedOnlyAdjustmentSchema, attribute: ComputedOnlyAttributeSchema, + branch: ComputedOnlyBranchSchema, buff: ComputedOnlyBuffSchema, buffRemover: ComputedOnlyBuffRemoverSchema, - branch: ComputedOnlyBranchSchema, class: ComputedOnlyClassSchema, classLevel: ComputedOnlyClassLevelSchema, constant: ComputedOnlyConstantSchema, container: ComputedOnlyContainerSchema, + creature: ComputedOnlyCreatureTemplateSchema, damage: ComputedOnlyDamageSchema, damageMultiplier: ComputedOnlyDamageMultiplierSchema, effect: ComputedOnlyEffectSchema, @@ -53,8 +55,8 @@ const propertySchemasIndex = { roll: ComputedOnlyRollSchema, savingThrow: ComputedOnlySavingThrowSchema, skill: ComputedOnlySkillSchema, - spellList: ComputedOnlySpellListSchema, spell: ComputedOnlySpellSchema, + spellList: ComputedOnlySpellListSchema, toggle: ComputedOnlyToggleSchema, trigger: ComputedOnlyTriggerSchema, any: new SimpleSchema({}), diff --git a/app/imports/api/properties/computedPropertySchemasIndex.js b/app/imports/api/properties/computedPropertySchemasIndex.js index 483e41d3..86fba824 100644 --- a/app/imports/api/properties/computedPropertySchemasIndex.js +++ b/app/imports/api/properties/computedPropertySchemasIndex.js @@ -10,6 +10,7 @@ import { ComputedClassSchema } from '/imports/api/properties/Classes'; import { ComputedClassLevelSchema } from '/imports/api/properties/ClassLevels'; import { ConstantSchema } from '/imports/api/properties/Constants'; import { ComputedContainerSchema } from '/imports/api/properties/Containers'; +import { ComputedCreatureTemplateSchema } from '/imports/api/properties/CreatureTemplates'; import { ComputedDamageSchema } from '/imports/api/properties/Damages'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers'; import { ComputedEffectSchema } from '/imports/api/properties/Effects'; @@ -33,17 +34,20 @@ const propertySchemasIndex = { action: ComputedActionSchema, adjustment: ComputedAdjustmentSchema, attribute: ComputedAttributeSchema, + branch: ComputedBranchSchema, buff: ComputedBuffSchema, buffRemover: ComputedBuffRemoverSchema, - branch: ComputedBranchSchema, class: ComputedClassSchema, classLevel: ComputedClassLevelSchema, constant: ConstantSchema, + container: ComputedContainerSchema, + creature: ComputedCreatureTemplateSchema, damage: ComputedDamageSchema, damageMultiplier: DamageMultiplierSchema, effect: ComputedEffectSchema, feature: ComputedFeatureSchema, folder: ComputedFolderSchema, + item: ComputedItemSchema, note: ComputedNoteSchema, pointBuy: ComputedPointBuySchema, proficiency: ProficiencySchema, @@ -52,12 +56,10 @@ const propertySchemasIndex = { roll: ComputedRollSchema, savingThrow: ComputedSavingThrowSchema, skill: ComputedSkillSchema, - spellList: ComputedSpellListSchema, spell: ComputedSpellSchema, + spellList: ComputedSpellListSchema, toggle: ComputedToggleSchema, trigger: ComputedTriggerSchema, - container: ComputedContainerSchema, - item: ComputedItemSchema, any: new SimpleSchema({}), }; diff --git a/app/imports/api/properties/propertySchemasIndex.js b/app/imports/api/properties/propertySchemasIndex.js index 4f56a373..4e022b63 100644 --- a/app/imports/api/properties/propertySchemasIndex.js +++ b/app/imports/api/properties/propertySchemasIndex.js @@ -2,17 +2,20 @@ import SimpleSchema from 'simpl-schema'; import { ActionSchema } from '/imports/api/properties/Actions'; import { AdjustmentSchema } from '/imports/api/properties/Adjustments'; import { AttributeSchema } from '/imports/api/properties/Attributes'; -import { BuffSchema } from '/imports/api/properties/Buffs'; -import { BuffRemoverSchema } from '/imports/api/properties/BuffRemovers'; import { BranchSchema } from '/imports/api/properties/Branches'; -import { ClassSchema } from '/imports/api/properties/Classes'; +import { BuffRemoverSchema } from '/imports/api/properties/BuffRemovers'; +import { BuffSchema } from '/imports/api/properties/Buffs'; import { ClassLevelSchema } from '/imports/api/properties/ClassLevels'; +import { ClassSchema } from '/imports/api/properties/Classes'; import { ConstantSchema } from '/imports/api/properties/Constants'; -import { DamageSchema } from '/imports/api/properties/Damages'; +import { ContainerSchema } from '/imports/api/properties/Containers'; +import { CreatureTemplateSchema } from '/imports/api/properties/CreatureTemplates'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers'; +import { DamageSchema } from '/imports/api/properties/Damages'; import { EffectSchema } from '/imports/api/properties/Effects'; import { FeatureSchema } from '/imports/api/properties/Features'; import { FolderSchema } from '/imports/api/properties/Folders'; +import { ItemSchema } from '/imports/api/properties/Items'; import { NoteSchema } from '/imports/api/properties/Notes'; import { PointBuySchema } from '/imports/api/properties/PointBuys'; import { ProficiencySchema } from '/imports/api/properties/Proficiencies'; @@ -25,24 +28,25 @@ import { SpellListSchema } from '/imports/api/properties/SpellLists'; import { SpellSchema } from '/imports/api/properties/Spells'; import { ToggleSchema } from '/imports/api/properties/Toggles'; import { TriggerSchema } from '/imports/api/properties/Triggers'; -import { ContainerSchema } from '/imports/api/properties/Containers'; -import { ItemSchema } from '/imports/api/properties/Items'; const propertySchemasIndex = { action: ActionSchema, adjustment: AdjustmentSchema, attribute: AttributeSchema, + branch: BranchSchema, buff: BuffSchema, buffRemover: BuffRemoverSchema, - branch: BranchSchema, class: ClassSchema, classLevel: ClassLevelSchema, constant: ConstantSchema, + container: ContainerSchema, + creature: CreatureTemplateSchema, damage: DamageSchema, damageMultiplier: DamageMultiplierSchema, effect: EffectSchema, feature: FeatureSchema, folder: FolderSchema, + item: ItemSchema, note: NoteSchema, pointBuy: PointBuySchema, proficiency: ProficiencySchema, @@ -51,12 +55,10 @@ const propertySchemasIndex = { roll: RollSchema, savingThrow: SavingThrowSchema, skill: SkillSchema, - spellList: SpellListSchema, spell: SpellSchema, + spellList: SpellListSchema, toggle: ToggleSchema, trigger: TriggerSchema, - container: ContainerSchema, - item: ItemSchema, any: new SimpleSchema({}), }; diff --git a/app/imports/api/tabletop/Tabletops.ts b/app/imports/api/tabletop/Tabletops.ts index fdd0b8ef..431cc0c0 100644 --- a/app/imports/api/tabletop/Tabletops.ts +++ b/app/imports/api/tabletop/Tabletops.ts @@ -111,5 +111,6 @@ import '/imports/api/tabletop/methods/insertTabletop'; import '/imports/api/tabletop/methods/updateTabletop'; import '/imports/api/tabletop/methods/addCreaturesToTabletop'; import '/imports/api/tabletop/methods/updateTabletopSharing'; +import '/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop'; export default Tabletops; diff --git a/app/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop.ts b/app/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop.ts new file mode 100644 index 00000000..47e283c8 --- /dev/null +++ b/app/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop.ts @@ -0,0 +1,105 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import { assertUserInTabletop } from './shared/tabletopPermissions'; +import { assertUserHasPaidBenefits } from '/imports/api/users/patreon/tiers'; +import Creatures from '/imports/api/creature/creatures/Creatures'; +import LibraryNodes from '/imports/api/library/LibraryNodes'; +import { getFilter, renewDocIds } from '/imports/api/parenting/parentingFunctions'; +import { reifyNodeReferences, storeLibraryNodeReferences } from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode'; +import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; + +const addCreaturesFromLibraryToTabletop = new ValidatedMethod({ + + name: 'tabletops.addCreaturesFromLibraryToTabletop', + + validate: new SimpleSchema({ + 'libraryNodeIds': { + type: Array, + }, + 'libraryNodeIds.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + tabletopId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }).validator(), + + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 1, + timeInterval: 5_000, + }, + + run({ libraryNodeIds, tabletopId }) { + if (!this.userId) { + throw new Meteor.Error('tabletops.addCreatures.denied', + 'You need to be logged in to remove a tabletop'); + } + assertUserHasPaidBenefits(this.userId); + assertUserInTabletop(tabletopId, this.userId); + + for (const nodeId of libraryNodeIds) { + const creatureNode = LibraryNodes.findOne({ + _id: nodeId, + type: 'creature', + removed: { $ne: true }, + }); + + if (!creatureNode) { + if (Meteor.isClient) return {}; + else { + throw new Meteor.Error( + 'Insert property from library failed', + `No library creature with id '${nodeId}' was found` + ); + } + } + + const creatureId = Creatures.insert({ + ...creatureNode, + type: 'monster', + tabletopId, + owner: this.userId, + writers: [this.userId], + dirty: true, + }); + insertSubProperties(creatureNode, creatureId); + } + }, +}); + +function insertSubProperties(node, creatureId: string) { + let nodes = LibraryNodes.find({ + ...getFilter.descendants(node), + removed: { $ne: true }, + }).fetch(); + + for (const node of nodes) { + node.root = { + '_id': creatureId, + collection: 'creatures', + }; + } + + // Convert all references into actual nodes + nodes = reifyNodeReferences(nodes); + + // set libraryNodeIds + storeLibraryNodeReferences(nodes); + + // Give the docs new IDs without breaking internal references + renewDocIds({ + docArray: nodes, + collectionMap: { 'libraryNodes': 'creatureProperties' } + }); + + // Insert the creature properties + // @ts-expect-error Batch insert not defined + CreatureProperties.batchInsert(nodes); + return node; +} + +export default addCreaturesFromLibraryToTabletop; diff --git a/app/imports/client/ui/dialogStack/DialogComponentIndex.js b/app/imports/client/ui/dialogStack/DialogComponentIndex.js index 6a14e4d1..bb78fb5a 100644 --- a/app/imports/client/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/client/ui/dialogStack/DialogComponentIndex.js @@ -21,6 +21,7 @@ import LibraryBrowserDialog from '/imports/client/ui/library/LibraryBrowserDialo // Lazily load less common dialogs const ArchiveDialog = () => import('/imports/client/ui/creature/archive/ArchiveDialog.vue'); +const CreatureFromLibraryDialog = () => import('/imports/client/ui/tabletop/CreatureFromLibraryDialog.vue'); const DeleteUserAccountDialog = () => import('/imports/client/ui/user/DeleteUserAccountDialog.vue'); const DependencyGraphDialog = () => import('/imports/client/ui/creature/dependencyGraph/DependencyGraphDialog.vue'); const InviteDialog = () => import('/imports/client/ui/user/InviteDialog.vue'); @@ -43,6 +44,7 @@ export default { CharacterCreationDialog, CharacterSheetDialog, CreatureFormDialog, + CreatureFromLibraryDialog, CreaturePropertyDialog, CreaturePropertyFromLibraryDialog, CreatureRootDialog, diff --git a/app/imports/client/ui/properties/forms/CreatureTemplateForm.vue b/app/imports/client/ui/properties/forms/CreatureTemplateForm.vue new file mode 100644 index 00000000..30365f8c --- /dev/null +++ b/app/imports/client/ui/properties/forms/CreatureTemplateForm.vue @@ -0,0 +1,53 @@ + + + diff --git a/app/imports/client/ui/properties/forms/shared/propertyFormIndex.js b/app/imports/client/ui/properties/forms/shared/propertyFormIndex.js index def2dba2..6e138da0 100644 --- a/app/imports/client/ui/properties/forms/shared/propertyFormIndex.js +++ b/app/imports/client/ui/properties/forms/shared/propertyFormIndex.js @@ -8,6 +8,7 @@ import ClassForm from '/imports/client/ui/properties/forms/ClassForm.vue'; import ClassLevelForm from '/imports/client/ui/properties/forms/ClassLevelForm.vue'; import ConstantForm from '/imports/client/ui/properties/forms/ConstantForm.vue'; import ContainerForm from '/imports/client/ui/properties/forms/ContainerForm.vue'; +import CreatureTemplateForm from '/imports/client/ui/properties/forms/CreatureTemplateForm.vue'; import DamageForm from '/imports/client/ui/properties/forms/DamageForm.vue'; import DamageMultiplierForm from '/imports/client/ui/properties/forms/DamageMultiplierForm.vue'; import EffectForm from '/imports/client/ui/properties/forms/EffectForm.vue'; @@ -38,6 +39,7 @@ export default { container: ContainerForm, class: ClassForm, classLevel: ClassLevelForm, + creature: CreatureTemplateForm, damage: DamageForm, damageMultiplier: DamageMultiplierForm, effect: EffectForm, diff --git a/app/imports/client/ui/properties/viewers/CreatureTemplateViewer.vue b/app/imports/client/ui/properties/viewers/CreatureTemplateViewer.vue new file mode 100644 index 00000000..c3b28efc --- /dev/null +++ b/app/imports/client/ui/properties/viewers/CreatureTemplateViewer.vue @@ -0,0 +1,17 @@ + + + diff --git a/app/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js b/app/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js index 29753094..a0cc904f 100644 --- a/app/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js +++ b/app/imports/client/ui/properties/viewers/shared/propertyViewerIndex.js @@ -8,6 +8,7 @@ import ContainerViewer from '/imports/client/ui/properties/viewers/ContainerView import ClassViewer from '/imports/client/ui/properties/viewers/ClassViewer.vue'; import ClassLevelViewer from '/imports/client/ui/properties/viewers/ClassLevelViewer.vue'; import ConstantViewer from '/imports/client/ui/properties/viewers/ConstantViewer.vue'; +import CreatureTemplateViewer from '/imports/client/ui/properties/viewers/CreatureTemplateViewer.vue'; import DamageViewer from '/imports/client/ui/properties/viewers/DamageViewer.vue'; import DamageMultiplierViewer from '/imports/client/ui/properties/viewers/DamageMultiplierViewer.vue'; import EffectViewer from '/imports/client/ui/properties/viewers/EffectViewer.vue'; @@ -38,6 +39,7 @@ export default { class: ClassViewer, classLevel: ClassLevelViewer, constant: ConstantViewer, + creature: CreatureTemplateViewer, damage: DamageViewer, damageMultiplier: DamageMultiplierViewer, effect: EffectViewer, diff --git a/app/imports/client/ui/tabletop/CreatureFromLibraryDialog.vue b/app/imports/client/ui/tabletop/CreatureFromLibraryDialog.vue new file mode 100644 index 00000000..3d56ce3f --- /dev/null +++ b/app/imports/client/ui/tabletop/CreatureFromLibraryDialog.vue @@ -0,0 +1,458 @@ + + + + + +resolveimport { toString } from '/imports/parser/toString'; diff --git a/app/imports/client/ui/tabletop/TabletopComponent.vue b/app/imports/client/ui/tabletop/TabletopComponent.vue index a52c22bb..a3103692 100644 --- a/app/imports/client/ui/tabletop/TabletopComponent.vue +++ b/app/imports/client/ui/tabletop/TabletopComponent.vue @@ -60,7 +60,10 @@ Add Character - + mdi-plus @@ -104,6 +107,7 @@ import { assertEditPermission } from '/imports/api/creature/creatures/creaturePe import ActionCard from '/imports/client/ui/tabletop/TabletopActionCard.vue'; import SelectedCreatureBar from '/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue'; import TabletopCreatureListItem from '/imports/client/ui/tabletop/TabletopCreatureListItem.vue'; +import addCreaturesFromLibraryToTabletop from '/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop'; const getProperties = function (creatureId, selector = {}) { return CreatureProperties.find({ @@ -206,6 +210,25 @@ export default { }, }); }, + addCreatureFromLibrary(){ + this.$store.commit('pushDialogStack', { + component: 'creature-from-library-dialog', + elementId: 'creatures-from-library', + data: {}, + callback: (libraryNodeIds) => { + if (!libraryNodeIds) return; + addCreaturesFromLibraryToTabletop.call({ + tabletopId: this.model._id, + libraryNodeIds, + }, error => { + if (error) { + console.error(error) + snackbar({ text: error.reason || error.message || error.toString() }); + } + }); + }, + }); + }, openCharacterSheetDialog(){ this.$store.commit('pushDialogStack', { component: 'character-sheet-dialog', diff --git a/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue b/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue index 96be8e37..7b16f42f 100644 --- a/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue +++ b/app/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue @@ -270,7 +270,7 @@ export default { // Get the folders that could hide a property const folderIds = CreatureProperties.find({ - 'ancestors.id': this.creatureId, + 'root.id': this.creatureId, type: 'folder', groupStats: true, hideStatsGroup: true, @@ -280,7 +280,7 @@ export default { // Get the properties that need to be shown as an icon const filter = { - 'ancestors.id': this.creatureId, + 'root.id': this.creatureId, 'parentId': { $nin: folderIds, }, diff --git a/app/imports/constants/PROPERTIES.js b/app/imports/constants/PROPERTIES.js index 4686082c..4d4b3cd2 100644 --- a/app/imports/constants/PROPERTIES.js +++ b/app/imports/constants/PROPERTIES.js @@ -71,6 +71,14 @@ const PROPERTIES = Object.freeze({ examples: 'Coin pouch, backpack', suggestedParents: ['folder'], }, + creature: { + icon: 'mdi-account', + name: 'Creature', + docsPath: 'property/creature', + helpText: 'A creature is a template for a creature that might become a real creature once added to a tabletop or summoned by a character', + examples: 'Monsters, raised undead', + suggestedParents: [], + }, damage: { icon: '$vuetify.icons.damage', name: 'Damage',