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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
METEOR@2.5.3
|
||||
METEOR@2.6
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
slot="prepend"
|
||||
name="prepend"
|
||||
/>
|
||||
<slot
|
||||
slot="prepend-inner"
|
||||
name="prepend-inner"
|
||||
/>
|
||||
</v-select>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
<v-tabs-items
|
||||
slot="unwrapped-content"
|
||||
v-model="tab"
|
||||
class="fill-height overflow-y-auto"
|
||||
>
|
||||
<v-tab-item :disabled="!!forcedType">
|
||||
<property-selector
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
<smart-select
|
||||
label="Operation"
|
||||
append-icon="mdi-menu-down"
|
||||
class="mx-2"
|
||||
:hint="operationHint"
|
||||
:error-messages="errors.operation"
|
||||
:menu-props="{transition: 'slide-y-transition', lazy: true}"
|
||||
@@ -19,8 +18,8 @@
|
||||
@change="change('operation', ...arguments)"
|
||||
>
|
||||
<v-icon
|
||||
slot="prepend"
|
||||
class="icon"
|
||||
slot="prepend-inner"
|
||||
class="icon ml-0"
|
||||
:class="iconClass"
|
||||
>
|
||||
{{ displayedIcon }}
|
||||
@@ -37,18 +36,6 @@
|
||||
{{ item.item.text }}
|
||||
</template>
|
||||
</smart-select>
|
||||
<smart-combobox
|
||||
label="Stats"
|
||||
class="mr-2"
|
||||
multiple
|
||||
chips
|
||||
deletable-chips
|
||||
hint="Which stats will this effect apply to"
|
||||
:value="model.stats"
|
||||
:items="attributeList"
|
||||
:error-messages="errors.stats"
|
||||
@change="change('stats', ...arguments)"
|
||||
/>
|
||||
<text-field
|
||||
v-if="model.operation === 'conditional'"
|
||||
label="Text"
|
||||
@@ -60,7 +47,6 @@
|
||||
<computed-field
|
||||
v-else
|
||||
label="Value"
|
||||
class="mr-2"
|
||||
hint="Number or calculation to determine the value of this effect"
|
||||
:persistent-hint="needsValue"
|
||||
:disabled="!needsValue"
|
||||
@@ -69,15 +55,123 @@
|
||||
@change="({path, value, ack}) =>
|
||||
$emit('change', {path: ['amount', ...path], value, ack})"
|
||||
/>
|
||||
<v-btn-toggle
|
||||
mandatory
|
||||
:value="radioGroup"
|
||||
class="ma-2 mb-8"
|
||||
@change="changeTargetByTags"
|
||||
>
|
||||
<v-btn value="stats">
|
||||
Target stats by variable name
|
||||
</v-btn>
|
||||
<v-btn value="tags">
|
||||
Target properties by tag
|
||||
</v-btn>
|
||||
</v-btn-toggle>
|
||||
|
||||
<smart-combobox
|
||||
label="Tags"
|
||||
v-if="!model.targetByTags"
|
||||
label="Stats"
|
||||
class="mr-2"
|
||||
multiple
|
||||
chips
|
||||
deletable-chips
|
||||
:value="model.tags"
|
||||
:error-messages="errors.tags"
|
||||
@change="change('tags', ...arguments)"
|
||||
hint="Which stats will this effect apply to"
|
||||
persistent-hint
|
||||
:value="model.stats"
|
||||
:items="attributeList"
|
||||
:error-messages="errors.stats"
|
||||
@change="change('stats', ...arguments)"
|
||||
/>
|
||||
<v-layout
|
||||
v-if="model.targetByTags"
|
||||
align-center
|
||||
>
|
||||
<v-btn
|
||||
icon
|
||||
style="margin-top: -30px;"
|
||||
class="mr-2"
|
||||
:loading="addExtraTagsLoading"
|
||||
:disabled="extraTagsFull"
|
||||
@click="addExtraTags"
|
||||
>
|
||||
<v-icon>
|
||||
mdi-plus
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
<smart-combobox
|
||||
label="Tags Required"
|
||||
hint="The effect will apply to properties that have all the listed tags"
|
||||
multiple
|
||||
chips
|
||||
deletable-chips
|
||||
persistent-hint
|
||||
:value="model.targetTags"
|
||||
:error-messages="errors.targetTags"
|
||||
@change="change('targetTags', ...arguments)"
|
||||
/>
|
||||
</v-layout>
|
||||
<v-slide-x-transition
|
||||
v-if="model.targetByTags"
|
||||
group
|
||||
>
|
||||
<div
|
||||
v-for="(extras, i) in model.extraTags"
|
||||
:key="extras._id"
|
||||
class="target-tags layout align-center justify-space-between"
|
||||
>
|
||||
<smart-select
|
||||
label="Operation"
|
||||
style="width: 90px; flex-grow: 0;"
|
||||
:items="['OR', 'NOT']"
|
||||
:value="extras.operation"
|
||||
:error-messages="errors.extraTags && errors.extraTags[i]"
|
||||
@change="change(['extraTags', i, 'operation'], ...arguments)"
|
||||
/>
|
||||
<smart-combobox
|
||||
label="Tags"
|
||||
:hint="extras.operation === 'OR' ? 'The effect will also target properties that have all of these tags instead' : 'The effect will ignore properties that have any of these tags'"
|
||||
class="mx-2"
|
||||
multiple
|
||||
chips
|
||||
deletable-chips
|
||||
persistent-hint
|
||||
:value="extras.tags"
|
||||
@change="change(['extraTags', i, 'tags'], ...arguments)"
|
||||
/>
|
||||
<v-btn
|
||||
icon
|
||||
style="margin-top: -30px;"
|
||||
@click="$emit('pull', {path: ['extraTags', i]})"
|
||||
>
|
||||
<v-icon>mdi-delete</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-slide-x-transition>
|
||||
<form-section
|
||||
name="Advanced"
|
||||
standalone
|
||||
>
|
||||
<smart-combobox
|
||||
label="Tags"
|
||||
multiple
|
||||
chips
|
||||
deletable-chips
|
||||
:value="model.tags"
|
||||
:error-messages="errors.tags"
|
||||
@change="change('tags', ...arguments)"
|
||||
/>
|
||||
<v-expand-transition>
|
||||
<text-field
|
||||
v-if="model.targetByTags"
|
||||
label="Target field"
|
||||
:value="model.variableName"
|
||||
hint="Target a specific calculation field on the affected properties"
|
||||
:error-messages="errors.targetField"
|
||||
@change="change('targetField', ...arguments)"
|
||||
/>
|
||||
</v-expand-transition>
|
||||
</form-section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -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}){
|
||||
|
||||
@@ -14,15 +14,21 @@
|
||||
<template v-if="model.name">
|
||||
{{ model.name }}
|
||||
</template>
|
||||
<template v-else-if="model.targetByTags">
|
||||
<span class="mr-1">
|
||||
{{ displayedValue }}
|
||||
</span>
|
||||
<span
|
||||
class="mr-1"
|
||||
>{{ displayedTags }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span class="mr-1">
|
||||
{{ displayedValue }}
|
||||
</span>
|
||||
<span
|
||||
v-for="stat in model.stats"
|
||||
:key="stat"
|
||||
class="mr-1"
|
||||
>{{ stat }}</span>
|
||||
>{{ displayedStats }}</span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -17,18 +17,54 @@
|
||||
<property-field
|
||||
v-if="model.operation !== 'conditional'"
|
||||
name="Amount"
|
||||
:value="displayedValue"
|
||||
:value="displayedValue || ' '"
|
||||
/>
|
||||
<property-field
|
||||
v-if="model.targetByTags"
|
||||
name="Targeted tags"
|
||||
>
|
||||
<div>
|
||||
<v-chip
|
||||
v-for="(tag, index) in model.targetTags"
|
||||
:key="index"
|
||||
class="ma-1"
|
||||
>
|
||||
{{ tag }}
|
||||
</v-chip>
|
||||
<div
|
||||
v-for="ex in model.extraTags"
|
||||
:key="ex._id"
|
||||
>
|
||||
<span class="ma-2">
|
||||
{{ ex.operation }}
|
||||
</span>
|
||||
<v-chip
|
||||
v-for="(extraTag, index) in ex.tags"
|
||||
:key="index"
|
||||
class="ma-1"
|
||||
>
|
||||
{{ extraTag }}
|
||||
</v-chip>
|
||||
</div>
|
||||
</div>
|
||||
</property-field>
|
||||
<property-field
|
||||
v-else
|
||||
name="Stats"
|
||||
:value="model.stats && model.stats.join(', ')"
|
||||
mono
|
||||
/>
|
||||
>
|
||||
<v-chip
|
||||
v-for="(stat, index) in model.stats"
|
||||
:key="index"
|
||||
class="ma-1"
|
||||
>
|
||||
{{ stat }}
|
||||
</v-chip>
|
||||
</property-field>
|
||||
<property-field
|
||||
v-if="model.operation === 'conditional'"
|
||||
name="Text"
|
||||
:cols="{cols: 12}"
|
||||
:value="model.text"
|
||||
:value="model.text || ' '"
|
||||
/>
|
||||
</v-row>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user