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"
>
-