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

@@ -16,3 +16,4 @@ notices-for-facebook-graph-api-2
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze

View File

@@ -3,7 +3,7 @@
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
accounts-password@1.5.1
accounts-password@1.5.2
accounts-ui@1.3.1
random@1.1.0
dburles:collection-helpers
@@ -13,26 +13,26 @@ matb33:collection-hooks
momentjs:moment
dburles:mongo-collection-instances
percolate:migrations
accounts-google@1.3.2
accounts-google@1.3.3
splendido:accounts-meld
email@1.2.3
meteorhacks:subs-manager
chuangbo:marked
meteor-base@1.4.0
mobile-experience@1.0.5
mongo@1.6.2
mongo@1.8.0
session@1.2.0
jquery@1.11.10
tracker@1.2.0
logging@1.1.20
reload@1.3.0
ejson@1.1.0
ejson@1.1.1
check@1.3.1
standard-minifier-js@2.4.1
standard-minifier-js@2.6.0
shell-server@0.4.0
seba:minifiers-autoprefixer
templates:array
ecmascript@0.12.4
ecmascript@0.14.0
es5-shim@4.8.0
reactive-dict@1.3.0
percolate:synced-cron

View File

@@ -1 +1 @@
METEOR@1.8.1
METEOR@1.9

View File

@@ -1,28 +1,28 @@
accounts-base@1.4.4
accounts-base@1.5.0
accounts-google@1.3.3
accounts-oauth@1.1.16
accounts-password@1.5.1
accounts-password@1.5.3
accounts-ui@1.3.1
accounts-ui-unstyled@1.4.2
akryum:npm-check@0.1.2
akryum:vue-component@0.15.0
akryum:vue-component-dev-client@0.4.6
akryum:vue-component@0.15.2
akryum:vue-component-dev-client@0.4.7
akryum:vue-component-dev-server@0.1.4
akryum:vue-router2@0.2.3
aldeed:collection2@3.0.2
aldeed:collection2@3.0.6
aldeed:schema-index@3.0.0
allow-deny@1.1.0
autoupdate@1.6.0
babel-compiler@7.3.4
babel-runtime@1.3.0
babel-compiler@7.5.1
babel-runtime@1.5.0
base64@1.0.12
binary-heap@1.0.11
blaze@2.3.3
blaze@2.3.4
blaze-tools@1.0.10
boilerplate-generator@1.6.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
callback-hook@1.1.0
callback-hook@1.3.0
check@1.3.1
chuangbo:marked@0.3.5_1
dburles:collection-helpers@1.1.0
@@ -35,11 +35,11 @@ ddp-server@2.3.0
deps@1.0.12
diff-sequence@1.1.1
dynamic-import@0.5.1
ecmascript@0.12.7
ecmascript@0.14.1
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.8.0
ecmascript-runtime-server@0.7.1
ejson@1.1.0
ecmascript-runtime-client@0.10.0
ecmascript-runtime-server@0.9.0
ejson@1.1.1
email@1.2.3
es5-shim@4.8.0
fetch@0.1.1
@@ -60,30 +60,30 @@ livedata@1.0.18
lmieulet:meteor-coverage@1.1.4
localstorage@1.2.0
logging@1.1.20
matb33:collection-hooks@0.8.4
matb33:collection-hooks@0.9.1
mdg:validated-method@1.2.0
meteor@1.9.3
meteor-base@1.4.0
meteorhacks:picker@1.0.3
meteorhacks:subs-manager@1.6.4
meteortesting:browser-tests@1.0.0
meteortesting:mocha@1.1.3
meteortesting:mocha-core@1.0.1
minifier-css@1.4.2
minifier-js@2.4.1
meteortesting:mocha@1.1.4
meteortesting:mocha-core@7.0.1
minifier-css@1.5.0
minifier-js@2.6.0
minimongo@1.4.5
mobile-experience@1.0.5
mobile-status-bar@1.0.14
modern-browsers@0.1.4
modules@0.13.0
modules-runtime@0.10.3
modern-browsers@0.1.5
modules@0.15.0
modules-runtime@0.12.0
momentjs:moment@2.24.0
mongo@1.6.3
mongo@1.8.0
mongo-decimal@0.1.1
mongo-dev-server@1.1.0
mongo-id@1.0.7
npm-bcrypt@0.9.3
npm-mongo@3.1.2
npm-mongo@3.3.0
oauth@1.2.8
oauth2@1.2.1
observe-sequence@1.0.16
@@ -111,7 +111,7 @@ spacebars-compiler@1.1.3
splendido:accounts-emails-field@1.2.0
splendido:accounts-meld@1.3.1
srp@1.0.12
standard-minifier-js@2.4.1
standard-minifier-js@2.6.0
static-html@1.2.2
templates:array@1.0.3
templating@1.3.2
@@ -122,5 +122,5 @@ tmeasday:check-npm-versions@0.3.2
tracker@1.2.0
underscore@1.0.10
url@1.2.0
webapp@1.7.4
webapp@1.8.2
webapp-hashing@1.0.9

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
*/