Started restructuring the library with attacks, saves, and limited parenting

This commit is contained in:
Stefan Zermatten
2019-08-12 16:42:30 +02:00
parent 6f4710bee3
commit a8b3fc3f2f
14 changed files with 204 additions and 91 deletions

View File

@@ -1,7 +1,6 @@
import Actions from '/imports/api/properties/Actions.js'; import Actions from '/imports/api/properties/Actions.js';
import Attributes from '/imports/api/properties/Attributes.js'; import Attributes from '/imports/api/properties/Attributes.js';
import Buffs from '/imports/api/properties/Buffs.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 ClassLevels from '/imports/api/properties/ClassLevels.js';
import DamageMultipliers from '/imports/api/properties/DamageMultipliers.js'; import DamageMultipliers from '/imports/api/properties/DamageMultipliers.js';
import Effects from '/imports/api/properties/Effects.js'; import Effects from '/imports/api/properties/Effects.js';
@@ -25,7 +24,6 @@ let creatureCollections = [
Actions, Actions,
Attributes, Attributes,
Buffs, Buffs,
Classes,
ClassLevels, ClassLevels,
DamageMultipliers, DamageMultipliers,
Effects, Effects,

View File

@@ -10,7 +10,6 @@ import Skills from "/imports/api/properties/Skills.js";
import Effects from "/imports/api/properties/Effects.js"; import Effects from "/imports/api/properties/Effects.js";
import Proficiencies from "/imports/api/properties/Proficiencies.js"; import Proficiencies from "/imports/api/properties/Proficiencies.js";
import DamageMultipliers from "/imports/api/properties/DamageMultipliers.js"; import DamageMultipliers from "/imports/api/properties/DamageMultipliers.js";
import Classes from "/imports/api/properties/Classes.js";
import * as math from 'mathjs'; import * as math from 'mathjs';
import parser from '/imports/parser/parser.js'; import parser from '/imports/parser/parser.js';
if (Meteor.isClient) console.log({parser}); if (Meteor.isClient) console.log({parser});

View File

@@ -5,7 +5,6 @@ import DamageMultipliers from '/imports/api/properties/DamageMultipliers.js';
import Effects from '/imports/api/properties/Effects.js'; import Effects from '/imports/api/properties/Effects.js';
import Containers from '/imports/api/properties/Containers.js'; import Containers from '/imports/api/properties/Containers.js';
import Items from '/imports/api/properties/Items.js'; import Items from '/imports/api/properties/Items.js';
import Classes from '/imports/api/properties/Classes.js';
const addDefaultDocs = function(docs){ const addDefaultDocs = function(docs){
Attributes.rawCollection().insert(docs.attributes, {ordered: false}); Attributes.rawCollection().insert(docs.attributes, {ordered: false});
@@ -14,7 +13,6 @@ const addDefaultDocs = function(docs){
Effects.rawCollection().insert(docs.effects, {ordered: false}); Effects.rawCollection().insert(docs.effects, {ordered: false});
Containers.rawCollection().insert(docs.containers, {ordered: false}); Containers.rawCollection().insert(docs.containers, {ordered: false});
Items.rawCollection().insert(docs.items, {ordered: false}); Items.rawCollection().insert(docs.items, {ordered: false});
Classes.rawCollection().insert(docs.classes, {ordered: false});
}; };
const insertCreature = new ValidatedMethod({ const insertCreature = new ValidatedMethod({

View File

@@ -2,7 +2,6 @@ import SimpleSchema from 'simpl-schema';
import { ActionSchema } from '/imports/api/properties/Actions.js'; import { ActionSchema } from '/imports/api/properties/Actions.js';
import { AttributeSchema } from '/imports/api/properties/Attributes.js'; import { AttributeSchema } from '/imports/api/properties/Attributes.js';
import { StoredBuffSchema } from '/imports/api/properties/Buffs.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 { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js';
import { EffectSchema } from '/imports/api/properties/Effects.js'; import { EffectSchema } from '/imports/api/properties/Effects.js';
@@ -22,7 +21,6 @@ const librarySchemas = {
action: ActionSchema, action: ActionSchema,
attribute: AttributeSchema, attribute: AttributeSchema,
buff: StoredBuffSchema, buff: StoredBuffSchema,
class: ClassSchema,
classLevel: ClassLevelSchema, classLevel: ClassLevelSchema,
damageMultiplier: DamageMultiplierSchema, damageMultiplier: DamageMultiplierSchema,
effect: EffectSchema, effect: EffectSchema,

View File

@@ -1,5 +1,41 @@
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
import getCollectionByName from '/imports/api/parenting/getCollectionByName.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}){ export function fetchParent({id, collection}){
return fetchDocByRef({id, collection}); return fetchDocByRef({id, collection});
@@ -55,7 +91,7 @@ export function getAncestry({parentRef, inheritedFields = {}}){
let ancestors = parentDoc.ancestors || []; let ancestors = parentDoc.ancestors || [];
ancestors.push(parent); ancestors.push(parent);
return {parent, ancestors}; return {parentDoc, parent, ancestors};
} }
export function updateParent({docRef, parentRef}){ export function updateParent({docRef, parentRef}){
@@ -63,14 +99,30 @@ export function updateParent({docRef, parentRef}){
let oldDoc = fetchDocByRef(docRef, {fields: { let oldDoc = fetchDocByRef(docRef, {fields: {
parent: 1, parent: 1,
ancestors: 1, ancestors: 1,
type: 1,
}}); }});
let updateOptions = { selector: {type: 'any'} }; let updateOptions = { selector: {type: 'any'} };
// Skip if we aren't changing the parent id // Skip if we aren't changing the parent id
if (oldDoc.parent.id === parentRef.id) return; 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 // update the document's parenting
let {parent, ancestors} = getAncestry({parentRef});
collection.update(docRef.id, { collection.update(docRef.id, {
$set: {parent, ancestors} $set: {parent, ancestors}
}, updateOptions); }, updateOptions);

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -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 };

View File

@@ -1,25 +1,4 @@
import SimpleSchema from 'simpl-schema'; 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 * 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 * child rolls are applied
*/ */
let RollSchema = new SimpleSchema({ let RollSchema = new SimpleSchema({
// The roll made against the target value. A calculation that resolves to a // The number to add to a d20 roll
// number or a roll. If it is a number, it will be added to a d20 roll rollBonus: {
roll: {
type: String, type: String,
optional: true, 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 // Effects can apply to this tag specifically
// Ranged spell attack, Ranged weapon attack, etc. // Ranged spell attack, Ranged weapon attack, etc.
tags: { tags: {
@@ -77,15 +33,6 @@ let RollSchema = new SimpleSchema({
'tags.$': { 'tags.$': {
type: String, 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 }; export { RollSchema };

View File

@@ -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 };

View File

@@ -10,6 +10,7 @@ let SkillSchema = new SimpleSchema({
optional: true, optional: true,
}, },
// The technical, lowercase, single-word name used in formulae // The technical, lowercase, single-word name used in formulae
// Ignored for skilltype = save
variableName: { variableName: {
type: String, type: String,
regEx: /^\w*[a-z]\w*$/i, regEx: /^\w*[a-z]\w*$/i,

View File

@@ -1,8 +1,7 @@
import { CreatureSchema } from '/imports/api/creature/Creatures.js';
import { ActionSchema } from '/imports/api/properties/Actions.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 { AttributeSchema } from '/imports/api/properties/Attributes.js';
import { StoredBuffSchema } from '/imports/api/properties/Buffs.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 { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js';
import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js'; import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js';
import { EffectSchema } from '/imports/api/properties/Effects.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 { NoteSchema } from '/imports/api/properties/Notes.js';
import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js'; import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js';
import { RollSchema } from '/imports/api/properties/Rolls.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 { SkillSchema } from '/imports/api/properties/Skills.js';
import { SpellListSchema } from '/imports/api/properties/SpellLists.js'; import { SpellListSchema } from '/imports/api/properties/SpellLists.js';
import { SpellSchema } from '/imports/api/properties/Spells.js'; import { SpellSchema } from '/imports/api/properties/Spells.js';
import { ContainerSchema } from '/imports/api/properties/Containers.js'; import { ContainerSchema } from '/imports/api/properties/Containers.js';
import { ItemSchema } from '/imports/api/properties/Items.js'; import { ItemSchema } from '/imports/api/properties/Items.js';
const propertySchemas = { const propertySchemas = {
creature: CreatureSchema,
action: ActionSchema, action: ActionSchema,
attack: AttackSchema,
attribute: AttributeSchema, attribute: AttributeSchema,
buff: StoredBuffSchema, buff: StoredBuffSchema,
class: ClassSchema,
classLevel: ClassLevelSchema, classLevel: ClassLevelSchema,
damageMultiplier: DamageMultiplierSchema, damageMultiplier: DamageMultiplierSchema,
effect: EffectSchema, effect: EffectSchema,
@@ -34,6 +33,8 @@ const propertySchemas = {
note: NoteSchema, note: NoteSchema,
proficiency: ProficiencySchema, proficiency: ProficiencySchema,
roll: RollSchema, roll: RollSchema,
rollResult: RollResultSchema,
savingThrow: SavingThrowSchema,
skill: SkillSchema, skill: SkillSchema,
spellList: SpellListSchema, spellList: SpellListSchema,
spell: SpellSchema, spell: SpellSchema,

View File

@@ -16,7 +16,7 @@
@click.stop="expanded = !expanded" @click.stop="expanded = !expanded"
:disabled="!hasChildren && !organize" :disabled="!hasChildren && !organize"
> >
<v-icon v-if="hasChildren || organize">chevron_right</v-icon> <v-icon v-if="canExpand && (hasChildren || organize)">chevron_right</v-icon>
</v-btn> </v-btn>
<div <div
class="layout row align-center justify-start" class="layout row align-center justify-start"
@@ -35,13 +35,15 @@
:class="selected && 'primary--text'" :class="selected && 'primary--text'"
/> />
<div class="text-no-wrap text-truncate"> <div class="text-no-wrap text-truncate">
{{node && node.order}}
{{node && node.name}} {{node && node.name}}
</div> </div>
</div> </div>
</div> </div>
<v-expand-transition> <v-expand-transition>
<div v-if="showExpanded"> <div v-if="showExpanded" class="pl-3">
<tree-node-list <tree-node-list
v-if="showExpanded"
:node="node" :node="node"
:children="computedChildren" :children="computedChildren"
:group="group" :group="group"
@@ -64,6 +66,8 @@
* character comes from. * character comes from.
**/ **/
import PropertyIcon from '/imports/ui/properties/PropertyIcon.vue'; import PropertyIcon from '/imports/ui/properties/PropertyIcon.vue';
import { canBeParent } from '/imports/api/parenting/parenting.js';
export default { export default {
name: 'tree-node', name: 'tree-node',
beforeCreate() { beforeCreate() {
@@ -101,6 +105,9 @@
} }
return children; return children;
}, },
canExpand(){
return canBeParent(this.node.type);
}
}, },
methods: { methods: {
icon(type){ icon(type){
@@ -115,9 +122,9 @@
transform: rotate(90deg); transform: rotate(90deg);
} }
.drag-area { .drag-area {
min-height: 40px;
box-shadow: -2px 0px 0px 0px #808080; box-shadow: -2px 0px 0px 0px #808080;
margin-left: 23px; margin-left: 0;
min-height: 32px;
} }
.handle { .handle {
cursor: move; cursor: move;

View File

@@ -1,10 +1,11 @@
<template lang="html"> <template lang="html">
<draggable <draggable
:value="children"
class="drag-area" class="drag-area"
@change="change" :value="children"
:group="group" :group="group"
:animation="200" :animation="200"
:move="move"
@change="change"
ghost-class="ghost" ghost-class="ghost"
draggable=".item" draggable=".item"
handle=".handle" handle=".handle"
@@ -31,6 +32,8 @@
<script> <script>
import draggable from 'vuedraggable'; import draggable from 'vuedraggable';
import TreeNode from '/imports/ui/components/tree/TreeNode.vue'; import TreeNode from '/imports/ui/components/tree/TreeNode.vue';
import { isParentAllowed } from '/imports/api/parenting/parenting.js';
export default { export default {
components: { components: {
draggable, 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;
},
}, },
}; };
</script> </script>