From 1535e00093da8a6a08255d586434bbef9af01217 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sun, 7 Jun 2020 21:08:53 +0200 Subject: [PATCH] Denormalized some calculations into recomputation step --- .../creature/computation/ComputationMemo.js | 10 ++- .../computation/computeEndStepProperty.js | 61 ++++++++++++++++++ .../api/creature/computation/computeMemo.js | 7 ++- .../computation/evaluateCalculation.js | 2 +- .../creature/computation/recomputeCreature.js | 6 ++ .../computation/writeAlteredProperties.js | 15 ++++- app/imports/api/properties/Actions.js | 38 ++++++++++- app/imports/api/properties/Adjustments.js | 1 + app/imports/api/properties/Attacks.js | 25 +++++++- app/imports/api/properties/Damages.js | 1 + app/imports/api/properties/Rolls.js | 2 +- app/imports/api/properties/SavingThrows.js | 21 ++++++- app/imports/api/properties/SpellLists.js | 21 ++++++- app/imports/api/properties/Spells.js | 11 +++- .../computedPropertySchemasIndex.js | 20 +++--- .../api/properties/subSchemas/ErrorSchema.js | 2 - .../character/characterSheetTabs/StatsTab.vue | 23 +++++-- .../components/actions/ActionListTile.vue | 45 ++++++++++++- .../components/actions/AttackListTile.vue | 49 --------------- .../ui/properties/viewers/ActionViewer.vue | 63 ++++++++++--------- .../ui/properties/viewers/AttackViewer.vue | 27 ++------ .../ui/properties/viewers/DamageViewer.vue | 2 +- 22 files changed, 320 insertions(+), 132 deletions(-) create mode 100644 app/imports/api/creature/computation/computeEndStepProperty.js delete mode 100644 app/imports/ui/properties/components/actions/AttackListTile.vue diff --git a/app/imports/api/creature/computation/ComputationMemo.js b/app/imports/api/creature/computation/ComputationMemo.js index fab2f978..3ee18e20 100644 --- a/app/imports/api/creature/computation/ComputationMemo.js +++ b/app/imports/api/creature/computation/ComputationMemo.js @@ -15,6 +15,8 @@ export default class ComputationMemo { this.classes = {}; this.togglesById = {}; this.toggleIds = new Set(); + // Properties that have calculations, but don't impact other properties + this.endStepPropsById = {}; // First note all the ids of all the toggles props.forEach((prop) => { if ( @@ -49,6 +51,8 @@ export default class ComputationMemo { this.addProficiency(prop); } else if (prop.type === 'classLevel'){ this.addClassLevel(prop); + } else { + this.addEndStepProp(prop); } }); for (let name in creature.denormalizedStats){ @@ -181,6 +185,10 @@ export default class ComputationMemo { }); return targets; } + addEndStepProp(prop){ + prop = this.registerProperty(prop); + this.endStepPropsById[prop._id] = prop; + } } function isAbility(prop){ @@ -206,7 +214,7 @@ function isSkillOperation(prop){ } function propDetails(prop){ - return propDetailsByType[prop.type]() || {}; + return propDetailsByType[prop.type] && propDetailsByType[prop.type]() || {}; } const propDetailsByType = { diff --git a/app/imports/api/creature/computation/computeEndStepProperty.js b/app/imports/api/creature/computation/computeEndStepProperty.js new file mode 100644 index 00000000..81a2096f --- /dev/null +++ b/app/imports/api/creature/computation/computeEndStepProperty.js @@ -0,0 +1,61 @@ +import evaluateCalculation from '/imports/api/creature/computation/evaluateCalculation.js'; + +export default function computeEndStepProperty(prop, memo){ + switch (prop.type){ + case 'action': + case 'spell': + computeAction(prop, memo); + break; + case 'attack': + computeAction(prop, memo); + computeAttack(prop, memo); + break; + case 'savingThrow': + computeSavingThrow(prop, memo); + break; + case 'spellList': + computeSpellList(prop, memo); + break; + } +} + +function computeAction(prop, memo){ + let {value, errors} = evaluateCalculation(prop.uses, memo); + prop.usesResult = value; + if (errors.length){ + prop.usesErrors = errors; + } else { + delete prop.usesErrors; + } + // TODO compute resources.$.$.available and insufficientResources +} + +function computeAttack(prop, memo){ + let {value, errors} = evaluateCalculation(prop.rollBonus, memo); + prop.rollBonusResult = value; + if (errors.length){ + prop.rollBonusErrors = errors; + } else { + delete prop.rollBonusErrors; + } +} + +function computeSavingThrow(prop, memo){ + let {value, errors} = evaluateCalculation(prop.dc, memo); + prop.dcResult = value; + if (errors.length){ + prop.dcErrors = errors; + } else { + delete prop.dcErrors; + } +} + +function computeSpellList(prop, memo){ + let {value, errors} = evaluateCalculation(prop.maxPrepared, memo); + prop.maxPreparedResult = value; + if (errors.length){ + prop.maxPreparedErrors = errors; + } else { + delete prop.maxPreparedErrors; + } +} diff --git a/app/imports/api/creature/computation/computeMemo.js b/app/imports/api/creature/computation/computeMemo.js index 635d8090..061a4bb8 100644 --- a/app/imports/api/creature/computation/computeMemo.js +++ b/app/imports/api/creature/computation/computeMemo.js @@ -3,6 +3,7 @@ import computeLevels from '/imports/api/creature/computation/computeLevels.js'; import computeStat from '/imports/api/creature/computation/computeStat.js'; import computeEffect from '/imports/api/creature/computation/computeEffect.js'; import computeToggle from '/imports/api/creature/computation/computeToggle.js'; +import computeEndStepProperty from '/imports/api/creature/computation/computeEndStepProperty.js'; export default function computeMemo(memo){ // Compute level @@ -15,8 +16,12 @@ export default function computeMemo(memo){ each(memo.unassignedEffects, effect => { computeEffect(effect, memo); }); + // Compute toggles which didn't already get computed by dependencies forOwn(memo.togglesById, toggle => { computeToggle(toggle, memo); }); - // Compute class levels + // Compute end step properties + forOwn(memo.endStepPropsById, prop => { + computeEndStepProperty(prop, memo); + }); } diff --git a/app/imports/api/creature/computation/evaluateCalculation.js b/app/imports/api/creature/computation/evaluateCalculation.js index dedda235..dbcd1ef4 100644 --- a/app/imports/api/creature/computation/evaluateCalculation.js +++ b/app/imports/api/creature/computation/evaluateCalculation.js @@ -3,7 +3,7 @@ import math from '/imports/math.js'; /* Convert a calculation into a constant output and errors*/ export default function evaluateCalculation(string, memo){ - if (!string) return string; + if (!string) return {errors: [], value: string}; let errors = []; // Parse the string using mathjs let calc; diff --git a/app/imports/api/creature/computation/recomputeCreature.js b/app/imports/api/creature/computation/recomputeCreature.js index 56dc3d15..e0fb59af 100644 --- a/app/imports/api/creature/computation/recomputeCreature.js +++ b/app/imports/api/creature/computation/recomputeCreature.js @@ -42,6 +42,12 @@ const calculationPropertyTypes = [ 'proficiency', 'classLevel', 'toggle', + // End step types + 'action', + 'attack', + 'savingThrow', + 'spellList', + 'spell', ]; export function recomputeCreatureById(creatureId){ diff --git a/app/imports/api/creature/computation/writeAlteredProperties.js b/app/imports/api/creature/computation/writeAlteredProperties.js index 7820b319..6a8129fc 100644 --- a/app/imports/api/creature/computation/writeAlteredProperties.js +++ b/app/imports/api/creature/computation/writeAlteredProperties.js @@ -1,16 +1,29 @@ import { Meteor } from 'meteor/meteor' import { isEqual, forOwn } from 'lodash'; +import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +// Schemas +// Calculated props import { ComputedOnlySkillSchema } from '/imports/api/properties/Skills.js'; import { ComputedOnlyAttributeSchema } from '/imports/api/properties/Attributes.js'; import { ComputedOnlyEffectSchema } from '/imports/api/properties/Effects.js'; import { ComputedOnlyToggleSchema } from '/imports/api/properties/Toggles.js'; -import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; +// End step props +import { ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js'; +import { ComputedOnlyAttackSchema } from '/imports/api/properties/Attacks.js'; +import { ComputedOnlySavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; +import { ComputedOnlySpellListSchema } from '/imports/api/properties/SpellLists.js'; +import { ComputedOnlySpellSchema } from '/imports/api/properties/Spells.js'; const schemasByType = { 'skill': ComputedOnlySkillSchema, 'attribute': ComputedOnlyAttributeSchema, 'effect': ComputedOnlyEffectSchema, 'toggle': ComputedOnlyToggleSchema, + 'action': ComputedOnlyActionSchema, + 'attack': ComputedOnlyAttackSchema, + 'savingThrow': ComputedOnlySavingThrowSchema, + 'spellList': ComputedOnlySpellListSchema, + 'spell': ComputedOnlySpellSchema, }; export default function writeAlteredProperties(memo){ diff --git a/app/imports/api/properties/Actions.js b/app/imports/api/properties/Actions.js index 7861bb91..5981a2d8 100644 --- a/app/imports/api/properties/Actions.js +++ b/app/imports/api/properties/Actions.js @@ -1,5 +1,6 @@ import SimpleSchema from 'simpl-schema'; import ResourcesSchema from '/imports/api/properties/subSchemas/ResourcesSchema.js' +import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; /* * Actions are things a character can do @@ -64,4 +65,39 @@ let ActionSchema = new SimpleSchema({ }, }); -export { ActionSchema }; +const ComputedOnlyActionSchema = new SimpleSchema({ + usesResult: { + type: SimpleSchema.Integer, + optional: true, + }, + usesErrors: { + type: Array, + optional: true, + }, + 'usesErrors.$':{ + type: ErrorSchema, + }, + resources: Object, + 'resources.itemsConsumed': Array, + 'resources.itemsConsumed.$': Object, + 'resources.itemsConsumed.$.available': { + type: Number, + optional: true, + }, + 'resources.attributesConsumed': Array, + 'resources.attributesConsumed.$': Object, + 'resources.attributesConsumed.$.available': { + type: Number, + optional: true, + }, + insufficientResources: { + type: Boolean, + optional: true, + }, +}); + +const ComputedActionSchema = new SimpleSchema() + .extend(ActionSchema) + .extend(ComputedOnlyActionSchema); + +export { ActionSchema, ComputedOnlyActionSchema, ComputedActionSchema}; diff --git a/app/imports/api/properties/Adjustments.js b/app/imports/api/properties/Adjustments.js index 8e6b316a..400160c8 100644 --- a/app/imports/api/properties/Adjustments.js +++ b/app/imports/api/properties/Adjustments.js @@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema'; const AdjustmentSchema = new SimpleSchema({ // The roll that determines how much to change the attribute + // This can be simplified, but should only compute when activated amount: { type: String, optional: true, diff --git a/app/imports/api/properties/Attacks.js b/app/imports/api/properties/Attacks.js index 5d69b74e..c5f7531d 100644 --- a/app/imports/api/properties/Attacks.js +++ b/app/imports/api/properties/Attacks.js @@ -1,5 +1,6 @@ import SimpleSchema from 'simpl-schema'; -import { ActionSchema } from '/imports/api/properties/Actions.js'; +import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js'; +import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; // Attacks are special instances of actions let AttackSchema = new SimpleSchema() @@ -25,4 +26,24 @@ let AttackSchema = new SimpleSchema() }, }); -export { AttackSchema }; +const ComputedOnlyAttackSchema = new SimpleSchema() + .extend(ComputedOnlyActionSchema) + .extend({ + rollBonusResult: { + type: Number, + optional: true, + }, + rollBonusErrors: { + type: Array, + optional: true, + }, + 'rollBonusErrors.$':{ + type: ErrorSchema, + }, + }); + +const ComputedAttackSchema = new SimpleSchema() + .extend(AttackSchema) + .extend(ComputedOnlyAttackSchema); + +export { AttackSchema, ComputedOnlyAttackSchema, ComputedAttackSchema }; diff --git a/app/imports/api/properties/Damages.js b/app/imports/api/properties/Damages.js index e5a2e5ee..8f049187 100644 --- a/app/imports/api/properties/Damages.js +++ b/app/imports/api/properties/Damages.js @@ -3,6 +3,7 @@ import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js'; const DamageSchema = new SimpleSchema({ // The roll that determines how much to damage the attribute + // This can be simplified, but only computed when applied amount: { type: String, optional: true, diff --git a/app/imports/api/properties/Rolls.js b/app/imports/api/properties/Rolls.js index fcb4bfa5..09fe948b 100644 --- a/app/imports/api/properties/Rolls.js +++ b/app/imports/api/properties/Rolls.js @@ -19,7 +19,7 @@ import SimpleSchema from 'simpl-schema'; * child rolls are applied */ let RollSchema = new SimpleSchema({ - // The roll + // The roll, can be simplified, but only computed in context roll: { type: String, optional: true, diff --git a/app/imports/api/properties/SavingThrows.js b/app/imports/api/properties/SavingThrows.js index 78a9520b..f24735fc 100644 --- a/app/imports/api/properties/SavingThrows.js +++ b/app/imports/api/properties/SavingThrows.js @@ -1,4 +1,5 @@ import SimpleSchema from 'simpl-schema'; +import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; // These are the rolls made when saves are called for // For the saving throw bonus or proficiency, see ./Skills.js @@ -18,4 +19,22 @@ let SavingThrowSchema = new SimpleSchema ({ }, }); -export { SavingThrowSchema }; +const ComputedOnlySavingThrowSchema = new SimpleSchema({ + dcResult: { + type: Number, + optional: true, + }, + dcErrors: { + type: Array, + optional: true, + }, + 'dcErrors.$':{ + type: ErrorSchema, + }, +}); + +const ComputedSavingThrowSchema = new SimpleSchema() + .extend(SavingThrowSchema) + .extend(ComputedOnlySavingThrowSchema); + +export { SavingThrowSchema, ComputedOnlySavingThrowSchema, ComputedSavingThrowSchema }; diff --git a/app/imports/api/properties/SpellLists.js b/app/imports/api/properties/SpellLists.js index 0646296a..45628d8d 100644 --- a/app/imports/api/properties/SpellLists.js +++ b/app/imports/api/properties/SpellLists.js @@ -1,4 +1,5 @@ import SimpleSchema from 'simpl-schema'; +import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js'; let SpellListSchema = new SimpleSchema({ name: { @@ -16,4 +17,22 @@ let SpellListSchema = new SimpleSchema({ }, }); -export { SpellListSchema } +const ComputedOnlySpellListSchema = new SimpleSchema({ + maxPreparedResult: { + type: Number, + optional: true, + }, + maxPreparedErrors: { + type: Array, + optional: true, + }, + 'maxPreparedErrors.$':{ + type: ErrorSchema, + }, +}); + +const ComputedSpellListSchema = new SimpleSchema() + .extend(SpellListSchema) + .extend(ComputedOnlySpellListSchema); + +export { SpellListSchema, ComputedOnlySpellListSchema, ComputedSpellListSchema }; diff --git a/app/imports/api/properties/Spells.js b/app/imports/api/properties/Spells.js index 3a8b4661..831fac0c 100644 --- a/app/imports/api/properties/Spells.js +++ b/app/imports/api/properties/Spells.js @@ -1,4 +1,4 @@ -import { ActionSchema } from '/imports/api/properties/Actions.js'; +import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js'; import SimpleSchema from 'simpl-schema'; const magicSchools = [ @@ -93,4 +93,11 @@ let SpellSchema = new SimpleSchema({}) }, }); -export { SpellSchema }; +const ComputedOnlySpellSchema = new SimpleSchema() + .extend(ComputedOnlyActionSchema); + +const ComputedSpellSchema = new SimpleSchema() + .extend(SpellSchema) + .extend(ComputedOnlySpellSchema); + +export { SpellSchema, ComputedOnlySpellSchema, ComputedSpellSchema }; diff --git a/app/imports/api/properties/computedPropertySchemasIndex.js b/app/imports/api/properties/computedPropertySchemasIndex.js index 56f4ea98..5ee33940 100644 --- a/app/imports/api/properties/computedPropertySchemasIndex.js +++ b/app/imports/api/properties/computedPropertySchemasIndex.js @@ -1,7 +1,7 @@ import SimpleSchema from 'simpl-schema'; -import { ActionSchema } from '/imports/api/properties/Actions.js'; +import { ComputedActionSchema } from '/imports/api/properties/Actions.js'; import { AdjustmentSchema } from '/imports/api/properties/Adjustments.js'; -import { AttackSchema } from '/imports/api/properties/Attacks.js'; +import { ComputedAttackSchema } from '/imports/api/properties/Attacks.js'; import { ComputedAttributeSchema } from '/imports/api/properties/Attributes.js'; import { BuffSchema } from '/imports/api/properties/Buffs.js'; import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js'; @@ -15,17 +15,17 @@ import { ItemSchema } from '/imports/api/properties/Items.js'; import { NoteSchema } from '/imports/api/properties/Notes.js'; import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js'; import { RollSchema } from '/imports/api/properties/Rolls.js'; -import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; +import { ComputedSavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; import { ComputedSkillSchema } from '/imports/api/properties/Skills.js'; import { SlotSchema } from '/imports/api/properties/Slots.js'; -import { SpellSchema } from '/imports/api/properties/Spells.js'; -import { SpellListSchema } from '/imports/api/properties/SpellLists.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'; const propertySchemasIndex = { - action: ActionSchema, + action: ComputedActionSchema, adjustment: AdjustmentSchema, - attack: AttackSchema, + attack: ComputedAttackSchema, attribute: ComputedAttributeSchema, buff: BuffSchema, classLevel: ClassLevelSchema, @@ -37,11 +37,11 @@ const propertySchemasIndex = { note: NoteSchema, proficiency: ProficiencySchema, roll: RollSchema, - savingThrow: SavingThrowSchema, + savingThrow: ComputedSavingThrowSchema, skill: ComputedSkillSchema, slot: SlotSchema, - spellList: SpellListSchema, - spell: SpellSchema, + spellList: ComputedSpellSchema, + spell: ComputedSpellListSchema, toggle: ToggleSchema, container: ContainerSchema, item: ItemSchema, diff --git a/app/imports/api/properties/subSchemas/ErrorSchema.js b/app/imports/api/properties/subSchemas/ErrorSchema.js index 658f8b53..9c3011ff 100644 --- a/app/imports/api/properties/subSchemas/ErrorSchema.js +++ b/app/imports/api/properties/subSchemas/ErrorSchema.js @@ -1,11 +1,9 @@ import SimpleSchema from 'simpl-schema'; const ErrorSchema = new SimpleSchema({ - // The roll that determines how much to change the attribute message: { type: String, }, - // Who this adjustment applies to type: { type: String, }, diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index 6f35ec77..132e77f1 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -276,9 +276,10 @@ subheader > Attacks - { + action.children = getActiveProperties({ + ancestorId: action._id, + options: {sort: {order: 1}}, + }); + return action; + }); + return props; }, attacks(){ - return getProperties(this.creature, {type: 'attack'}); + let props = getProperties(this.creature, {type: 'attack'}).map(attack => { + attack.children = getActiveProperties({ + ancestorId: attack._id, + options: {sort: {order: 1}}, + }); + return attack; + }); + return props; }, }, methods: { diff --git a/app/imports/ui/properties/components/actions/ActionListTile.vue b/app/imports/ui/properties/components/actions/ActionListTile.vue index e90d8506..a7ffe8f5 100644 --- a/app/imports/ui/properties/components/actions/ActionListTile.vue +++ b/app/imports/ui/properties/components/actions/ActionListTile.vue @@ -3,26 +3,69 @@ class="ability-list-tile" v-on="hasClickListener ? {click} : {}" > + + {{ rollBonus }} + {{ model.name }} + + + + {{ model.usesResult - (model.usesUsed) }}/{{ model.usesResult }} + + - - diff --git a/app/imports/ui/properties/viewers/ActionViewer.vue b/app/imports/ui/properties/viewers/ActionViewer.vue index 5a1790d4..fd00f12d 100644 --- a/app/imports/ui/properties/viewers/ActionViewer.vue +++ b/app/imports/ui/properties/viewers/ActionViewer.vue @@ -1,31 +1,33 @@