Triggers needed action context to function outside of the action engine proper, so now it's been abstracted into its own class
101 lines
3.1 KiB
JavaScript
101 lines
3.1 KiB
JavaScript
import SimpleSchema from 'simpl-schema';
|
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
|
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
|
import {
|
|
getProperyAncestors, getPropertyDecendants
|
|
} from '/imports/api/engine/loadCreatures.js';
|
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
|
import applyProperty from './applyProperty.js';
|
|
import ActionContext from '/imports/api/engine/actions/ActionContext.js';
|
|
|
|
const doAction = new ValidatedMethod({
|
|
name: 'creatureProperties.doAction',
|
|
validate: new SimpleSchema({
|
|
actionId: SimpleSchema.RegEx.Id,
|
|
targetIds: {
|
|
type: Array,
|
|
defaultValue: [],
|
|
maxCount: 20,
|
|
optional: true,
|
|
},
|
|
'targetIds.$': {
|
|
type: String,
|
|
regEx: SimpleSchema.RegEx.Id,
|
|
},
|
|
scope: {
|
|
type: Object,
|
|
blackbox: true,
|
|
optional: true,
|
|
},
|
|
}).validator(),
|
|
mixins: [RateLimiterMixin],
|
|
rateLimit: {
|
|
numRequests: 10,
|
|
timeInterval: 5000,
|
|
},
|
|
run({ actionId, targetIds = [], scope }) {
|
|
// Get action context
|
|
let action = CreatureProperties.findOne(actionId);
|
|
const creatureId = action.ancestors[0].id;
|
|
const actionContext = new ActionContext(creatureId, targetIds, this);
|
|
|
|
// Check permissions
|
|
assertEditPermission(actionContext.creature, this.userId);
|
|
actionContext.targets.forEach(target => {
|
|
assertEditPermission(target, this.userId);
|
|
});
|
|
|
|
const ancestors = getProperyAncestors(creatureId, action._id);
|
|
ancestors.sort((a, b) => a.order - b.order);
|
|
|
|
const properties = getPropertyDecendants(creatureId, action._id);
|
|
properties.push(action);
|
|
properties.sort((a, b) => a.order - b.order);
|
|
|
|
// Do the action
|
|
doActionWork({properties, ancestors, actionContext, methodScope: scope});
|
|
|
|
// Recompute all involved creatures
|
|
Creatures.update({
|
|
_id: { $in: [creatureId, ...targetIds] }
|
|
}, {
|
|
$set: {dirty: true},
|
|
});
|
|
},
|
|
});
|
|
|
|
export default doAction;
|
|
|
|
export function doActionWork({
|
|
properties, ancestors, actionContext, methodScope = {},
|
|
}){
|
|
// get the docs
|
|
const ancestorScope = getAncestorScope(ancestors);
|
|
const propertyForest = nodeArrayToTree(properties);
|
|
if (propertyForest.length !== 1){
|
|
throw new Meteor.Error(`The action has ${propertyForest.length} top level properties, expected 1`);
|
|
}
|
|
|
|
// Include the ancestry and method scope in the context scope
|
|
Object.assign(actionContext.scope, ancestorScope, methodScope);
|
|
|
|
// Apply the top level property, it is responsible for applying its children
|
|
// recursively
|
|
applyProperty(propertyForest[0], actionContext);
|
|
|
|
// Insert the log
|
|
actionContext.writeLog();
|
|
}
|
|
|
|
// Assumes ancestors are in tree order already
|
|
function getAncestorScope(ancestors){
|
|
let scope = {};
|
|
ancestors.forEach(prop => {
|
|
scope[`#${prop.type}`] = prop;
|
|
});
|
|
return scope;
|
|
}
|