Migrated creature computations to use the new data structure for creature properties

This commit is contained in:
Stefan Zermatten
2020-01-27 11:21:52 +02:00
parent 4ee7307e34
commit c3cc4c881d
5 changed files with 135 additions and 191 deletions

View File

@@ -5,11 +5,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import Creatures from "/imports/api/creature/Creatures.js";
import Attributes from "/imports/api/properties/Attributes.js";
import Skills from "/imports/api/properties/Skills.js";
import Effects from "/imports/api/properties/Effects.js";
import Proficiencies from "/imports/api/properties/Proficiencies.js";
import DamageMultipliers from "/imports/api/properties/DamageMultipliers.js";
import CreatureProperties from "/imports/api/creature/CreatureProperties.js";
import * as math from 'mathjs';
import parser from '/imports/parser/parser.js';
if (Meteor.isClient) console.log({parser});
@@ -89,7 +85,7 @@ export function recomputeCreatureById(charId){
*/
function writeCreature(char) {
writeAttributes(char);
writeSkills(char);
writeCreatureProperties(char);
writeDamageMultipliers(char);
writeEffects(char);
writeCreatureDoc(char);
@@ -113,7 +109,7 @@ function writeAttributes(char) {
let bulkWriteOps = _.map(char.atts, (att, variableName) => {
let op = {
updateMany: {
filter: {charId: char.id, variableName},
filter: {'ancestors.id': char.id, variableName},
update: {$set: {
value: att.result,
}},
@@ -127,12 +123,12 @@ function writeAttributes(char) {
return op;
});
if (Meteor.isServer){
Attributes.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e, r){
CreatureProperties.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2));
});
} else {
_.each(bulkWriteOps, op => {
Attributes.update(op.updateMany.filter, op.updateMany.update, {multi: true});
CreatureProperties.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
@@ -147,27 +143,28 @@ function writeEffects(char){
},
}));
if (Meteor.isServer){
Effects.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e, r){
CreatureProperties.rawCollection().bulkWrite(bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2));
});
} else {
_.each(bulkWriteOps, op => {
Effects.update(op.updateOne.filter, op.updateOne.update);
CreatureProperties.update(op.updateOne.filter, op.updateOne.update);
});
}
}
/**
* Write all the skills from the in-memory char object to the Skills docs
* Write all the Creature Properties from the in-memory char object to the
* properties docs
*
* @param {type} char description
* @returns {type} description
*/
function writeSkills(char) {
function writeCreatureProperties(char) {
let bulkWriteOps = _.map(char.skills, (skill, variableName) => {
let op = {
updateMany: {
filter: {charId: char.id, variableName},
filter: {'ancestors.id': char.id, variableName},
update: {$set: {
value: skill.result,
abilityMod: skill.abilityMod,
@@ -182,12 +179,12 @@ function writeSkills(char) {
return op;
});
if (Meteor.isServer){
Skills.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
CreatureProperties.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2));
});
} else {
_.each(bulkWriteOps, op => {
Skills.update(op.updateMany.filter, op.updateMany.update, {multi: true});
CreatureProperties.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
@@ -202,7 +199,7 @@ function writeDamageMultipliers(char) {
let bulkWriteOps = _.map(char.dms, (dm, variableName) => {
let op = {
updateMany: {
filter: {charId: char.id, variableName},
filter: {'ancestors.id': char.id, variableName},
update: {$set: {
value: dm.result,
}},
@@ -211,12 +208,12 @@ function writeDamageMultipliers(char) {
return op;
});
if (Meteor.isServer){
DamageMultipliers.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
CreatureProperties.rawCollection().bulkWrite( bulkWriteOps, {ordered : false}, function(e, r){
if (e) console.warn(JSON.stringify(e, null, 2));
});
} else {
_.each(bulkWriteOps, op => {
DamageMultipliers.update(op.updateMany.filter, op.updateMany.update, {multi: true});
CreatureProperties.update(op.updateMany.filter, op.updateMany.update, {multi: true});
});
}
}
@@ -224,7 +221,7 @@ function writeDamageMultipliers(char) {
/**
* Get the creature's data from the database and build an in-memory model that
* can be computed. Hits 7 database collections with indexed queries.
* can be computed.
*
* @param {type} charId description
* @returns {type} description
@@ -241,162 +238,117 @@ function buildCreature(charId){
computedEffects: [],
level: 0,
};
// Fetch the attributes of the creature and add them to an object for quick lookup
Attributes.find({charId}).forEach(attribute => {
const key = attribute.variableName;
if (!char.atts[key]){
// Fetch the properties of the creature and add them to the char object for
// quicker lookup
CreatureProperties.find({'ancestors.id': charId}).forEach(prop => {
const key = prop.variableName;
// Attributes
if (prop.type === 'attribute'){
char.atts[key] = {
computed: false,
busyComputing: false,
type: "attribute",
attributeType: attribute.type,
base: attribute.baseValue || 0,
decimal: attribute.decimal,
attributeType: prop.attributeType,
base: prop.baseValue || 0,
decimal: prop.decimal,
result: 0,
mod: 0, // The resulting modifier if this is an ability
add: 0,
mul: 1,
min: Number.NEGATIVE_INFINITY,
max: Number.POSITIVE_INFINITY,
effects: [],
};
char.variables[key] = char.atts[key];
if (attribute.type === 'ability' && !char.variables[key + "Mod"]){
char.variables[key + "Mod"] = {
type: "abilityMod",
ability: char.atts[key],
get result(){
return this.ability.mod;
},
get computed(){
return this.ability.computed;
},
};
}
}
});
// Fetch the skills of the creature and store them
Skills.find({charId}).forEach(skill => {
const key = skill.variableName;
if (!char.skills[key]){
char.skills[key] = {
computed: false,
busyComputing: false,
type: "skill",
ability: skill.ability,
base: skill.baseValue,
result: 0, // For skills the result is the skillMod
proficiency: skill.baseProficiency || 0,
add: 0,
mul: 1,
min: Number.NEGATIVE_INFINITY,
max: Number.POSITIVE_INFINITY,
advantage: 0,
disadvantage: 0,
passiveAdd: 0,
fail: 0,
conditional: 0,
effects: [],
proficiencies: [],
};
if (!char.variables[key]){
char.variables[key] = char.skills[key];
char.variables[key] = char.atts[key];
}
//Skill
else if (prop.type === 'skill'){
if (!char.skills[key]){
char.skills[key] = {
computed: false,
busyComputing: false,
type: "skill",
ability: prop.ability,
base: prop.baseValue,
result: 0, // For skills the result is the skillMod
proficiency: prop.baseProficiency || 0,
add: 0,
mul: 1,
min: Number.NEGATIVE_INFINITY,
max: Number.POSITIVE_INFINITY,
advantage: 0,
disadvantage: 0,
passiveAdd: 0,
fail: 0,
conditional: 0,
effects: [],
proficiencies: [],
};
if (!char.variables[key]){
char.variables[key] = char.skills[key];
}
}
}
});
// Fetch the damage multipliers of the creature and store them
DamageMultipliers.find({charId}).forEach(damageMultiplier =>{
const key = damageMultiplier.variableName;
if (!char.dms[key]){
char.dms[key] = {
// Damage multipliers
else if (prop.type === 'damageMultiplier'){
if (!char.dms[key]){
char.dms[key] = {
computed: false,
busyComputing: false,
type: "damageMultiplier",
result: 0,
immunityCount: 0,
ressistanceCount: 0,
vulnerabilityCount: 0,
effects: [],
};
if (!char.variables[key]){
char.variables[key] = char.dms[key];
}
}
}
// Classes
//TODO
// Effects
else if (prop.type === 'effect'){
let storedEffect = {
_id: effect._id,
computed: false,
busyComputing: false,
type: "damageMultiplier",
result: 0,
immunityCount: 0,
ressistanceCount: 0,
vulnerabilityCount: 0,
effects: [],
operation: prop.operation,
calculation: prop.calculation,
};
if (!char.variables[key]){
char.variables[key] = char.dms[key];
if (char.atts[effect.stat]) {
char.atts[effect.stat].effects.push(storedEffect);
} else if (char.skills[effect.stat]) {
char.skills[effect.stat].effects.push(storedEffect);
} else if (char.dms[effect.stat]) {
char.dms[effect.stat].effects.push(storedEffect);
} else {
char.otherEffects.push(storedEffect);
}
}
});
// Fetch the class levels and store them
// don't use the word "class" it's reserved
const levelOverwritten = !!char.variables.level;
if (!levelOverwritten){
char.variables.level = {
result: 0,
type: 'characterLevel',
computed: true,
};
}
Classes.find({charId}).forEach(cls => {
const strippedCls = cls.name.replace(/\s+/g, '');
if (!char.classes[strippedCls]){
char.classes[strippedCls] = {level: cls.level};
char.level += cls.level;
if (!char.variables[strippedCls]){
char.variables[strippedCls + "Level"] = {
result: cls.level,
type: 'classLevel',
// Proficiencies
else if (prop.type === 'proficiency'){
if (char.skills[prop.skill]) {
char.skills[prop.skill].proficiencies.push(proficiency);
}
}
// Add direct properties from creature to variable list
const fields = { xp: 1, weightCarried: 1};
const creature = Creatures.findOne(charId, {fields});
for (let key in fields){
if (!char.variables[key]){
char.variables[key] = {
result: creature[key] || 0,
type: 'creatureProperty',
computed: true,
};
}
if (!levelOverwritten){
char.variables.level.result = char.level;
}
}
});
// Add direct properties from creature to variable list
const fields = { xp: 1, weightCarried: 1};
const creature = Creatures.findOne(charId, {fields});
for (let key in fields){
if (!char.variables[key]){
char.variables[key] = {
result: creature[key] || 0,
type: 'creatureProperty',
computed: true,
};
}
}
// Fetch the effects which apply to each stat and store them under the attribute
Effects.find({
charId: charId,
enabled: true,
}).forEach(effect => {
let storedEffect = {
_id: effect._id,
computed: false,
result: 0,
operation: effect.operation,
calculation: effect.calculation,
};
if (char.atts[effect.stat]) {
char.atts[effect.stat].effects.push(storedEffect);
} else if (char.skills[effect.stat]) {
char.skills[effect.stat].effects.push(storedEffect);
} else if (char.dms[effect.stat]) {
char.dms[effect.stat].effects.push(storedEffect);
} else {
char.otherEffects.push(storedEffect);
}
});
// Fetch the proficiencies and store them under each skill
Proficiencies.find({
charId: charId,
enabled: true,
}).forEach(proficiency => {
if (char.skills[proficiency.skill]) {
char.skills[proficiency.skill].proficiencies.push(proficiency);
}
});
return char;
@@ -437,10 +389,6 @@ export function computeCreature(char){
* @returns {type} description
*/
function computeStat(stat, char){
// Ability mods aren't stats, use the stat they are based off of
if (stat.type === 'abilityMod'){
stat = stat.ability;
}
// If the stat is already computed, skip it
if (stat.computed) return;
@@ -455,7 +403,6 @@ function computeStat(stat, char){
return;
}
// Iterate over each effect which applies to the stat
for (let i in stat.effects){
computeEffect(stat.effects[i], char);
@@ -540,7 +487,6 @@ function applyEffect(effect, stat){
}
}
/**
* Combine the results of multiple effects to get the result of the stat
*/
@@ -554,7 +500,6 @@ function combineStat(stat, char){
}
}
/**
* combineAttribute - Combine attributes's results into final values
*/
@@ -568,7 +513,6 @@ function combineAttribute(stat, char){
}
}
/**
* Combine skills results into final values
*/
@@ -685,7 +629,6 @@ export const recomputeCreatureXP = new ValidatedMethod({
},
});
/**
* Recompute a character's weight carried from a given id
*/