diff --git a/app/imports/api/creature/creatureCollections.js b/app/imports/api/creature/creatureCollections.js index 1c07dedf..a6efb369 100644 --- a/app/imports/api/creature/creatureCollections.js +++ b/app/imports/api/creature/creatureCollections.js @@ -1,7 +1,6 @@ import Actions from '/imports/api/properties/Actions.js'; import Attributes from '/imports/api/properties/Attributes.js'; import Buffs from '/imports/api/properties/Buffs.js'; -import Classes from '/imports/api/properties/Classes.js'; import ClassLevels from '/imports/api/properties/ClassLevels.js'; import DamageMultipliers from '/imports/api/properties/DamageMultipliers.js'; import Effects from '/imports/api/properties/Effects.js'; @@ -25,7 +24,6 @@ let creatureCollections = [ Actions, Attributes, Buffs, - Classes, ClassLevels, DamageMultipliers, Effects, diff --git a/app/imports/api/creature/creatureComputation.js b/app/imports/api/creature/creatureComputation.js index 7d062ff7..c205e1c9 100644 --- a/app/imports/api/creature/creatureComputation.js +++ b/app/imports/api/creature/creatureComputation.js @@ -10,7 +10,6 @@ import Skills from "/imports/api/properties/Skills.js"; import Effects from "/imports/api/properties/Effects.js"; import Proficiencies from "/imports/api/properties/Proficiencies.js"; import DamageMultipliers from "/imports/api/properties/DamageMultipliers.js"; -import Classes from "/imports/api/properties/Classes.js"; import * as math from 'mathjs'; import parser from '/imports/parser/parser.js'; if (Meteor.isClient) console.log({parser}); diff --git a/app/imports/api/creature/insertCreature.js b/app/imports/api/creature/insertCreature.js index a485560b..fc225123 100644 --- a/app/imports/api/creature/insertCreature.js +++ b/app/imports/api/creature/insertCreature.js @@ -5,7 +5,6 @@ import DamageMultipliers from '/imports/api/properties/DamageMultipliers.js'; import Effects from '/imports/api/properties/Effects.js'; import Containers from '/imports/api/properties/Containers.js'; import Items from '/imports/api/properties/Items.js'; -import Classes from '/imports/api/properties/Classes.js'; const addDefaultDocs = function(docs){ Attributes.rawCollection().insert(docs.attributes, {ordered: false}); @@ -14,7 +13,6 @@ const addDefaultDocs = function(docs){ Effects.rawCollection().insert(docs.effects, {ordered: false}); Containers.rawCollection().insert(docs.containers, {ordered: false}); Items.rawCollection().insert(docs.items, {ordered: false}); - Classes.rawCollection().insert(docs.classes, {ordered: false}); }; const insertCreature = new ValidatedMethod({ diff --git a/app/imports/api/library/librarySchemas.js b/app/imports/api/library/librarySchemas.js index 60059bb2..a98f566f 100644 --- a/app/imports/api/library/librarySchemas.js +++ b/app/imports/api/library/librarySchemas.js @@ -2,7 +2,6 @@ import SimpleSchema from 'simpl-schema'; import { ActionSchema } from '/imports/api/properties/Actions.js'; import { AttributeSchema } from '/imports/api/properties/Attributes.js'; import { StoredBuffSchema } from '/imports/api/properties/Buffs.js'; -import { ClassSchema } from '/imports/api/properties/Classes.js'; import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js'; import { EffectSchema } from '/imports/api/properties/Effects.js'; @@ -22,7 +21,6 @@ const librarySchemas = { action: ActionSchema, attribute: AttributeSchema, buff: StoredBuffSchema, - class: ClassSchema, classLevel: ClassLevelSchema, damageMultiplier: DamageMultiplierSchema, effect: EffectSchema, diff --git a/app/imports/api/parenting/parenting.js b/app/imports/api/parenting/parenting.js index 8e977b73..5fc34263 100644 --- a/app/imports/api/parenting/parenting.js +++ b/app/imports/api/parenting/parenting.js @@ -1,5 +1,41 @@ import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; +import { flatten } from 'lodash'; + +const generalParents = [ + 'attribute', + 'buff', + 'classLevel', + 'feature', + 'folder', + 'root', + 'item', + 'spell', +]; + +// Which types are allowed as parents for other types +const allowedParenting = { + folder: [...generalParents, 'container'], + rollResult: ['roll', 'rollResult'], + container: ['root', 'folder'], + item: ['root', 'container', 'folder'], +}; + +const allParentTypes = new Set(flatten(Object.values(allowedParenting))); + +export function canBeParent(type){ + return type && allParentTypes.has(type); +} + +export function getAllowedParents({childType}){ + return allowedParenting[childType] || generalParents; +} + +export function isParentAllowed({parentType = 'root', childType}){ + if (!childType) throw 'childType is required'; + let allowedParents = getAllowedParents({childType}); + return allowedParents.includes(parentType); +} export function fetchParent({id, collection}){ return fetchDocByRef({id, collection}); @@ -55,7 +91,7 @@ export function getAncestry({parentRef, inheritedFields = {}}){ let ancestors = parentDoc.ancestors || []; ancestors.push(parent); - return {parent, ancestors}; + return {parentDoc, parent, ancestors}; } export function updateParent({docRef, parentRef}){ @@ -63,14 +99,30 @@ export function updateParent({docRef, parentRef}){ let oldDoc = fetchDocByRef(docRef, {fields: { parent: 1, ancestors: 1, + type: 1, }}); let updateOptions = { selector: {type: 'any'} }; // Skip if we aren't changing the parent id if (oldDoc.parent.id === parentRef.id) return; + // Get the parent and its ancestry + let {parentDoc, parent, ancestors} = getAncestry({parentRef}); + + // If the doc and its parent are in the same collection, apply the allowed + // parent rules based on type + if (docRef.collection === parentRef.collection){ + let parentAllowed = isParentAllowed({ + parentType: parentDoc.type, + childType: oldDoc.type + }); + if (!parentAllowed){ + throw new Meteor.Error('invalid parenting', + `Can't make ${oldDoc.type} a child of ${parentDoc.type}`) + } + } + // update the document's parenting - let {parent, ancestors} = getAncestry({parentRef}); collection.update(docRef.id, { $set: {parent, ancestors} }, updateOptions); diff --git a/app/imports/api/properties/Attacks.js b/app/imports/api/properties/Attacks.js new file mode 100644 index 00000000..4df61496 --- /dev/null +++ b/app/imports/api/properties/Attacks.js @@ -0,0 +1,38 @@ +import SimpleSchema from 'simpl-schema'; +import AdjustmentSchema from '/imports/api/creature/subSchemas/AdjustmentSchema.js'; +import { ActionSchema } from '/imports/api/properties/Actions.js'; + +// Attacks are special instances of actions +let AttackSchema = new SimpleSchema() + .extend(ActionSchema) + .extend({ + // What gets added to the d20 roll + rollBonus: { + type: String, + optional: true, + }, + // What damage does it do to the targets + adjustments: { + type: Array, + defaultValue: [], + }, + 'adjustments.$': { + type: AdjustmentSchema, + }, + // If set reference an item whose quantity is reduced by 1 every time this + // attack is rolled + ammunition: { + type: String, + regEx: SimpleSchema.RegEx.Id, + optional: true, + }, + // Set better defaults for the action + type: { + defaultValue: 'attack', + }, + tags: { + defaultValue: ['attack'], + }, + }); + +export { AttackSchema }; diff --git a/app/imports/api/properties/Classes.js b/app/imports/api/properties/Classes.js deleted file mode 100644 index 35655979..00000000 --- a/app/imports/api/properties/Classes.js +++ /dev/null @@ -1,16 +0,0 @@ -import SimpleSchema from 'simpl-schema'; -import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js'; - -// TODO use variable name in computation engine, rather than a generated one -let ClassSchema = new SimpleSchema({ - name: { - type: String, - optional: true, - }, - variableName: { - type: String, - regEx: VARIABLE_NAME_REGEX, - }, -}); - -export { ClassSchema }; diff --git a/app/imports/api/properties/RollResult.js b/app/imports/api/properties/RollResult.js new file mode 100644 index 00000000..bb61410c --- /dev/null +++ b/app/imports/api/properties/RollResult.js @@ -0,0 +1,31 @@ +import SimpleSchema from 'simpl-schema'; +import AdjustmentSchema from '/imports/api/creature/subSchemas/AdjustmentSchema.js'; +import { StoredBuffWithIdSchema } from '/imports/api/properties/Buffs.js'; + +let RollResultSchema = new SimpleSchema ({ + comparison: { + type: String, + allowedValues: ['>', '<', '>=', '<=', '==', 'always', 'else'], + }, + targetValue: { + type: String, + optional: true, + }, + adjustments: { + type: Array, + defaultValue: [], + }, + 'adjustments.$': { + type: AdjustmentSchema, + }, + // The buffs to be applied + buffs: { + type: Array, + defaultValue: [], + }, + 'buffs.$': { + type: StoredBuffWithIdSchema, + }, +}); + +export { RollResultSchema }; diff --git a/app/imports/api/properties/Rolls.js b/app/imports/api/properties/Rolls.js index 111e7132..dc97f201 100644 --- a/app/imports/api/properties/Rolls.js +++ b/app/imports/api/properties/Rolls.js @@ -1,25 +1,4 @@ import SimpleSchema from 'simpl-schema'; -import AdjustmentSchema from '/imports/api/creature/subSchemas/AdjustmentSchema.js'; -import { StoredBuffWithIdSchema } from '/imports/api/properties/Buffs.js'; - -let RollChildrenSchema = new SimpleSchema({ - // The adjustments to be applied - adjustments: { - type: Array, - defaultValue: [], - }, - 'adjustments.$': { - type: AdjustmentSchema, - }, - // The buffs to be applied - buffs: { - type: Array, - defaultValue: [], - }, - 'buffs.$': { - type: StoredBuffWithIdSchema, - }, -}); /** * Rolls are children to actions or other rolls, they are triggered with 0 or @@ -40,34 +19,11 @@ let RollChildrenSchema = new SimpleSchema({ * child rolls are applied */ let RollSchema = new SimpleSchema({ - // The roll made against the target value. A calculation that resolves to a - // number or a roll. If it is a number, it will be added to a d20 roll - roll: { + // The number to add to a d20 roll + rollBonus: { type: String, optional: true, }, - // The target number to meet or exceed - targetNumber: { - type: String, - optional: true, - }, - // Is this roll a saving throw - rollType: { - type: String, - defaultValue: 'roll', - allowedValues: ['roll', 'savingThrow'], - }, - // Apply this only if the parent roll missed - // i.e. roll failed or target suceeded on their save - onMiss: { - type: Boolean, - optional: true, - }, - // Swap who wins ties - invertTies: { - type: Boolean, - optional: true, - }, // Effects can apply to this tag specifically // Ranged spell attack, Ranged weapon attack, etc. tags: { @@ -77,15 +33,6 @@ let RollSchema = new SimpleSchema({ 'tags.$': { type: String, }, - // The buffs and adjustments to apply based on the outcome of the roll - hit: { - type: RollChildrenSchema, - defaultValue: {}, - }, - miss: { - type: RollChildrenSchema, - defaultValue: {}, - }, }); export { RollSchema }; diff --git a/app/imports/api/properties/SavingThrows.js b/app/imports/api/properties/SavingThrows.js new file mode 100644 index 00000000..8825dd21 --- /dev/null +++ b/app/imports/api/properties/SavingThrows.js @@ -0,0 +1,49 @@ +import SimpleSchema from 'simpl-schema'; +import AdjustmentSchema from '/imports/api/creature/subSchemas/AdjustmentSchema.js'; +import { StoredBuffWithIdSchema } from '/imports/api/properties/Buffs.js'; + +// These are the rolls made when saves are called for +// For the saving throw bonus or proficiency, see ./Skills.js +let SavingThrowSchema = new SimpleSchema ({ + dc: { + type: String, + optional: true, + }, + // The variable name of ability the saving throw relies on + ability: { + type: String, + optional: true, + }, + passAdjustments: { + type: Array, + defaultValue: [], + }, + 'passAdjustments.$': { + type: AdjustmentSchema, + }, + // The buffs to be applied + passBuffs: { + type: Array, + defaultValue: [], + }, + 'passBuffs.$': { + type: StoredBuffWithIdSchema, + }, + failAdjustments: { + type: Array, + defaultValue: [], + }, + 'failAdjustments.$': { + type: AdjustmentSchema, + }, + // The buffs to be applied + failBuffs: { + type: Array, + defaultValue: [], + }, + 'failBuffs.$': { + type: StoredBuffWithIdSchema, + }, +}); + +export { SavingThrowSchema }; diff --git a/app/imports/api/properties/Skills.js b/app/imports/api/properties/Skills.js index 0f0dc5fc..cd9cb6d5 100644 --- a/app/imports/api/properties/Skills.js +++ b/app/imports/api/properties/Skills.js @@ -10,6 +10,7 @@ let SkillSchema = new SimpleSchema({ optional: true, }, // The technical, lowercase, single-word name used in formulae + // Ignored for skilltype = save variableName: { type: String, regEx: /^\w*[a-z]\w*$/i, diff --git a/app/imports/api/properties/propertySchemas.js b/app/imports/api/properties/propertySchemas.js index c36bd78f..4abfa65d 100644 --- a/app/imports/api/properties/propertySchemas.js +++ b/app/imports/api/properties/propertySchemas.js @@ -1,8 +1,7 @@ -import { CreatureSchema } from '/imports/api/creature/Creatures.js'; import { ActionSchema } from '/imports/api/properties/Actions.js'; +import { AttackSchema } from 'app/imports/api/properties/Attacks.js'; import { AttributeSchema } from '/imports/api/properties/Attributes.js'; import { StoredBuffSchema } from '/imports/api/properties/Buffs.js'; -import { ClassSchema } from '/imports/api/properties/Classes.js'; import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js'; import { EffectSchema } from '/imports/api/properties/Effects.js'; @@ -12,19 +11,19 @@ import { FolderSchema } from '/imports/api/properties/Folders.js'; import { NoteSchema } from '/imports/api/properties/Notes.js'; import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js'; import { RollSchema } from '/imports/api/properties/Rolls.js'; +import { RollResultSchema } from '/imports/api/properties/RollResult.js'; +import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; import { SkillSchema } from '/imports/api/properties/Skills.js'; import { SpellListSchema } from '/imports/api/properties/SpellLists.js'; import { SpellSchema } from '/imports/api/properties/Spells.js'; import { ContainerSchema } from '/imports/api/properties/Containers.js'; import { ItemSchema } from '/imports/api/properties/Items.js'; - const propertySchemas = { - creature: CreatureSchema, action: ActionSchema, + attack: AttackSchema, attribute: AttributeSchema, buff: StoredBuffSchema, - class: ClassSchema, classLevel: ClassLevelSchema, damageMultiplier: DamageMultiplierSchema, effect: EffectSchema, @@ -34,6 +33,8 @@ const propertySchemas = { note: NoteSchema, proficiency: ProficiencySchema, roll: RollSchema, + rollResult: RollResultSchema, + savingThrow: SavingThrowSchema, skill: SkillSchema, spellList: SpellListSchema, spell: SpellSchema, diff --git a/app/imports/ui/components/tree/TreeNode.vue b/app/imports/ui/components/tree/TreeNode.vue index 7205017a..763b6b6d 100644 --- a/app/imports/ui/components/tree/TreeNode.vue +++ b/app/imports/ui/components/tree/TreeNode.vue @@ -16,7 +16,7 @@ @click.stop="expanded = !expanded" :disabled="!hasChildren && !organize" > - chevron_right + chevron_right
+ {{node && node.order}} {{node && node.name}}
-
+
import draggable from 'vuedraggable'; import TreeNode from '/imports/ui/components/tree/TreeNode.vue'; + import { isParentAllowed } from '/imports/api/parenting/parenting.js'; + export default { components: { draggable, @@ -71,6 +74,13 @@ } } }, + move(evt){ + let parentNode = evt.relatedContext.component.$parent.node + let parentType = parentNode && parentNode.type || 'root'; + let childType = evt.draggedContext.element.node.type; + let allowed = isParentAllowed({parentType, childType}); + return allowed; + }, }, };