diff --git a/app/imports/api/creature/actions/applyAction.js b/app/imports/api/creature/actions/applyAction.js
index 5685edf9..189bd6a2 100644
--- a/app/imports/api/creature/actions/applyAction.js
+++ b/app/imports/api/creature/actions/applyAction.js
@@ -1,11 +1,9 @@
import spendResources from '/imports/api/creature/actions/spendResources.js'
-import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
-export default function applyAction({prop, creature}){
+export default function applyAction({prop, log}){
spendResources(prop);
- insertCreatureLog.call({
- log: {
- text: prop.name,
- creatureId: creature._id},
- });
+ // If this is not the top level action, we can add its name to the log
+ if (log.content.length){
+ log.content.push({name: prop.name});
+ }
}
diff --git a/app/imports/api/creature/actions/applyAdjustment.js b/app/imports/api/creature/actions/applyAdjustment.js
index bdd8d21c..ddc792ef 100644
--- a/app/imports/api/creature/actions/applyAdjustment.js
+++ b/app/imports/api/creature/actions/applyAdjustment.js
@@ -1,12 +1,12 @@
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
-import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
import damagePropertiesByName from '/imports/api/creature/creatureProperties/methods/damagePropertiesByName.js';
export default function applyAdjustment({
prop,
creature,
targets,
- actionContext
+ actionContext,
+ log
}){
let damageTargets = prop.target === 'self' ? [creature] : targets;
let scope = {
@@ -16,16 +16,10 @@ export default function applyAdjustment({
try {
var {result, errors} = evaluateString(prop.amount, scope, 'reduce');
if (typeof result !== 'number') {
- return insertCreatureLog.call({ log: {
- text: errors.join(', ') || 'Something went wrong',
- creatureId: creature._id,
- }});
+ log.content.push({error: errors.join(', ') || 'Something went wrong'});
}
} catch (e){
- return insertCreatureLog.call({ log: {
- text: e.toString(),
- creatureId: creature._id,
- }});
+ log.content.push({error: e.toString()});
}
if (damageTargets) {
damageTargets.forEach(target => {
@@ -38,19 +32,15 @@ export default function applyAdjustment({
operation: prop.operation || 'increment',
value: result
});
- insertCreatureLog.call({
- log: {
- text: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''} ${-result}`,
- creatureId: target._id,
- }
+ log.content.push({
+ resultPrefix: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''}`,
+ result: `${-result}`,
});
});
} else {
- insertCreatureLog.call({
- log: {
- text: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''} ${-result}`,
- creatureId: creature._id,
- }
+ log.content.push({
+ resultPrefix: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''}`,
+ result: `${-result}`,
});
}
}
diff --git a/app/imports/api/creature/actions/applyAttack.js b/app/imports/api/creature/actions/applyAttack.js
index 3da3d099..99e62ab5 100644
--- a/app/imports/api/creature/actions/applyAttack.js
+++ b/app/imports/api/creature/actions/applyAttack.js
@@ -1,18 +1,14 @@
import roll from '/imports/parser/roll.js';
-import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
export default function applyAttack({
prop,
- //children,
- creature,
- //targets,
- //actionContext
+ log,
}){
let result = roll(1, 20)[0] + prop.rollBonusResult;
- insertCreatureLog.call({
- log: {
- text: `${prop.name} attack. ${result} to hit`,
- creatureId: creature._id,
- }
+ log.content.push({
+ // If this is not the first item in the log content, give it a name
+ name: log.content.length ? prop.name + ' attack' : undefined,
+ result,
+ details: 'to hit',
});
}
diff --git a/app/imports/api/creature/actions/applyDamage.js b/app/imports/api/creature/actions/applyDamage.js
index 5c3fc3a1..35b2aabe 100644
--- a/app/imports/api/creature/actions/applyDamage.js
+++ b/app/imports/api/creature/actions/applyDamage.js
@@ -1,12 +1,13 @@
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
-import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
import dealDamage from '/imports/api/creature/creatureProperties/methods/dealDamage.js';
+import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js';
export default function applyDamage({
prop,
creature,
targets,
- actionContext
+ actionContext,
+ log,
}){
let damageTargets = prop.target === 'self' ? [creature] : targets;
let scope = {
@@ -16,18 +17,16 @@ export default function applyDamage({
try {
var {result, errors} = evaluateString(prop.amount, scope, 'reduce');
if (typeof result !== 'number') {
- return insertCreatureLog.call({ log: {
- text: errors.join(', '),
- creatureId: creature._id,
- }});
+ log.content.push({
+ error: errors.join(', '),
+ });
}
} catch (e){
- return insertCreatureLog.call({ log: {
- text: e.toString(),
- creatureId: creature._id,
- }});
+ log.content.push({
+ error: e.toString(),
+ });
}
- if (damageTargets) {
+ if (damageTargets && damageTargets.length) {
damageTargets.forEach(target => {
if (prop.target === 'each'){
result = evaluateString(prop.amount, scope, 'reduce');
@@ -44,20 +43,17 @@ export default function applyDamage({
}
});
if (target._id !== creature._id){
- insertCreatureLog.call({
- log: {
- text: `Dealt ${damageDealt} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`,
- creatureId: creature._id,
- }
+ log.content.push({
+ result: damageDealt,
+ details: `${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}` +
+ `${target.name && ' to '}${target.name}`,
});
}
});
} else {
- insertCreatureLog.call({
- log: {
- text: `${result} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`,
- creatureId: creature._id,
- }
+ log.content.push({
+ result,
+ details: `${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`,
});
}
}
diff --git a/app/imports/api/creature/actions/applyProperties.js b/app/imports/api/creature/actions/applyProperties.js
index fc66f053..ece1aee3 100644
--- a/app/imports/api/creature/actions/applyProperties.js
+++ b/app/imports/api/creature/actions/applyProperties.js
@@ -6,46 +6,49 @@ import applyBuff from '/imports/api/creature/actions/applyBuff.js';
function applyProperty(options){
let prop = options.prop;
- if (
- prop.disabled === true || // ignore disabled props
- prop.equipped === false || // ignore unequipped items
- prop.toggleResult === false || // ignore untoggled toggles
- prop.applied === true // ignore buffs that are already applied
- ){
+ if (prop.type === 'buff'){
+ // ignore only applied buffs
+ if (prop.applied === true){
+ return false;
+ }
+ // Ignore inactive props of other types
+ } else if (prop.inactive === true){
return false;
}
switch (prop.type){
case 'action':
case 'spell':
applyAction(options);
- return true;
+ break;
case 'attack':
applyAction(options);
applyAttack(options);
- return true;
+ break;
case 'damage':
applyDamage(options);
- return true;
+ break;
case 'adjustment':
applyAdjustment(options);
- return true;
+ break;
case 'buff':
applyBuff(options);
- return false;
+ break;
case 'roll':
// applyRoll(options);
- return true;
+ break;
case 'savingThrow':
// applySavingThrow(options);
- return false;
+ break;
}
+ return true;
}
export default function applyProperties({
forest,
creature,
targets,
- actionContext
+ actionContext,
+ log,
}){
forest.forEach(child => {
let walkChildren = applyProperty({
@@ -53,14 +56,16 @@ export default function applyProperties({
children: child.children,
creature,
targets,
- actionContext
+ actionContext,
+ log,
});
if (walkChildren){
applyProperties({
forest: child.children,
creature,
targets,
- actionContext
+ actionContext,
+ log,
});
}
});
diff --git a/app/imports/api/creature/actions/castSpellWithSlot.js b/app/imports/api/creature/actions/castSpellWithSlot.js
index d0da6a42..041008a4 100644
--- a/app/imports/api/creature/actions/castSpellWithSlot.js
+++ b/app/imports/api/creature/actions/castSpellWithSlot.js
@@ -66,6 +66,7 @@ const castSpellWithSlot = new ValidatedMethod({
context: {slotLevel},
creature,
target,
+ method: this,
});
// Note this only recomputes the top-level creature, not the nearest one
recomputeCreatureByDoc(creature);
diff --git a/app/imports/api/creature/actions/doAction.js b/app/imports/api/creature/actions/doAction.js
index 8c1e3a7c..80be4a13 100644
--- a/app/imports/api/creature/actions/doAction.js
+++ b/app/imports/api/creature/actions/doAction.js
@@ -3,6 +3,7 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
import Creatures from '/imports/api/creature/Creatures.js';
+import CreatureLogs, { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js';
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
@@ -13,10 +14,15 @@ const doAction = new ValidatedMethod({
name: 'creatureProperties.doAction',
validate: new SimpleSchema({
actionId: SimpleSchema.RegEx.Id,
- targetId: {
+ targetIds: {
+ type: Array,
+ defaultValue: [],
+ maxCount: 10,
+ optional: true,
+ },
+ 'targetIds.$': {
type: String,
regEx: SimpleSchema.RegEx.Id,
- optional: true,
},
}).validator(),
mixins: [RateLimiterMixin],
@@ -24,26 +30,41 @@ const doAction = new ValidatedMethod({
numRequests: 10,
timeInterval: 5000,
},
- run({actionId, targetId}) {
+ run({actionId, targetIds = []}) {
let action = CreatureProperties.findOne(actionId);
// Check permissions
let creature = getRootCreatureAncestor(action);
assertEditPermission(creature, this.userId);
- let target = undefined;
- if (targetId) {
- target = Creatures.findOne(targetId);
+ let targets = [];
+ targetIds.forEach(targetId => {
+ let target = Creatures.findOne(targetId);
assertEditPermission(target, this.userId);
- }
- doActionWork({action, creature, target});
- // Note this only recomputes the top-level creature, not the nearest one
+ targets.push(target);
+ });
+ doActionWork({action, creature, targets, method: this});
+
+ // recompute creatures
recomputeCreatureByDoc(creature);
- if (target){
+ targets.forEach(target => {
recomputeCreatureByDoc(target);
- }
+ });
},
});
-export function doActionWork({action, creature, target, context = {}}){
+export function doActionWork({
+ action,
+ creature,
+ targets,
+ context = {},
+ method
+}){
+ // Create the log
+ let log = CreatureLogSchema.clean({
+ name: action.name,
+ creatureId: creature._id,
+ creatureName: creature.name,
+ });
+
let decendantForest = nodesToTree({
collection: CreatureProperties,
ancestorId: action._id,
@@ -56,8 +77,10 @@ export function doActionWork({action, creature, target, context = {}}){
forest: startingForest,
actionContext: context,
creature,
- target,
+ targets,
+ log,
});
+ insertCreatureLogWork({log, creature, method});
}
export default doAction;
diff --git a/app/imports/api/creature/computation/afterComputation/evaluateString.js b/app/imports/api/creature/computation/afterComputation/evaluateString.js
index e012ff58..5bd780a5 100644
--- a/app/imports/api/creature/computation/afterComputation/evaluateString.js
+++ b/app/imports/api/creature/computation/afterComputation/evaluateString.js
@@ -23,7 +23,6 @@ export default function evaluateString(string, scope, fn = 'compile'){
errors.push('...');
return {result: string, errors};
}
- console.log(node);
let context = new CompilationContext();
let result = node[fn](scope, context);
if (result instanceof ConstantNode){
diff --git a/app/imports/api/creature/log/CreatureLogs.js b/app/imports/api/creature/log/CreatureLogs.js
index c617052e..d6f7bc76 100644
--- a/app/imports/api/creature/log/CreatureLogs.js
+++ b/app/imports/api/creature/log/CreatureLogs.js
@@ -1,5 +1,6 @@
import SimpleSchema from 'simpl-schema';
import Creatures from '/imports/api/creature/Creatures.js';
+import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import {assertEditPermission} from '/imports/api/creature/creaturePermissions.js';
@@ -13,13 +14,16 @@ if (Meteor.isServer){
let CreatureLogs = new Mongo.Collection('creatureLogs');
let CreatureLogSchema = new SimpleSchema({
- text: {
+ name: {
type: String,
+ optional: true,
},
- type: {
- type: String,
- allowedValues: ['roll', 'change', 'damage', 'info'],
- defaultValue: 'info',
+ content: {
+ type: Array,
+ defaultValue: [],
+ },
+ 'content.$': {
+ type: LogContentSchema,
},
// The real-world date that it occured, usually sorted by date
date: {
@@ -37,6 +41,10 @@ let CreatureLogSchema = new SimpleSchema({
regEx: SimpleSchema.RegEx.Id,
index: 1,
},
+ creatureName: {
+ type: String,
+ optional: true,
+ },
});
CreatureLogs.attachSchema(CreatureLogSchema);
@@ -73,7 +81,7 @@ const insertCreatureLog = new ValidatedMethod({
timeInterval: 5000,
},
validate: new SimpleSchema({
- log: CreatureLogSchema.omit('type', 'date'),
+ log: CreatureLogSchema.omit('date'),
}).validator(),
run({log}){
const creatureId = log.creatureId;
@@ -87,21 +95,27 @@ const insertCreatureLog = new ValidatedMethod({
}});
assertEditPermission(creature, this.userId);
// Build the new log
- if (typeof log === 'string'){
- log = {text: log};
- }
- log.date = new Date();
- // Insert it
- let id = CreatureLogs.insert(log);
- if (Meteor.isServer){
- this.unblock();
- removeOldLogs(creatureId);
- logWebhook({log, creature});
- }
+ let id = insertCreatureLogWork({log, creature, method: this})
return id;
},
});
+export function insertCreatureLogWork({log, creature, method}){
+ // Build the new log
+ if (typeof log === 'string'){
+ log = {text: log};
+ }
+ log.date = new Date();
+ // Insert it
+ let id = CreatureLogs.insert(log);
+ if (Meteor.isServer){
+ method.unblock();
+ removeOldLogs(creature._id);
+ logWebhook({log, creature});
+ }
+ return id;
+}
+
function equalIgnoringWhitespace(a, b){
if (typeof a !== 'string' || typeof b !== 'string') return a === b;
@@ -136,38 +150,42 @@ const logRoll = new ValidatedMethod({
}});
assertEditPermission(creature, this.userId);
let parsedResult = parse(roll);
- let logText;
+ let logContent;
if (parsedResult === null) {
- logText = 'Unexpected end of input';
+ logContent = [{error: 'Unexpected end of input'}];
}
else try {
- logText = [];
+ logContent = [];
let rollContext = new CompilationContext();
let compiled = parsedResult.compile(creature.variables, rollContext);
let compiledString = compiled.toString();
- if (!equalIgnoringWhitespace(compiledString, roll)) logText.push(roll);
- logText.push(compiledString);
+ if (!equalIgnoringWhitespace(compiledString, roll)) logContent.push({
+ result: roll
+ });
+ logContent.push({
+ details: compiledString
+ });
let rolled = compiled.roll(creature.variables, rollContext);
let rolledString = rolled.toString();
- if (rolledString !== compiledString) logText.push(rolled.toString());
+ if (rolledString !== compiledString) logContent.push({
+ details: rolled.toString()
+ });
let result = rolled.reduce(creature.variables, rollContext);
let resultString = result.toString();
- if (resultString !== rolledString) logText.push(resultString);
- logText = logText.join('\n\n');
+ if (resultString !== rolledString) logContent.push({
+ result: resultString
+ });
} catch (e){
- logText = 'Calculation error';
+ logContent = [{error: 'Calculation error'}];
}
const log = {
- text: logText,
+ content: logContent,
creatureId,
date: new Date(),
};
- let id = CreatureLogs.insert(log);
- if (Meteor.isServer){
- this.unblock();
- removeOldLogs(creatureId);
- logWebhook({log, creature});
- }
+
+ let id = insertCreatureLogWork({log, creature, method: this});
+
return id;
},
});
diff --git a/app/imports/api/creature/log/LogContentSchema.js b/app/imports/api/creature/log/LogContentSchema.js
new file mode 100644
index 00000000..824d9133
--- /dev/null
+++ b/app/imports/api/creature/log/LogContentSchema.js
@@ -0,0 +1,54 @@
+import SimpleSchema from 'simpl-schema';
+import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
+import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
+
+let LogContentSchema = new SimpleSchema({
+ name: {
+ type: String,
+ optional: true,
+ },
+ error: {
+ type: String,
+ optional: true,
+ },
+ resultPrefix: {
+ type: String,
+ optional: true,
+ },
+ result: {
+ type: String,
+ optional: true,
+ },
+ expandedResult: {
+ type: String,
+ optional: true,
+ },
+ details: {
+ type: String,
+ optional: true,
+ },
+ context: {
+ type: Object,
+ optional: true,
+ },
+ 'context.errors':{
+ type: Array,
+ defaultValue: [],
+ },
+ 'context.errors.$': {
+ type: ErrorSchema,
+ },
+ 'context.rolls': {
+ type: Array,
+ defaultValue: [],
+ },
+ 'context.rolls.$': {
+ type: RollDetailsSchema,
+ },
+ 'context.doubleRolls': {
+ type: Boolean,
+ optional: true,
+ },
+});
+
+export default LogContentSchema;
diff --git a/app/imports/api/properties/subSchemas/RollDetailsSchema.js b/app/imports/api/properties/subSchemas/RollDetailsSchema.js
new file mode 100644
index 00000000..4cf28ed4
--- /dev/null
+++ b/app/imports/api/properties/subSchemas/RollDetailsSchema.js
@@ -0,0 +1,19 @@
+import SimpleSchema from 'simpl-schema';
+
+const RollDetailsSchema = new SimpleSchema({
+ number: {
+ type: Number,
+ },
+ diceSize: {
+ type: Number,
+ },
+ values: {
+ type: Array,
+ defaultValue: [],
+ },
+ 'values.$': {
+ type: Number,
+ },
+});
+
+export default RollDetailsSchema;
diff --git a/app/imports/parser/parseTree/RollNode.js b/app/imports/parser/parseTree/RollNode.js
index be84e690..d61495df 100644
--- a/app/imports/parser/parseTree/RollNode.js
+++ b/app/imports/parser/parseTree/RollNode.js
@@ -30,14 +30,12 @@ export default class RollNode extends ParseNode {
return new ErrorNode({
node: this,
error: 'Number of dice is not an integer',
- previousNodes: [this, left, right],
});
}
if (!right.isInteger){
return new ErrorNode({
node: this,
error: 'Dice size is not an integer',
- previousNodes: [this, left, right],
});
}
let number = left.value;
diff --git a/app/imports/ui/creature/character/CharacterSheetRightDrawer.vue b/app/imports/ui/creature/character/CharacterSheetRightDrawer.vue
index 0e6338e7..f3af8445 100644
--- a/app/imports/ui/creature/character/CharacterSheetRightDrawer.vue
+++ b/app/imports/ui/creature/character/CharacterSheetRightDrawer.vue
@@ -10,7 +10,7 @@
+
+
diff --git a/app/imports/ui/properties/forms/ConstantForm.vue b/app/imports/ui/properties/forms/ConstantForm.vue
index ef4fd46b..e6978aeb 100644
--- a/app/imports/ui/properties/forms/ConstantForm.vue
+++ b/app/imports/ui/properties/forms/ConstantForm.vue
@@ -39,8 +39,7 @@ export default {
computed: {
// We can't rely on autoValue running in every form, so recalculate errors
clientErrors(){
- let validationContext = ConstantSchema.newContext();
- let cleanModel = validationContext.clean(this.model);
+ let cleanModel = ConstantSchema.clean(this.model);
return cleanModel.errors;
}
}