Creature computations working again
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ChildSchema, { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||
import { recomputeCreature } from '/imports/api/creature/computation/creatureComputation.js';
|
||||
import { recomputeCreature } from '/imports/api/creature/computation/recomputeCreature.js';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||
import { softRemove } from '/imports/api/parenting/softRemove.js';
|
||||
|
||||
@@ -34,6 +34,7 @@ export default class ComputationMemo {
|
||||
if (this.statsByVariableName[variableName]){
|
||||
prop.value = NaN;
|
||||
prop.computationDetails.error = 'variableNameCollision';
|
||||
console.warn('variableNameCollision', prop);
|
||||
return;
|
||||
}
|
||||
this.statsByVariableName[variableName] = prop;
|
||||
|
||||
@@ -25,7 +25,6 @@ function combineAttribute(stat, aggregator){
|
||||
|
||||
function combineSkill(stat, aggregator, memo){
|
||||
// Skills are based on some ability Modifier
|
||||
let abilityMod = 0;
|
||||
let ability = memo.statsByVariableName[stat.ability]
|
||||
if (stat.ability && ability){
|
||||
if (!ability.computationDetails.computed){
|
||||
@@ -48,9 +47,9 @@ function combineSkill(stat, aggregator, memo){
|
||||
// Multiply the proficiency bonus by the actual proficiency
|
||||
profBonus *= stat.proficiency;
|
||||
// Combine everything to get the final result
|
||||
let result = (abilityMod + profBonus + stat.add) * stat.mul;
|
||||
if (result < stat.min) result = stat.min;
|
||||
if (result > stat.max) result = stat.max;
|
||||
let result = (stat.abilityMod + profBonus + aggregator.add) * aggregator.mul;
|
||||
if (result < aggregator.min) result = aggregator.min;
|
||||
if (result > aggregator.max) result = aggregator.max;
|
||||
result = Math.floor(result);
|
||||
if (stat.base > result) result = stat.base;
|
||||
stat.value = result;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import evaluateCalculation from '/imports/api/creature/computation/evaluateCalculation.js';
|
||||
|
||||
export default function computeEffect(effect, memo){
|
||||
if (effect.computed) return;
|
||||
if (effect.computationDetails.computed) return;
|
||||
if (_.isFinite(effect.calculation)){
|
||||
effect.result = +effect.calculation;
|
||||
} else if(effect.operation === "conditional" || effect.operation === "rollBonuses"){
|
||||
} else if(effect.operation === "conditional" || effect.operation === "rollBonus"){
|
||||
effect.result = effect.calculation;
|
||||
} else if(_.contains(["advantage", "disadvantage", "fail"], effect.operation)){
|
||||
effect.result = 1;
|
||||
|
||||
@@ -13,6 +13,7 @@ export default function computeStat(stat, memo){
|
||||
stat.value = NaN;
|
||||
stat.computationDetails.busyComputing = false;
|
||||
stat.computationDetails.error = 'dependencyLoop';
|
||||
console.warn('dependencyLoop', stat);
|
||||
return;
|
||||
}
|
||||
// Compute and aggregate all the effects
|
||||
|
||||
@@ -4,7 +4,7 @@ export default function computedValueOfVariableName(sub, memo){
|
||||
const stat = memo.statsByVariableName[sub];
|
||||
if (!stat) return null;
|
||||
if (!stat.computationDetails.computed){
|
||||
computeStat(stat, char);
|
||||
computeStat(stat, memo);
|
||||
}
|
||||
return stat.result;
|
||||
return stat.value;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import computedValueOfVariableName from '/imports/api/creature/computation/computedValueOfVariableName.js'
|
||||
import * as math from 'mathjs';
|
||||
|
||||
export default function evaluateCalculation(string, memo){
|
||||
if (!string) return string;
|
||||
@@ -7,6 +8,7 @@ export default function evaluateCalculation(string, memo){
|
||||
try {
|
||||
calc = math.parse(string);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return string;
|
||||
}
|
||||
// Replace all symbols with known values
|
||||
@@ -20,7 +22,6 @@ export default function evaluateCalculation(string, memo){
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
// Evaluate the expression to a number or return with substitutions
|
||||
try {
|
||||
return substitutedCalc.eval();
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { isEqual, forOwn } from 'lodash';
|
||||
import { ComputedOnlySkilLSchema } from '/imports/api/properties/Skills.js';
|
||||
|
||||
export default function logAlterations(memo){
|
||||
forOwn(memo.originalPropsById, old => {
|
||||
let changed = memo.propsById[old._id];
|
||||
delete changed.computationDetails;
|
||||
|
||||
if (!isEqual(old, changed)){
|
||||
console.log({change: {old, changed}})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO use this as a starting point to write only computed fields that have
|
||||
// changed
|
||||
@@ -1,14 +1,10 @@
|
||||
// TODO allow abilities to get advantage/disadvantage, making all skills that are based
|
||||
// on them disadvantaged as well
|
||||
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||
import ComputationMemo from '/imports/api/creature/computation/ComputationMemo.js';
|
||||
import computeMemo from '/imports/api/creature/computation/computeMemo.js';
|
||||
import getCalculationProperties from '/imports/api/creature/computation/getCalculationProperties.js';
|
||||
import logAlterations from '/imports/api/creature/computation/logAlterations.js';
|
||||
import * as math from 'mathjs';
|
||||
import writeAlteredProperties from '/imports/api/creature/computation/writeAlteredProperties.js';
|
||||
|
||||
export const recomputeCreature = new ValidatedMethod({
|
||||
|
||||
@@ -67,10 +63,7 @@ export const recomputeCreature = new ValidatedMethod({
|
||||
export function recomputeCreatureById(creatureId){
|
||||
let props = getCalculationProperties(creatureId);
|
||||
let computationMemo = new ComputationMemo(props);
|
||||
console.log({toCompute: computationMemo});
|
||||
computeMemo(computationMemo);
|
||||
console.log({computed: computationMemo});
|
||||
logAlterations(computationMemo);
|
||||
//writeAlteredProps(computationMemo);
|
||||
writeAlteredProperties(computationMemo);
|
||||
return computationMemo;
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
import { isEqual, forOwn } from 'lodash';
|
||||
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 CreatureProperties from '/imports/api/creature/CreatureProperties.js';
|
||||
|
||||
export default function writeAlteredProperties(memo){
|
||||
let bulkWriteOperations = [];
|
||||
// Loop through all properties on the memo
|
||||
forOwn(memo.originalPropsById, (original, _id) => {
|
||||
let changed = memo.propsById[_id];
|
||||
|
||||
let schema;
|
||||
switch (changed.type){
|
||||
case 'skill':
|
||||
schema = ComputedOnlySkillSchema;
|
||||
break;
|
||||
case 'attribute':
|
||||
schema = ComputedOnlyAttributeSchema;
|
||||
break;
|
||||
case 'effect':
|
||||
schema = ComputedOnlyEffectSchema;
|
||||
break;
|
||||
}
|
||||
let op = undefined;
|
||||
// Loop through all keys that can be changed by computation
|
||||
// and compile an operation that sets all those keys
|
||||
for (let key of schema.objectKeys()){
|
||||
if (!isEqual(original[key], changed[key])){
|
||||
if (!op) op = newOperation(_id, changed.type);
|
||||
op.updateOne.update.$set[key] = changed[key];
|
||||
}
|
||||
}
|
||||
if (op){
|
||||
bulkWriteOperations.push(op);
|
||||
}
|
||||
});
|
||||
bulkWriteProperties(bulkWriteOperations);
|
||||
}
|
||||
|
||||
function newOperation(_id, type){
|
||||
let newOp = {
|
||||
updateOne: {
|
||||
filter: {_id},
|
||||
update: {$set: {}},
|
||||
}
|
||||
};
|
||||
if (Meteor.isClient){
|
||||
newOp.type = type;
|
||||
}
|
||||
return newOp;
|
||||
};
|
||||
|
||||
function bulkWriteProperties(bulkWriteOps){
|
||||
if (!bulkWriteOps.length) return;
|
||||
if (Meteor.isServer){
|
||||
CreatureProperties.rawCollection().bulkWrite(
|
||||
bulkWriteOps,
|
||||
{ordered : false},
|
||||
function(e){
|
||||
if (e) console.error(e);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
_.each(bulkWriteOps, op => {
|
||||
CreatureProperties.update(op.updateOne.filter, op.updateOne.update, {
|
||||
selector: {type: op.type}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
|
||||
function writeCreature(char) {
|
||||
//TODO these functions don't filter the stats before trying to write
|
||||
writeAttributes(char);
|
||||
writeSkills(char);
|
||||
writeDamageMultipliers(char);
|
||||
writeEffects(char);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write all the attributes from the in-memory char object to the Attirbute docs
|
||||
*/
|
||||
function writeAttributes(char) {
|
||||
let bulkWriteOps = _.map(char.atts, (att, variableName) => {
|
||||
let op = {
|
||||
updateMany: {
|
||||
filter: {'ancestors.id': char.id, variableName},
|
||||
update: {'$set': {
|
||||
value: att.result,
|
||||
rollBonuses: skill.rollBonus,
|
||||
}},
|
||||
}
|
||||
};
|
||||
if (typeof att.mod === 'number'){
|
||||
op.updateMany.update.$set.mod = att.mod;
|
||||
} else {
|
||||
op.updateMany.update.$unset = {mod: 1};
|
||||
}
|
||||
return op;
|
||||
});
|
||||
bulkWriteProperties({bulkWriteOps, selectorType: 'attribute'});
|
||||
}
|
||||
|
||||
function writeSkills(char) {
|
||||
let bulkWriteOps = _.map(char.skills, (skill, variableName) => {
|
||||
let op = {
|
||||
updateMany: {
|
||||
filter: {'ancestors.id': char.id, variableName},
|
||||
update: {$set: {
|
||||
value: skill.result,
|
||||
abilityMod: skill.abilityMod,
|
||||
advantage: skill.advantage,
|
||||
passiveBonus: skill.passiveAdd,
|
||||
proficiency: skill.proficiency,
|
||||
conditionalBenefits: skill.conditional,
|
||||
rollBonuses: skill.rollBonus,
|
||||
fail: skill.fail,
|
||||
}},
|
||||
}
|
||||
};
|
||||
return op;
|
||||
});
|
||||
bulkWriteProperties({bulkWriteOps, selectorType: 'skill'});
|
||||
}
|
||||
|
||||
function writeDamageMultipliers(char) {
|
||||
let bulkWriteOps = _.map(char.dms, (dm, variableName) => {
|
||||
let op = {
|
||||
updateMany: {
|
||||
filter: {'ancestors.id': char.id, variableName},
|
||||
update: {$set: {
|
||||
value: dm.result,
|
||||
}},
|
||||
}
|
||||
};
|
||||
return op;
|
||||
});
|
||||
bulkWriteProperties({bulkWriteOps, selectorType: 'damageMultiplier'});
|
||||
}
|
||||
|
||||
function writeEffects(char){
|
||||
let bulkWriteOps = _.map(char.computedEffects, effect => ({
|
||||
updateOne: {
|
||||
filter: {_id: effect._id},
|
||||
update: {$set: {
|
||||
result: effect.result,
|
||||
}},
|
||||
},
|
||||
}));
|
||||
if (!bulkWriteOps.length) return;
|
||||
bulkWriteProperties({bulkWriteOps, selectorType: 'effect'});
|
||||
}
|
||||
|
||||
function bulkWriteProperties({bulkWriteOps, selectorType}){
|
||||
if (!bulkWriteOps.length) return;
|
||||
if (Meteor.isServer){
|
||||
CreatureProperties.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e){
|
||||
if (e) console.error(e);
|
||||
});
|
||||
} else {
|
||||
_.each(bulkWriteOps, op => {
|
||||
CreatureProperties.update(op.updateMany.filter, op.updateMany.update, {
|
||||
multi: true,
|
||||
selector: {type: selectorType}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import {computeCreature} from "./creatureComputation.js";
|
||||
import {computeCreature} from "./recomputeCreature.js";
|
||||
import assert from "assert";
|
||||
|
||||
const makeEffect = function(operation, value){
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { recomputeCreatureById } from '/imports/api/creature/computation/creatureComputation.js';
|
||||
import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js';
|
||||
|
||||
export default function recomputeCreatureMixin(methodOptions){
|
||||
let runFunc = methodOptions.run;
|
||||
|
||||
@@ -196,13 +196,6 @@ export function updateParent({docRef, parentRef}){
|
||||
});
|
||||
}
|
||||
|
||||
// TODO move these functions to character properties collection
|
||||
export function findEnabled(collection, query, options){
|
||||
query.enabled = true;
|
||||
query['ancestors.$.enabled'] = {$not: false};
|
||||
return collection.find(query, options);
|
||||
}
|
||||
|
||||
export function getName(doc){
|
||||
if (doc.name) return name;
|
||||
var i = doc.ancestors.length;
|
||||
|
||||
@@ -67,7 +67,7 @@ let AttributeSchema = new SimpleSchema({
|
||||
},
|
||||
});
|
||||
|
||||
let ComputedAttributeSchema = new SimpleSchema({
|
||||
let ComputedOnlyAttributeSchema = new SimpleSchema({
|
||||
// The computed value of the attribute
|
||||
value: {
|
||||
type: Number,
|
||||
@@ -78,6 +78,10 @@ let ComputedAttributeSchema = new SimpleSchema({
|
||||
type: SimpleSchema.Integer,
|
||||
optional: true,
|
||||
},
|
||||
}).extend(AttributeSchema);
|
||||
});
|
||||
|
||||
export { AttributeSchema, ComputedAttributeSchema };
|
||||
const ComputedAttributeSchema = new SimpleSchema()
|
||||
.extend(ComputedOnlyAttributeSchema)
|
||||
.extend(AttributeSchema);
|
||||
|
||||
export { AttributeSchema, ComputedOnlyAttributeSchema, ComputedAttributeSchema };
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
|
||||
// TODO consider damage types as an array that applies to multiple types at once
|
||||
|
||||
/*
|
||||
* DamageMultipliers are multipliers that affect how much damage is taken from
|
||||
|
||||
@@ -51,12 +51,16 @@ const StoredEffectSchema = new SimpleSchema({
|
||||
},
|
||||
}).extend(EffectSchema);
|
||||
|
||||
const ComputedEffectSchema = new SimpleSchema({
|
||||
const ComputedOnlyEffectSchema = new SimpleSchema({
|
||||
// The computed result of the effect
|
||||
result: {
|
||||
type: SimpleSchema.oneOf(Number, String),
|
||||
optional: true,
|
||||
},
|
||||
}).extend(EffectSchema);
|
||||
})
|
||||
|
||||
export { EffectSchema, StoredEffectSchema, ComputedEffectSchema };
|
||||
const ComputedEffectSchema = new SimpleSchema()
|
||||
.extend(ComputedOnlyEffectSchema)
|
||||
.extend(EffectSchema);
|
||||
|
||||
export { EffectSchema, StoredEffectSchema, ComputedEffectSchema, ComputedOnlyEffectSchema };
|
||||
|
||||
@@ -79,7 +79,7 @@ let ComputedOnlySkillSchema = new SimpleSchema({
|
||||
allowedValues: [0, 0.5, 1, 2],
|
||||
defaultValue: 0,
|
||||
},
|
||||
// Computed number of total conditional benefits
|
||||
// Compiled text of all conditional benefits
|
||||
conditionalBenefits: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
@@ -87,7 +87,7 @@ let ComputedOnlySkillSchema = new SimpleSchema({
|
||||
'conditionalBenefits.$': {
|
||||
type: String,
|
||||
},
|
||||
// Computed number of things forcing this skill to fail
|
||||
// Compiled text of all roll bonuses
|
||||
rollBonuses: {
|
||||
type: Array,
|
||||
optional: true,
|
||||
@@ -102,6 +102,8 @@ let ComputedOnlySkillSchema = new SimpleSchema({
|
||||
},
|
||||
})
|
||||
|
||||
let ComputedSkillSchema = ComputedOnlySkillSchema.extend(SkillSchema);
|
||||
const ComputedSkillSchema = new SimpleSchema()
|
||||
.extend(ComputedOnlySkillSchema)
|
||||
.extend(SkillSchema);
|
||||
|
||||
export { SkillSchema, ComputedSkillSchema, ComputedOnlySkillSchema };
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
import SpellsTab from '/imports/ui/creature/character/characterSheetTabs/SpellsTab.vue';
|
||||
import PersonaTab from '/imports/ui/creature/character/characterSheetTabs/PersonaTab.vue';
|
||||
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
|
||||
import { recomputeCreature } from '/imports/api/creature/computation/creatureComputation.js';
|
||||
import { recomputeCreature } from '/imports/api/creature/computation/recomputeCreature.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "/imports/server/publications/index.js";
|
||||
import "/imports/api/creature/computation/creatureComputation.js";
|
||||
import "/imports/api/parenting/deleteRemovedDocuments.js";
|
||||
import "/imports/server/config/simpleSchemaDebug.js";
|
||||
import "/imports/api/parenting/organizeMethods.js";
|
||||
|
||||
Reference in New Issue
Block a user