From e0f621cc44a2664bccf2aa19daca9481974842be Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 14 Feb 2022 16:26:49 +0200 Subject: [PATCH] Added data and UI for effects targeting calculations by tag Still need to: - update engine to compute calculations with effects. - Add UI for effects applied to each calculation --- app/.meteor/packages | 2 +- app/.meteor/release | 2 +- app/.meteor/versions | 16 +- .../buildComputation/linkTypeDependencies.js | 21 ++- app/imports/api/properties/Effects.js | 119 +++++++++---- .../ui/components/global/SmartSelect.vue | 4 + .../AddCreaturePropertyDialog.vue | 1 + .../ui/properties/forms/EffectForm.vue | 167 +++++++++++++++--- .../forms/shared/schemaFormMixin.js | 10 +- .../treeNodeViews/EffectTreeNode.vue | 31 +++- .../ui/properties/viewers/EffectViewer.vue | 46 ++++- 11 files changed, 336 insertions(+), 83 deletions(-) diff --git a/app/.meteor/packages b/app/.meteor/packages index 8a53403c..232dc1ac 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -11,7 +11,7 @@ accounts-google@1.4.0 email@2.2.0 meteor-base@1.5.1 mobile-experience@1.1.0 -mongo@1.13.0 +mongo@1.14.0 session@1.2.0 tracker@1.2.0 logging@1.3.1 diff --git a/app/.meteor/release b/app/.meteor/release index c2ff29ae..a19cd698 100644 --- a/app/.meteor/release +++ b/app/.meteor/release @@ -1 +1 @@ -METEOR@2.5.3 +METEOR@2.6 diff --git a/app/.meteor/versions b/app/.meteor/versions index e9557bec..1a35a30c 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -1,4 +1,4 @@ -accounts-base@2.2.0 +accounts-base@2.2.1 accounts-google@1.4.0 accounts-oauth@1.4.0 accounts-password@2.2.0 @@ -10,7 +10,7 @@ akryum:vue-component-dev-server@0.1.4 akryum:vue-router2@0.2.3 aldeed:collection2@3.5.0 aldeed:schema-index@3.0.0 -allow-deny@1.1.0 +allow-deny@1.1.1 autoupdate@1.8.0 babel-compiler@7.8.0 babel-runtime@1.5.0 @@ -25,7 +25,7 @@ callback-hook@1.4.0 check@1.3.1 coffeescript@2.4.1 coffeescript-compiler@2.4.1 -dburles:mongo-collection-instances@0.3.5 +dburles:mongo-collection-instances@0.3.6 ddp@1.4.0 ddp-client@2.5.0 ddp-common@1.4.0 @@ -59,20 +59,20 @@ meteor-base@1.5.1 meteortesting:browser-tests@1.3.5 meteortesting:mocha@2.0.3 meteortesting:mocha-core@8.1.2 -mikowals:batch-insert@1.2.0 +mikowals:batch-insert@1.3.0 minifier-css@1.6.0 minifier-js@2.7.3 -minimongo@1.7.0 +minimongo@1.8.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 modern-browsers@0.1.7 modules@0.18.0 modules-runtime@0.12.0 -mongo@1.13.0 +mongo@1.14.4 mongo-decimal@0.1.2 mongo-dev-server@1.1.0 mongo-id@1.0.8 -npm-mongo@3.9.1 +npm-mongo@4.3.1 oauth@2.1.1 oauth2@1.3.1 ordered-dict@1.1.0 @@ -85,7 +85,7 @@ peerlibrary:computed-field@0.10.0 peerlibrary:data-lookup@0.3.0 peerlibrary:extend-publish@0.6.0 peerlibrary:fiber-utils@0.10.0 -peerlibrary:reactive-mongo@0.4.0 +peerlibrary:reactive-mongo@0.4.1 peerlibrary:reactive-publish@0.10.0 peerlibrary:server-autorun@0.8.0 peerlibrary:subscription-data@0.8.0 diff --git a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js index 2d11ae29..2e1b29ea 100644 --- a/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js +++ b/app/imports/api/engine/computation/buildComputation/linkTypeDependencies.js @@ -122,14 +122,25 @@ function linkDamage(dependencyGraph, prop){ dependOnCalc({dependencyGraph, prop, key: 'amount'}); } -function linkEffects(dependencyGraph, prop){ +function linkEffects(dependencyGraph, prop, computation){ // The effect depends on its amount calculation dependOnCalc({dependencyGraph, prop, key: 'amount'}); // The stats depend on the effect - prop.stats.forEach(statName => { - if (!statName) return; - dependencyGraph.addLink(statName, prop._id, 'effect'); - }); + if (prop.targetByTags){ + // TODO: + getEffectTagTargets(prop, computation).forEach(targetProp => { + const key = prop.targetField || getDefaultCalculationField(targetProp); + const calcObj = get(targetProp, key); + if (calcObj){ + dependencyGraph.addLink(`${targetProp._id}.${key}`, prop._id , 'effect'); + } + }); + } else { + prop.stats.forEach(statName => { + if (!statName) return; + dependencyGraph.addLink(statName, prop._id, 'effect'); + }); + } } function linkRoll(dependencyGraph, prop){ diff --git a/app/imports/api/properties/Effects.js b/app/imports/api/properties/Effects.js index 542d344d..e1ba0c37 100644 --- a/app/imports/api/properties/Effects.js +++ b/app/imports/api/properties/Effects.js @@ -7,48 +7,99 @@ import createPropertySchema from '/imports/api/properties/subSchemas/createPrope * that modify their final value or presentation in some way */ let EffectSchema = createPropertySchema({ - name: { - type: String, - optional: true, + name: { + type: String, + optional: true, max: STORAGE_LIMITS.name, - }, - operation: { - type: String, - defaultValue: 'add', - allowedValues: [ - 'base', - 'add', - 'mul', - 'min', - 'max', + }, + operation: { + type: String, + defaultValue: 'add', + allowedValues: [ + 'base', + 'add', + 'mul', + 'min', + 'max', 'set', - 'advantage', - 'disadvantage', - 'passiveAdd', - 'fail', - 'conditional', - ], - }, - amount: { - type: 'fieldToCompute', - optional: true, - }, + 'advantage', + 'disadvantage', + 'passiveAdd', + 'fail', + 'conditional', + ], + }, + amount: { + type: 'fieldToCompute', + optional: true, + }, // Conditional benefits store just uncomputed text text: { type: String, optional: true, max: STORAGE_LIMITS.effectCondition, }, - //which stats the effect is applied to - stats: { - type: Array, - defaultValue: [], + // Which stats the effect is applied to + // Each entry is a variableName targeted by this effect + stats: { + type: Array, + defaultValue: [], maxCount: STORAGE_LIMITS.statsToTarget, - }, - 'stats.$': { - type: String, + }, + 'stats.$': { + type: String, max: STORAGE_LIMITS.variableName, - }, + }, + // True when targeting by tags instead of stats + targetByTags: { + type: Boolean, + optional: true, + }, + // If targeting by tags, the field which will be targeted + targetField: { + type: String, + optional: true, + max: STORAGE_LIMITS.variableName, + }, + // Which tags the effect is applied to + targetTags: { + type: Array, + optional: true, + maxCount: STORAGE_LIMITS.tagCount, + }, + 'targetTags.$': { + type: String, + max: STORAGE_LIMITS.tagLength, + }, + extraTags: { + type: Array, + optional: true, + maxCount: STORAGE_LIMITS.extraTagsCount, + }, + 'extraTags.$': { + type: Object, + }, + 'extraTags.$._id': { + type: String, + regEx: SimpleSchema.RegEx.Id, + autoValue(){ + if (!this.isSet) return Random.id(); + } + }, + 'extraTags.$.operation': { + type: String, + allowedValues: ['OR', 'NOT'], + defaultValue: 'OR', + }, + 'extraTags.$.tags': { + type: Array, + defaultValue: [], + maxCount: STORAGE_LIMITS.tagCount, + }, + 'extraTags.$.tags.$': { + type: String, + max: STORAGE_LIMITS.tagLength, + }, }); const ComputedOnlyEffectSchema = createPropertySchema({ @@ -59,7 +110,7 @@ const ComputedOnlyEffectSchema = createPropertySchema({ }); const ComputedEffectSchema = new SimpleSchema() - .extend(ComputedOnlyEffectSchema) - .extend(EffectSchema); + .extend(ComputedOnlyEffectSchema) + .extend(EffectSchema); export { EffectSchema, ComputedEffectSchema, ComputedOnlyEffectSchema }; diff --git a/app/imports/ui/components/global/SmartSelect.vue b/app/imports/ui/components/global/SmartSelect.vue index 8b48ad6e..439f735b 100644 --- a/app/imports/ui/components/global/SmartSelect.vue +++ b/app/imports/ui/components/global/SmartSelect.vue @@ -15,6 +15,10 @@ slot="prepend" name="prepend" /> + diff --git a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue index 9ca7838a..133d713b 100644 --- a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue +++ b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue @@ -44,6 +44,7 @@ {{ displayedIcon }} @@ -37,18 +36,6 @@ {{ item.item.text }} - + + + Target stats by variable name + + + Target properties by tag + + + + + + + mdi-plus + + + + + +
+ + + + mdi-delete + +
+
+ + + + + + @@ -85,13 +179,19 @@ import getEffectIcon from '/imports/ui/utility/getEffectIcon.js'; import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js'; import attributeListMixin from '/imports/ui/properties/forms/shared/lists/attributeListMixin.js'; + import { EffectSchema } from '/imports/api/properties/Effects.js'; + import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue'; const ICON_SPIN_DURATION = 300; export default { + components: { + FormSection, + }, mixins: [propertyFormMixin, attributeListMixin], data(){ return { displayedIcon: 'add', iconClass: '', + addExtraTagsLoading: false, operations: [ {value: 'base', text: 'Base Value'}, {value: 'add', text: 'Add'}, @@ -107,6 +207,14 @@ ], }}, computed: { + radioGroup(){ + return this.model.targetByTags ? 'tags' : 'stats'; + }, + extraTagsFull(){ + if (!this.model.extraTags) return false; + let maxCount = EffectSchema.get('extraTags', 'maxCount'); + return this.model.extraTags.length >= maxCount; + }, needsValue(){ switch(this.model.operation) { case 'base': return true; @@ -163,6 +271,25 @@ }, methods: { getEffectIcon, + changeTargetByTags(value){ + if(value === 'stats'){ + this.$emit('change', {path: ['targetByTags'], value: undefined}); + } else if (value === 'tags'){ + this.$emit('change', {path: ['targetByTags'], value: true}); + } + }, + addExtraTags(){ + this.addExtraTagsLoading = true; + this.$emit('push', { + path: ['extraTags'], + value: { + _id: Random.id(), + operation: 'OR', + tags: [], + }, + ack: () => this.addExtraTagsLoading = false, + }); + }, } }; diff --git a/app/imports/ui/properties/forms/shared/schemaFormMixin.js b/app/imports/ui/properties/forms/shared/schemaFormMixin.js index 2cb8b332..e1d3a349 100644 --- a/app/imports/ui/properties/forms/shared/schemaFormMixin.js +++ b/app/imports/ui/properties/forms/shared/schemaFormMixin.js @@ -57,10 +57,14 @@ const schemaFormMixin = { }, push({path, value, ack}){ let array = get(this.model, path); - if (!array || !array.join){ - throw `${path.join('.')} is ${array}, doesn't have "join"` + if (array === undefined){ + let {object, key} = resolvePath(this.model, path, this.$set); + this.$set(object, key, [value]); + } else if (!array.push){ + throw `${path.join('.')} is ${array}, doesn't have "push"` + } else { + array.push(value); } - array.push(value); if (ack) ack(); }, pull({path, ack}){ diff --git a/app/imports/ui/properties/treeNodeViews/EffectTreeNode.vue b/app/imports/ui/properties/treeNodeViews/EffectTreeNode.vue index d56a48f0..60d22b14 100644 --- a/app/imports/ui/properties/treeNodeViews/EffectTreeNode.vue +++ b/app/imports/ui/properties/treeNodeViews/EffectTreeNode.vue @@ -14,15 +14,21 @@ + @@ -47,19 +53,32 @@ export default { displayedValue(){ let value = this.resolvedValue; switch(this.model.operation) { - case 'base': return value; - case 'add': return isFinite(value) ? Math.abs(value) : value; + case 'base': return value || 0; + case 'add': return isFinite(value) ? Math.abs(value) : value || 0; case 'mul': return value; case 'min': return value; case 'max': return value; case 'advantage': return; case 'disadvantage': return; - case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value; + case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value || 0; case 'fail': return; case 'conditional': return; default: return undefined; } }, + displayedStats(){ + if (!this.model.stats) return 'Selected stats'; + return this.model.stats.join(', '); + }, + displayedTags(){ + if (!this.model.targetTags) return 'Selected tags'; + const tags = this.model.targetTags.join(', '); + if (!this.model.extraTags) return tags; + const extraTags = this.model.extraTags.map(ex => { + return ` ${ex.operation} ${ex.tags.join(', ')}` + }).join(' '); + return tags + extraTags; + } } } diff --git a/app/imports/ui/properties/viewers/EffectViewer.vue b/app/imports/ui/properties/viewers/EffectViewer.vue index 64159818..dadcde7f 100644 --- a/app/imports/ui/properties/viewers/EffectViewer.vue +++ b/app/imports/ui/properties/viewers/EffectViewer.vue @@ -17,18 +17,54 @@ +
+ + {{ tag }} + +
+ + {{ ex.operation }} + + + {{ extraTag }} + +
+
+
+ + > + + {{ stat }} + +