Added slotfiller property type to increase control over slot filling

This commit is contained in:
Stefan Zermatten
2020-10-15 14:54:58 +02:00
parent 8e9405b5ad
commit ed17d9e2d2
10 changed files with 214 additions and 16 deletions

View File

@@ -0,0 +1,38 @@
// SlotFiller fillers specifically fill a slot with a bit more control than
// other properties
import SimpleSchema from 'simpl-schema';
let SlotFillerSchema = new SimpleSchema({
name: {
type: String,
optional: true,
},
picture: {
type: String,
optional: true,
},
description: {
type: String,
optional: true,
},
// Overrides the type when searching for properties
slotFillerType: {
type: String,
optional: true,
},
// Fill more than one quantity in a slot, like feats and ability score
// improvements, filtered out of UI if there isn't space in quantityExpected
slotQuantityFilled: {
type: SimpleSchema.Integer,
defaultValue: 1,
min: 0,
},
// Filters out of UI if condition isn't met, but isn't otherwise enforced
slotFillerCondition: {
type: String,
optional: true,
},
});
export { SlotFillerSchema };

View File

@@ -18,6 +18,7 @@ import { RollSchema } from '/imports/api/properties/Rolls.js';
import { ComputedSavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
import { ComputedSkillSchema } from '/imports/api/properties/Skills.js';
import { ComputedSlotSchema } from '/imports/api/properties/Slots.js';
import { SlotFillerSchema } from '/imports/api/properties/SlotFillers.js';
import { ComputedSpellSchema } from '/imports/api/properties/Spells.js';
import { ComputedSpellListSchema } from '/imports/api/properties/SpellLists.js';
import { ToggleSchema } from '/imports/api/properties/Toggles.js';
@@ -36,10 +37,11 @@ const propertySchemasIndex = {
folder: FolderSchema,
note: NoteSchema,
proficiency: ProficiencySchema,
propertySlot: ComputedSlotSchema,
roll: RollSchema,
savingThrow: ComputedSavingThrowSchema,
skill: ComputedSkillSchema,
propertySlot: ComputedSlotSchema,
slotFiller: SlotFillerSchema,
spellList: ComputedSpellListSchema,
spell: ComputedSpellSchema,
toggle: ToggleSchema,

View File

@@ -16,6 +16,7 @@ import { RollSchema } from '/imports/api/properties/Rolls.js';
import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js';
import { SkillSchema } from '/imports/api/properties/Skills.js';
import { SlotSchema } from '/imports/api/properties/Slots.js';
import { SlotFillerSchema } from '/imports/api/properties/SlotFillers.js';
import { SpellListSchema } from '/imports/api/properties/SpellLists.js';
import { SpellSchema } from '/imports/api/properties/Spells.js';
import { ToggleSchema } from '/imports/api/properties/Toggles.js';
@@ -36,10 +37,11 @@ const propertySchemasIndex = {
folder: FolderSchema,
note: NoteSchema,
proficiency: ProficiencySchema,
propertySlot: SlotSchema,
roll: RollSchema,
savingThrow: SavingThrowSchema,
skill: SkillSchema,
propertySlot: SlotSchema,
slotFiller: SlotFillerSchema,
spellList: SpellListSchema,
spell: SpellSchema,
toggle: ToggleSchema,

View File

@@ -75,6 +75,10 @@ const PROPERTIES = Object.freeze({
icon: 'tab_unselected',
name: 'Slot'
},
slotFiller: {
icon: 'picture_in_picture',
name: 'Slot filler'
},
spellList: {
icon: '$vuetify.icons.spell_list',
name: 'Spell list'

View File

@@ -33,10 +33,17 @@ Meteor.publish('slotFillers', function(slotId){
// Build a filter for nodes in those libraries that match the slot
let filter = {
'ancestors.id': {$in: libraryIds},
'tags': {$all: slot.slotTags},
};
if (slot.tags.length){
filter.tags = {$all: slot.slotTags};
}
if (slot.slotType){
filter.type = slot.slotType;
filter.$or = [{
type: slot.slotType
},{
type: 'slotFiller',
slotFillerType: slot.slotType,
}];
}
return LibraryNodes.find(filter);
});

View File

@@ -73,11 +73,14 @@
</template>
<script>
import Creatures from '/imports/api/creature/Creatures.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import { parse, CompilationContext } from '/imports/parser/parser.js';
export default {
components: {
DialogBase,
@@ -87,7 +90,15 @@ export default {
slotId: {
type: String,
required: true,
}
},
creatureId: {
type: String,
required: true,
},
numToFill: {
type: Number,
required: true,
},
},
data(){return {
selectedNode: undefined,
@@ -109,14 +120,41 @@ export default {
model(){
return CreatureProperties.findOne(this.slotId);
},
creature(){
return Creatures.findOne(this.creatureId);
},
libraryNodes(){
let filter = {
tags: {$all: this.model.slotTags},
};
let filter = {};
if (this.model.tags.length){
filter.tags = {$all: this.model.slotTags};
}
if (this.model.slotType){
filter.type = this.model.slotType;
filter.$or = [{
type: this.model.slotType
},{
type: 'slotFiller',
slotFillerType: this.model.slotType,
}];
}
let nodes = LibraryNodes.find(filter).fetch();
// Filter out slotFillers whose condition isn't met or are too big to fit
// the quantity to fill
nodes = nodes.filter(node => {
if (node.type === 'slotFiller'){
if (node.slotFillerCondition){
let context = new CompilationContext();
let conditionResult = parse(node.slotFillerCondition)
.reduce(this.creature.variables, context);
if (conditionResult && !conditionResult.value) return false;
}
console.log({numToFill: this.numToFill, node});
if (this.numToFill > 0 && node.slotQuantityFilled > this.numToFill){
return false;
}
}
return true;
});
// Filter out slotFillers whose
if (nodes.length === 1) this.selectedNode = nodes[0];
return nodes;
},

View File

@@ -9,7 +9,7 @@
{{ slot.name }}
<v-spacer />
<span v-if="slot.quantityExpected > 1">
{{ slot.children.length }} / {{ slot.quantityExpected }}
{{ slot.totalFilled }} / {{ slot.quantityExpected }}
</span>
</h3>
<div
@@ -33,11 +33,11 @@
</v-btn>
</div>
<v-btn
v-if="!slot.quantityExpected || slot.quantityExpected > slot.children.length"
v-if="!slot.quantityExpected || slot.quantityExpected > slot.totalFilled"
icon
:data-id="`slot-add-button-${slot._id}`"
style="background-color: inherit;"
@click="fillSlot(slot._id)"
@click="fillSlot(slot)"
>
<v-icon>add</v-icon>
</v-btn>
@@ -64,11 +64,19 @@ export default {
},
},
methods: {
fillSlot(slotId){
fillSlot(slot){
let slotId = slot._id;
let creatureId = this.creatureId;
let numToFill = slot.quantityExpected === 0 ?
0 : slot.quantityExpected - slot.totalFilled;
this.$store.commit('pushDialogStack', {
component: 'slot-fill-dialog',
elementId: `slot-add-button-${slotId}`,
data: {slotId},
data: {
slotId,
creatureId,
numToFill,
},
callback(node){
if(!node) return;
let newPropertyId = insertPropertyFromLibraryNode.call({
@@ -105,6 +113,14 @@ export default {
'parent.id': slot._id,
removed: {$ne: true},
}).fetch();
slot.totalFilled = 0;
slot.children.forEach(child => {
if (child.type === 'slotFiller'){
slot.totalFilled += child.slotQuantityFilled;
} else {
slot.totalFilled++;
}
});
return slot;
});
},

View File

@@ -0,0 +1,88 @@
<template lang="html">
<div class="slot-filler-form">
<div class="layout column align-center">
<icon-picker
label="Icon"
:value="model.icon"
:error-messages="errors.icon"
@change="change('icon', ...arguments)"
/>
</div>
<text-field
ref="focusFirst"
label="Name"
:value="model.name"
:error-messages="errors.name"
@change="change('name', ...arguments)"
/>
<text-field
label="Picture URL"
hint="A link to an image representing this property"
:value="model.picture"
:error-messages="errors.picture"
@change="change('picture', ...arguments)"
/>
<smart-select
label="Type"
style="flex-basis: 300px;"
clearable
:items="slotTypes"
:value="model.slotFillerType"
:error-messages="errors.slotFillerType"
@change="change('slotFillerType', ...arguments)"
/>
<smart-combobox
label="Tags"
multiple
chips
deletable-chips
:value="model.tags"
:error-messages="errors.tags"
@change="change('tags', ...arguments)"
/>
<text-field
label="Quantity"
type="number"
min="0"
hint="How many properties this counts as when filling a slot"
:value="model.slotQuantityFilled"
:error-messages="errors.slotQuantityFilled"
@change="change('slotQuantityFilled', ...arguments)"
/>
<text-field
label="Condition"
hint="A caclulation to determine if this can be added to the character"
placeholder="Always active"
:value="model.slotFillerCondition"
:error-messages="errors.slotFillerCondition"
@change="change('slotFillerCondition', ...arguments)"
/>
<calculation-error-list :errors="model.slotConditionErrors" />
<text-area
label="Description"
:value="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
/>
</div>
</template>
<script>
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
import PROPERTIES from '/imports/constants/PROPERTIES.js';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
data(){
let slotTypes = [];
for (let key in PROPERTIES){
slotTypes.push({text: PROPERTIES[key].name, value: key});
}
return {slotTypes};
},
};
</script>

View File

@@ -1,5 +1,5 @@
<template lang="html">
<div class="spell-form">
<div class="slot-form">
<text-field
ref="focusFirst"
label="Name"
@@ -10,6 +10,7 @@
<smart-select
label="Type"
style="flex-basis: 300px;"
clearable
:items="slotTypes"
:value="model.slotType"
:error-messages="errors.slotType"

View File

@@ -17,6 +17,7 @@ import RollForm from '/imports/ui/properties/forms/RollForm.vue';
import SavingThrowForm from '/imports/ui/properties/forms/SavingThrowForm.vue';
import SkillForm from '/imports/ui/properties/forms/SkillForm.vue';
import SlotForm from '/imports/ui/properties/forms/SlotForm.vue';
import SlotFillerForm from '/imports/ui/properties/forms/SlotFillerForm.vue';
import SpellListForm from '/imports/ui/properties/forms/SpellListForm.vue';
import SpellForm from '/imports/ui/properties/forms/SpellForm.vue';
import ToggleForm from '/imports/ui/properties/forms/ToggleForm.vue';
@@ -37,10 +38,11 @@ export default {
item: ItemForm,
note: NoteForm,
proficiency: ProficiencyForm,
propertySlot: SlotForm,
roll: RollForm,
savingThrow: SavingThrowForm,
skill: SkillForm,
propertySlot: SlotForm,
slotFiller: SlotFillerForm,
spellList: SpellListForm,
spell: SpellForm,
toggle: ToggleForm,