Updated forms and some UI for new data structure

This commit is contained in:
Stefan Zermatten
2021-09-28 14:25:11 +02:00
parent b471d0c5cf
commit cb10b53a10
52 changed files with 304 additions and 301 deletions

View File

@@ -7,10 +7,12 @@ export default function(){
const computation = buildComputationFromProps(testProperties);
computeCreatureComputation(computation);
const prop = id => computation.propsById[id];
const scope = variableName => computation.scope[variableName];
assert.equal(prop('emptyId').value, 0);
assert.equal(prop('noVariableNameId').value, 8);
assert.equal(prop('strengthId').value, 12);
assert.equal(prop('strengthId').modifier, 1);
assert.equal(scope('strength').modifier, 1);
assert.equal(prop('referencesDexId').value, 4);
assert.equal(prop('hitDiceId').constitutionMod, 5);
assert.equal(

View File

@@ -36,9 +36,6 @@ function addChangedKeysToOp(op, keys, original, changed) {
// and compile an operation that sets all those keys
for (let key of keys){
if (!isEqual(original[key], changed[key])){
console.log('not equal: ', key);
console.log(original[key])
console.log(changed[key])
if (!op) op = newOperation(original._id, changed.type);
let value = changed[key];
if (value === undefined){
@@ -86,7 +83,6 @@ function addUnsetOp(op, key){
// compensation without needing to roll back changes, which causes multiple
// expensive redraws of the character sheet
function writePropertiesSequentially(bulkWriteOps){
console.log({opsLength: bulkWriteOps.length});
bulkWriteOps.forEach(op => {
let updateOneOrMany = op.updateOne || op.updateMany;
CreatureProperties.update(updateOneOrMany.filter, updateOneOrMany.update, {
@@ -95,7 +91,7 @@ function writePropertiesSequentially(bulkWriteOps){
bypassCollection2: true,
});
});
console.log('finished writing ops');
if (bulkWriteOps.length) console.log(`Wrote ${bulkWriteOps.length} props`);
}
// This is more efficient on the database, but significantly less efficient

View File

@@ -3,9 +3,11 @@ import computeCreatureComputation from './computation/computeCreatureComputation
import writeAlteredProperties from './computation/writeComputation/writeAlteredProperties.js';
export default function computeCreature(creatureId){
console.time('Compute creature');
const computation = buildCreatureComputation(creatureId);
computeCreatureComputation(computation);
writeAlteredProperties(computation);
console.timeEnd('Compute creature');
}
// For now just recompute the whole creature, TODO only recompute a single

View File

@@ -9,7 +9,7 @@ export default function applyAttack({
let value = roll(1, 20)[0];
actionContext.attackRoll = {value};
let criticalHitTarget = creature.variables.criticalHitTarget &&
creature.variables.criticalHitTarget.currentValue || 20;
creature.variables.criticalHitTarget.value || 20;
let criticalHit = value >= criticalHitTarget;
if (criticalHit) actionContext.criticalHit = {value: true};
let result = value + prop.rollBonusResult;

View File

@@ -49,7 +49,7 @@ const castSpellWithSlot = new ValidatedMethod({
throw new Meteor.Error('No slot',
'Slot not found to cast spell');
}
if (!slot.currentValue){
if (!slot.value){
throw new Meteor.Error('No slot',
'Slot depleted');
}

View File

@@ -10,7 +10,6 @@ let AttackSchema = new SimpleSchema()
// What gets added to the d20 roll
rollBonus: {
type: 'fieldToCompute',
parseLevel: 'compile',
optional: true,
defaultValue: 'strength.modifier + proficiencyBonus',
},
@@ -36,7 +35,6 @@ const ComputedOnlyAttackSchema = new SimpleSchema()
.extend(createPropertySchema({
rollBonus: {
type: 'computedOnlyField',
parseLevel: 'compile',
optional: true,
},
}));

View File

@@ -225,11 +225,6 @@ export default {
},
},
meteor: {
$subscribe: {
'singleCharacter'(){
return [this.creatureId];
},
},
creature(){
return Creatures.findOne(this.creatureId);
},

View File

@@ -142,7 +142,7 @@
class="resource"
>
<resource-card
v-bind="resource"
:model="resource"
:data-id="resource._id"
@click="clickProperty({_id: resource._id})"
@change="e => incrementChange(resource._id, e)"

View File

@@ -40,8 +40,8 @@
<div class="flex">
{{ model.actionType }}
</div>
<div v-if="model.uses">
{{ usesLeft }} uses
<div v-if="Number.isFinite(model.usesLeft)">
{{ model.usesLeft }} uses
</div>
</div>
</div>
@@ -71,9 +71,7 @@
</template>
<template v-if="model.summary">
<property-description
:string="model.summary"
:calculations="model.summaryCalculations"
:inactive="model.inactive"
:model="model.summary"
/>
<v-divider
v-if="children.length"
@@ -135,18 +133,12 @@ export default {
}},
computed: {
rollBonus(){
if (!this.attack) return;
return numberToSignedString(this.model.rollBonusResult);
if (!this.attack || !this.model.rollBonus) return;
return numberToSignedString(this.model.rollBonus.value);
},
rollBonusTooLong(){
return this.rollBonus && this.rollBonus.length > 3;
},
totalUses(){
return Math.max(this.model.usesResult, 0);
},
usesLeft(){
return Math.max(this.model.usesResult - (this.model.usesUsed || 0), 0);
},
propertyName(){
return getPropertyName(this.model.type);
},

View File

@@ -7,7 +7,7 @@
class="mr-2"
style="width: 24px; text-align: center;"
>
{{ model.quantity }}
{{ model.quantity && model.quantity.value }}
</div>
<div
class="text-no-wrap text-truncate"

View File

@@ -31,8 +31,8 @@
class="mr-2 text-no-wrap"
style="min-width: 24px; text-align: center;"
>
<template v-if="model.quantity !== 0 && insufficient">
{{ model.available }} / {{ model.quantity }}
<template v-if="quantity !== 0 && insufficient">
{{ model.available }} / {{ quantity }}
</template>
<template v-else>
{{ model.available }}
@@ -70,7 +70,7 @@
class="mr-2"
style="width: 24px; text-align: center;"
>
{{ model.quantity }}
{{ quantity }}
</div>
<div
class="text-no-wrap text-truncate"
@@ -110,7 +110,10 @@ export default {
},
computed: {
insufficient(){
return this.model.quantity > this.model.available;
return this.quantity > this.model.available;
},
quantity(){
return this.model.quantity && this.model.quantity.value || 0;
},
},
}

View File

@@ -9,8 +9,8 @@
>
<div class="text-h4 mod">
<template v-if="swapScoresAndMods">
<span :class="{'primary--text': model.currentValue !== model.value}">
{{ model.currentValue }}
<span :class="{'primary--text': model.total !== model.value}">
{{ model.value }}
</span>
</template>
<template v-else>
@@ -22,8 +22,8 @@
{{ numberToSignedString(model.modifier) }}
</template>
<template v-else>
<span :class="{'primary--text': model.currentValue !== model.value}">
{{ model.currentValue }}
<span :class="{'primary--text': model.total !== model.value}">
{{ model.value }}
</span>
</template>
</div>

View File

@@ -30,9 +30,9 @@
computedValue(){
if (this.model.type === 'attribute'){
if (this.model.attributeType === 'modifier'){
return numberToSignedString(this.model.currentValue);
return numberToSignedString(this.model.value);
} else {
return this.model.currentValue
return this.model.value
}
} else {
return this.model.value;

View File

@@ -25,7 +25,7 @@
</div>
<div class="layout column my-2">
<div class="text-body-1 mb-1">
{{ model.name || operation }}
{{ displayedText }}
</div>
<div v-if="!hideBreadcrumbs">
<breadcrumbs
@@ -57,9 +57,18 @@
computed: {
hasClickListener(){
return this.$listeners && this.$listeners.click
},
displayedText(){
if (this.model.operation === 'conditional'){
return this.model.text || this.model.name || this.operation
} else {
return this.model.name || this.operation
}
},
resolvedValue(){
return this.model.result !== undefined ? this.model.result : this.model.calculation;
let amount = this.model.amount;
if (!amount) return;
return amount.value !== undefined ? amount.value : amount.calculation;
},
effectIcon(){
let value = this.resolvedValue;
@@ -107,7 +116,7 @@
case 'disadvantage': return;
case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value;
case 'fail': return;
case 'conditional': return;
case 'conditional': return undefined;
default: return undefined;
}
}

View File

@@ -3,8 +3,8 @@
<health-bar
v-for="attribute in attributes"
:key="attribute._id"
:value="attribute.currentValue"
:max-value="attribute.value"
:value="attribute.value"
:max-value="attribute.total"
:name="attribute.name"
:_id="attribute._id"
@change="e => $emit('change', {_id: attribute._id, change: e})"

View File

@@ -16,7 +16,7 @@
<v-btn
icon
small
:disabled="currentValue >= model.value || context.editPermission === false"
:disabled="model.value >= model.total || context.editPermission === false"
@click="increment(1)"
>
<v-icon>mdi-chevron-up</v-icon>
@@ -24,7 +24,7 @@
<v-btn
icon
small
:disabled="currentValue <= 0 || context.editPermission === false"
:disabled="model.value <= 0 || context.editPermission === false"
@click="increment(-1)"
>
<v-icon>mdi-chevron-down</v-icon>
@@ -35,10 +35,10 @@
align-end
>
<div class="text-h4">
{{ currentValue }}
{{ model.value }}
</div>
<div class="text-h6 max-value ml-2">
/{{ model.value }}
/{{ model.total }}
</div>
</v-layout>
</v-layout>
@@ -73,9 +73,6 @@ export default {
hover: false,
}},
computed: {
currentValue(){
return this.model.value - (this.model.damage || 0);
},
signedConMod(){
return numberToSignedString(this.model.constitutionMod);
},

View File

@@ -8,7 +8,7 @@
<v-btn
icon
small
:disabled="currentValue >= value || context.editPermission === false"
:disabled="model.value >= model.total || context.editPermission === false"
@click="increment(1)"
>
<v-icon>mdi-chevron-up</v-icon>
@@ -16,7 +16,7 @@
<v-btn
icon
small
:disabled="currentValue <= 0 || context.editPermission === false"
:disabled="model.value <= 0 || context.editPermission === false"
@click="increment(-1)"
>
<v-icon>mdi-chevron-down</v-icon>
@@ -26,10 +26,10 @@
class="layout align-center value pl-2 pr-3"
>
<div class="text-h4">
{{ currentValue }}
{{ model.value }}
</div>
<div class="text-h6 ml-2 max-value">
/{{ value }}
/{{ model.total }}
</div>
</div>
<div
@@ -39,7 +39,7 @@
@mouseleave="hover = false"
>
<div class="text-truncate ">
{{ name }}
{{ model.name }}
</div>
</div>
</v-layout>
@@ -48,24 +48,18 @@
<script lang="js">
export default {
inject: {
context: { default: {} }
},
props: {
_id: String,
name: String,
color: String,
value: Number,
damage: Number,
model: {
type: Object,
required: true,
}
},
data(){ return{
hover: false,
}},
inject: {
context: { default: {} }
},
computed: {
currentValue(){
return this.value - (this.damage || 0);
},
},
methods: {
click(e){
this.$emit('click', e);

View File

@@ -6,7 +6,7 @@
<v-list-item-content>
<v-list-item-title>
<div
v-if="model.value > 4"
v-if="model.total > 4"
class="layout value"
style="align-items: baseline;"
>
@@ -14,10 +14,10 @@
style="font-weight: 500; font-size: 24px"
class="current-value"
>
{{ model.currentValue }}
{{ model.value }}
</div>
<div class="ml-2 max-value">
/{{ model.value }}
/{{ model.total }}
</div>
</div>
<div
@@ -25,11 +25,11 @@
class="layout align-center slot-bubbles"
>
<v-icon
v-for="i in model.value"
v-for="i in model.total"
:key="i"
>
{{
i > model.currentValue ?
i > model.value ?
'mdi-radiobox-blank' :
'mdi-radiobox-marked'
}}
@@ -66,9 +66,6 @@ export default {
hideCastButton: Boolean,
},
computed: {
currentValue(){
return this.value - this.damage;
},
hasClickListener(){
return this.$listeners && !!this.$listeners.click;
},

View File

@@ -11,7 +11,7 @@
<v-spacer />
</template>
<v-card-text v-if="model.summary">
<PropertyDescription
<property-description
:model="model.summary"
/>
</v-card-text>

View File

@@ -12,9 +12,7 @@
</v-card-title>
<v-card-text v-if="model.summary">
<property-description
:string="model.summary"
:calculations="model.summaryCalculations"
:inactive="model.inactive"
:model="model.summary"
/>
</v-card-text>
</v-card>
@@ -29,7 +27,10 @@ export default {
PropertyDescription,
},
props: {
model: Object,
model: {
type: Object,
required: true,
},
},
computed: {
isDark(){

View File

@@ -25,33 +25,32 @@
@change="change('actionType', ...arguments)"
/>
<text-field
v-if="attackForm"
<computed-field
label="Roll bonus"
:value="model.rollBonus"
:error-messages="errors.rollBonus"
hint="A number (or calculation which returns a number) that is added to a d20 when making the attack roll"
@change="change('rollBonus', ...arguments)"
:model="model.rollBonus"
:error-messages="errors.rollBonus"
@change="({path, value, ack}) =>
$emit('change', {path: ['rollBonus', ...path], value, ack})"
/>
<calculation-error-list :errors="model.rollBonusErrors" />
<text-area
<inline-computation-field
label="Summary"
hint="This will appear in the action card in the character sheet"
:value="model.summary"
:model="model.summary"
:error-messages="errors.summary"
@change="change('summary', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['summary', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.summaryCalculations" />
<text-area
<inline-computation-field
label="Description"
hint="The rest of the description that doesn't fit in the summary goes here"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<form-sections>
<form-section name="Resources">
@@ -84,15 +83,14 @@
/>
-->
<div class="layout wrap">
<text-field
<computed-field
label="Uses"
hint="How many times this action can be used before needing to be reset"
style="flex-basis: 300px;"
:value="model.uses"
:model="model.uses"
:error-messages="errors.uses"
@change="change('uses', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['uses', ...path], value, ack})"
/>
<calculation-error-list :errors="model.usesErrors" />
<text-field
label="Uses used"
type="number"
@@ -123,14 +121,12 @@
import FormSection, {FormSections} from '/imports/ui/properties/forms/shared/FormSection.vue';
import ResourcesForm from '/imports/ui/properties/forms/ResourcesForm.vue';
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
FormSection,
FormSections,
ResourcesForm,
CalculationErrorList,
},
mixins: [propertyFormMixin],
props: {

View File

@@ -10,13 +10,14 @@
:error-messages="errors.stat"
@change="change('stat', ...arguments)"
/>
<text-field
<computed-field
label="Amount"
hint="The amount of damage to apply to the selected stat, can be a calculation or roll. Negative values will restore the selected from previous damage. If the operation is set, this is the final value of the stat instead."
style="flex-basis: 300px;"
:value="model.amount"
:model="model.amount"
:error-messages="errors.amount"
@change="change('amount', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['amount', ...path], value, ack})"
/>
</div>
<smart-select

View File

@@ -9,13 +9,13 @@
:error-messages="errors.variableName"
@change="change('variableName', ...arguments)"
/>
<text-field
<computed-field
label="Quantity"
hint="How much of the attribute will be consumed. If this amount is not available in the attribute, the action can't be taken"
style="flex-basis: 300px;"
:value="model.quantity"
:model="model.quantity"
:error-messages="errors.quantity"
@change="change('quantity', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['quantity', ...path], value, ack})"
/>
</div>
</template>

View File

@@ -9,7 +9,8 @@
style="width: 332px;"
:model="model.baseValue"
:error-messages="errors.baseValue"
@change="({path, value, ack}) => $emit('change', {path: ['baseValue', ...path], value, ack})"
@change="({path, value, ack}) =>
$emit('change', {path: ['baseValue', ...path], value, ack})"
/>
</div>
<div class="layout wrap">
@@ -46,18 +47,20 @@
:menu-props="{auto: true, lazy: true}"
@change="change('hitDiceSize', ...arguments)"
/>
<text-field
<computed-field
v-if="model.attributeType === 'spellSlot'"
label="Spell slot level"
:value="model.spellSlotLevelCalculation"
:error-messages="errors.spellSlotLevelCalculation"
@change="change('spellSlotLevelCalculation', ...arguments)"
:model="model.spellSlotLevel"
:error-messages="errors.spellSlotLevel"
@change="({path, value, ack}) =>
$emit('change', {path: ['spellSlotLevel', ...path], value, ack})"
/>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<form-section
name="Advanced"
@@ -119,12 +122,10 @@
<script lang="js">
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import ComputedField from '/imports/ui/properties/forms/shared/ComputedField.vue';
export default {
components: {
FormSection,
ComputedField,
},
mixins: [propertyFormMixin],
inject: {

View File

@@ -26,21 +26,23 @@
</v-alert>
</div>
</v-expand-transition>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<!-- Duration not implemented yet
<text-field
<computed-field
label="Duration"
hint="How long the buff lasts"
:value="model.duration"
hint="How many rounds the buff lasts"
:model="model.duration"
:error-messages="errors.duration"
@change="change('duration', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['duration', ...path], value, ack})"
/>
-->
<v-expand-transition>
@@ -69,12 +71,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
props: {
parentTarget: {

View File

@@ -28,14 +28,14 @@
/>
</div>
<text-area
<inline-computation-field
label="Description"
hint="A brief description of what this class level gives a character"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<text-field
label="Condition"
@@ -60,12 +60,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
};
</script>

View File

@@ -46,13 +46,14 @@
@change="change('weight', ...arguments)"
/>
</div>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<form-section
name="Advanced"
@@ -84,12 +85,10 @@
<script lang="js">
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
FormSection,
CalculationErrorList,
},
mixins: [propertyFormMixin],
}

View File

@@ -1,14 +1,14 @@
<template lang="html">
<div>
<div class="layout">
<text-field
<computed-field
ref="focusFirst"
label="Damage"
style="flex-basis: 300px;"
hint="A caculation including dice rolls of the damge to deal to the target when activated by an action"
:value="model.amount"
:model="model.amount"
:error-messages="errors.amount"
@change="change('amount', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['amount', ...path], value, ack})"
/>
<calculation-error-list :errors="model.amountErrors" />
<smart-select

View File

@@ -49,15 +49,16 @@
:error-messages="errors.stats"
@change="change('stats', ...arguments)"
/>
<text-field
<computed-field
label="Value"
class="mr-2"
hint="Number or calculation to determine the value of this effect"
:persistent-hint="needsValue"
:value="needsValue ? (model.calculation) : ' '"
:disabled="!needsValue"
:error-messages="errors.calculation"
@change="change('calculation', ...arguments)"
:model="model.amount"
:error-messages="errors.amount"
@change="({path, value, ack}) =>
$emit('change', {path: ['amount', ...path], value, ack})"
/>
<v-expand-transition>
<text-field

View File

@@ -7,23 +7,23 @@
:error-messages="errors.name"
@change="change('name', ...arguments)"
/>
<text-area
<inline-computation-field
label="Summary"
hint="This will appear in the feature card in the character sheet"
:value="model.summary"
:model="model.summary"
:error-messages="errors.summary"
@change="change('summary', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['summary', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.summaryCalculations" />
<text-area
<inline-computation-field
label="Description"
hint="The rest of the description that doesn't fit in the summary goes here"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<smart-combobox
label="Tags"
@@ -40,12 +40,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
data(){ return{
enabledOptions: [

View File

@@ -8,13 +8,14 @@
:error-messages="errors.tag"
@change="change('tag', ...arguments)"
/>
<text-field
<computed-field
label="Quantity"
hint="How many will be consumed"
style="flex-basis: 300px;"
:value="model.quantity"
:model="model.quantity"
:error-messages="errors.quantity"
@change="change('quantity', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['quantity', ...path], value, ack})"
/>
</div>
</template>

View File

@@ -71,13 +71,14 @@
:error-messages="errors.quantity"
@change="change('quantity', ...arguments)"
/>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<form-section
name="Advanced"
@@ -126,12 +127,10 @@
<script lang="js">
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
FormSection,
CalculationErrorList,
},
mixins: [propertyFormMixin],
}

View File

@@ -8,23 +8,23 @@
@change="change('name', ...arguments)"
/>
<text-area
<inline-computation-field
label="Summary"
hint="This will appear in the card in the character sheet"
:value="model.summary"
:model="model.summary"
:error-messages="errors.summary"
@change="change('summary', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['summary', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.summaryCalculations" />
<text-area
<inline-computation-field
label="Description"
hint="Text that does not fit in the summary"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<smart-combobox
label="Tags"
@@ -40,12 +40,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
}
</script>

View File

@@ -16,13 +16,13 @@
@change="change('variableName', ...arguments)"
/>
</div>
<text-field
ref="focusFirst"
label="Roll"
<computed-field
label="Roll bonus"
hint="The calculation that will be evaluated when the roll is triggered by an action. The result will be saved as the variable name in the context of the roll."
:value="model.roll"
:model="model.roll"
:error-messages="errors.roll"
@change="change('roll', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['roll', ...path], value, ack})"
/>
<form-sections>
<form-section name="Advanced">

View File

@@ -7,14 +7,15 @@
:error-messages="errors.name"
@change="change('name', ...arguments)"
/>
<text-field
<computed-field
label="DC"
hint="A calculation of the DC that the target of an action needs to save against in order to succeed. If the saving throw is lower than the DC, the children of this property will be activated."
:value="model.dc"
:model="model.dc"
:error-messages="errors.dc"
@change="change('dc', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['dc', ...path], value, ack})"
/>
<calculation-error-list :errors="model.dcErrors" />
<smart-combobox
label="Save"

View File

@@ -36,13 +36,13 @@
@change="change('skillType', ...arguments)"
/>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<form-section
name="Advanced"
@@ -57,13 +57,13 @@
@change="change('tags', ...arguments)"
/>
<div class="layout justify-center">
<text-field
<computed-field
label="Base Value"
class="base-value-field no-flex"
:value="model.baseValueCalculation"
hint="This is the value of the skill before effects are applied"
:error-messages="errors.baseValueCalculation"
@change="change('baseValueCalculation', ...arguments)"
:model="model.baseValue"
:error-messages="errors.baseValue"
@change="({path, value, ack}) =>
$emit('change', {path: ['baseValue', ...path], value, ack})"
/>
<proficiency-select
style="flex-basis: 300px;"
@@ -73,7 +73,6 @@
@change="change('baseProficiency', ...arguments)"
/>
</div>
<calculation-error-list :errors="model.baseValueErrors" />
</form-section>
</div>
</template>
@@ -83,13 +82,11 @@
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
import createListOfProperties from '/imports/ui/properties/forms/shared/lists/createListOfProperties.js';
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
ProficiencySelect,
FormSection,
CalculationErrorList,
},
mixins: [propertyFormMixin],
data(){return{

View File

@@ -74,23 +74,25 @@
</v-btn>
</div>
</v-slide-x-transition>
<text-field
<computed-field
label="Quantity"
hint="How many matching properties must be used to fill this slot, 0 is unlimited"
:value="model.quantityExpected"
:model="model.quantityExpected"
:error-messages="errors.quantityExpected"
@change="change('quantityExpected', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['quantityExpected', ...path], value, ack})"
/>
<calculation-error-list :errors="model.quantityExpectedErrors" />
<text-field
<computed-field
label="Condition"
hint="A caclulation to determine if this slot should be active"
placeholder="Always active"
:value="model.slotCondition"
:model="model.slotCondition"
:error-messages="errors.slotCondition"
@change="change('slotCondition', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['slotCondition', ...path], value, ack})"
/>
<calculation-error-list :errors="model.slotConditionErrors" />
<smart-select
label="Unique"
@@ -115,13 +117,13 @@
</v-btn>
</v-layout>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<form-section
name="Advanced"
@@ -161,14 +163,12 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
import PROPERTIES from '/imports/constants/PROPERTIES.js';
import { SlotSchema } from '/imports/api/properties/Slots.js';
export default {
components: {
FormSection,
CalculationErrorList,
},
mixins: [propertyFormMixin],
inject: {

View File

@@ -98,13 +98,13 @@
@change="change('duration', ...arguments)"
/>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<smart-combobox
label="Tags"
@@ -133,14 +133,12 @@
import FormSection, { FormSections } from '/imports/ui/properties/forms/shared/FormSection.vue';
import ActionForm from '/imports/ui/properties/forms/ActionForm.vue'
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
FormSections,
FormSection,
ActionForm,
CalculationErrorList,
},
mixins: [propertyFormMixin],
data(){return {

View File

@@ -10,40 +10,40 @@
/>
</div>
<text-area
<inline-computation-field
label="Description"
:value="model.description"
:model="model.description"
:error-messages="errors.description"
@change="change('description', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['description', ...path], value, ack})"
/>
<calculation-error-list :calculations="model.descriptionCalculations" />
<text-field
<computed-field
label="Maximum prepared spells"
:value="model.maxPrepared"
hint="How many spells can be prepared"
:model="model.maxPrepared"
:error-messages="errors.maxPrepared"
@change="change('maxPrepared', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['maxPrepared', ...path], value, ack})"
/>
<calculation-error-list :errors="model.maxPreparedErrors" />
<text-field
<computed-field
label="Spell save DC"
:value="model.dc"
hint="The spell save DC of spells in this list"
:model="model.dc"
:error-messages="errors.dc"
@change="change('dc', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['dc', ...path], value, ack})"
/>
<calculation-error-list :errors="model.dcErrors" />
<text-field
<computed-field
label="Attack roll bonus"
:value="model.attackRollBonus"
hint="The attack roll bonus of spell attacks made by spells in this list"
:model="model.attackRollBonus"
:error-messages="errors.attackRollBonus"
@change="change('attackRollBonus', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['attackRollBonus', ...path], value, ack})"
/>
<calculation-error-list :errors="model.attackRollBonusErrors" />
<smart-combobox
label="Tags"
@@ -59,12 +59,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
};
</script>

View File

@@ -30,16 +30,16 @@
</v-radio-group>
</v-layout>
<v-fade-transition>
<text-field
<computed-field
v-show="radioSelection === 'calculated'"
label="Condition"
hint="When this calculation returns a value that isn't false or zero the children will be active"
:value="model.condition"
:model="model.condition"
:error-messages="errors.condition"
@change="change('condition', ...arguments)"
@change="({path, value, ack}) =>
$emit('change', {path: ['condition', ...path], value, ack})"
/>
</v-fade-transition>
<calculation-error-list :errors="model.errors" />
<smart-combobox
label="Tags"
multiple
@@ -54,12 +54,8 @@
<script lang="js">
import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js';
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
mixins: [propertyFormMixin],
computed: {
radioSelection(){

View File

@@ -2,7 +2,6 @@
<div class="computed-field">
<text-field
:value="model.calculation"
:rows="1"
v-bind="$attrs"
@change="(value, ack) => $emit('change', {path: ['calculation'], value, ack})"
>
@@ -17,7 +16,7 @@
</div>
</template>
<script>
<script lang="js">
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {

View File

@@ -0,0 +1,42 @@
<template lang="html">
<div class="inline-computation-field">
<text-area
:value="model.text"
v-bind="$attrs"
@change="(value, ack) => $emit('change', {path: ['text'], value, ack})"
/>
<template v-for="calc in model.inlineCalculations">
<div
v-if="calc && calc.calculation && calc.errors.length"
:key="calc.calculation"
>
<v-subheader
class="warning--text"
style="font-family: monospace;"
>
{ {{ calc.calculation }} }
</v-subheader>
<calculation-error-list :errors="calc.errors" />
</div>
</template>
</div>
</template>
<script lang="js">
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
export default {
components: {
CalculationErrorList,
},
props: {
model: {
type: Object,
default: () => ({}),
},
},
}
</script>
<style lang="css" scoped>
</style>

View File

@@ -1,4 +1,11 @@
import ComputedField from '/imports/ui/properties/forms/shared/ComputedField.vue';
import InlineComputationField from '/imports/ui/properties/forms/shared/InlineComputationField.vue';
export default {
components: {
ComputedField,
InlineComputationField,
},
props: {
model: {
type: [Object, Array],

View File

@@ -10,7 +10,7 @@
<div
class="text-no-wrap text-truncate"
>
<span v-if="model.amountResult < 0">+</span>
<span v-if="amount < 0">+</span>
{{ absoluteAmount }} {{ model.stat }}
<span v-if="typeof absoluteAmount === 'string' || absoluteAmount >= 0">
damage
@@ -25,11 +25,14 @@ import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeView
export default {
mixins: [treeNodeViewMixin],
computed: {
amount(){
return this.model.amount && this.model.amount.value;
},
absoluteAmount(){
if (typeof this.model.amountResult === 'number'){
return Math.abs(this.model.amountResult);
if (typeof this.amount === 'number'){
return Math.abs(this.amount);
} else {
return this.model.amountResult;
return this.amount;
}
},
}

View File

@@ -11,7 +11,7 @@
<div
class="text-no-wrap text-truncate"
>
{{ model.amountResult }}
{{ model.amount && model.amount.value }}
{{ model.damageType }}<span
v-if="model.damageType !== 'healing'"
>&nbsp;damage</span>

View File

@@ -37,7 +37,8 @@ export default {
mixins: [treeNodeViewMixin],
computed: {
resolvedValue(){
return this.model.result !== undefined ? this.model.result : this.model.calculation;
return (this.model.amount && this.model.amount.value) !== undefined ?
this.model.amount.value : this.model.amount && this.model.amount.calculation;
},
effectIcon(){
let value = this.resolvedValue;

View File

@@ -38,7 +38,7 @@
style="font-size: 18px;"
class="ma-2"
>
<code>{{ model.rollBonus }}</code>
<code>{{ model.rollBonus && model.rollBonus.value }}</code>
<span
class="mx-1"
style="font-size: 14px"
@@ -46,13 +46,13 @@
</div>
<div v-if="model.uses">
<span
v-if="context.creatureId"
v-if="context.creatureId && model.uses.value"
class="uses mx-2"
>
{{ usesLeft }}/{{ model.usesResult }} uses
{{ usesLeft }}/{{ model.uses.value }} uses
</span>
<span v-else>
<code>{{ model.uses }}</code>
<code>{{ model.uses.calculation }}</code>
<span class="mx-1">
uses
</span>
@@ -97,24 +97,21 @@
left-pad
/>
<v-divider
v-if="model.summary || model.description"
v-if="(model.summary && model.summary.text) ||
(model.description && model.description.text)"
class="my-3"
/>
<template v-if="model.summary">
<template v-if="(model.summary && model.summary.text)">
<property-description
:string="model.summary"
:calculations="model.summaryCalculations"
:inactive="model.inactive"
:model="model.summary"
/>
<v-divider
v-if="model.description"
v-if="model.description && model.description.text"
class="my-3"
/>
</template>
<property-description
:string="model.description"
:calculations="model.descriptionCalculations"
:inactive="model.inactive"
:model="model.description"
/>
</div>
</template>
@@ -157,17 +154,18 @@ export default {
return undefined;
},
rollBonus(){
if (!this.attack) return;
return numberToSignedString(this.model.rollBonusResult);
if (!this.attack || !this.model.rollBonus) return;
return numberToSignedString(this.model.rollBonus.value);
},
rollBonusTooLong(){
return this.rollBonus && this.rollBonus.length > 3;
},
totalUses(){
return Math.max(this.model.usesResult, 0);
if (!this.model.uses) return 0;
return Math.max(this.model.uses.value || 0, 0);
},
usesLeft(){
return Math.max(this.model.usesResult - (this.model.usesUsed || 0), 0);
return Math.max(this.totalUses - (this.model.usesUsed || 0), 0);
},
actionTypeIcon() {
return `$vuetify.icons.${this.model.actionType}`;

View File

@@ -47,9 +47,11 @@
return getEffectIcon(effectOp, -value);
},
value(){
return 'amountResult' in this.model ?
this.model.amountResult :
this.model.amount;
if (this.model.amount && 'value' in this.model.amount){
return this.model.amount.value;
} else {
return this.model.amount.calculation;
}
},
displayedValue(){
if (

View File

@@ -11,7 +11,7 @@
<div
v-if="model.damage !== undefined"
>
{{ model.value - model.damage }} / {{ model.value }}
{{ model.value }} / {{ model.total }}
</div>
<div v-else>
{{ model.value }}
@@ -24,7 +24,7 @@
large
outlined
color="primary"
:value="model.value - (model.damage || 0)"
:value="model.value"
@change="damageProperty"
>
<v-icon>$vuetify.icons.abacus</v-icon>
@@ -52,9 +52,7 @@
:value="reset"
/>
<property-description
:string="model.description"
:calculations="model.descriptionCalculations"
:inactive="model.inactive"
:model="model.description"
/>
<v-list>
<attribute-effect
@@ -135,11 +133,10 @@
_id: prop._id,
name: 'Attribute base value',
operation: 'base',
calculation: prop.baseValueCalculation,
result: prop.baseValue,
amount: prop.baseValue,
stats: [prop.variableName],
ancestors: prop.ancestors,
}) ).filter(effect => effect.result);
}) ).filter(effect => effect.amount);
} else {
return [];
}

View File

@@ -1,14 +1,12 @@
<template lang="html">
<div class="buff-viewer">
<property-name :value="model.name" />
<property-field
<!--<property-field
name="Duration"
:value="model.duration"
/>
/>-->
<property-description
:string="model.description"
:calculations="model.descriptionCalculations"
:inactive="model.inactive"
:model="model.description"
/>
</div>
</template>

View File

@@ -9,9 +9,7 @@
<code>{{ model.variableName }}</code>
</p>
<property-description
:string="model.description"
:calculations="model.descriptionCalculations"
:inactive="model.inactive"
:model="model.description"
/>
</div>
</template>

View File

@@ -222,7 +222,7 @@ export default {
let creature = Creatures.findOne(creatureId)
return creature &&
creature.variables.proficiencyBonus &&
creature.variables.proficiencyBonus.currentValue;
creature.variables.proficiencyBonus.value;
},
},
}