Files
DiceCloud/app/imports/api/engine/actions/doAction.js
Stefan Zermatten 566d6a4fef Triggers 🤫
2022-07-20 15:57:38 +02:00

139 lines
4.2 KiB
JavaScript

import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
import {
getCreature, getVariables, getProperyAncestors, getPropertyDecendants, getPropertiesOfType
} 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 { groupBy, remove } from 'lodash';
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}) {
let action = CreatureProperties.findOne(actionId);
// Check permissions
const creatureId = action.ancestors[0].id;
let creature = getCreature(action.ancestors[0].id);
assertEditPermission(creature, this.userId);
// Add the variables to the creature document
const variables = getVariables(creatureId);
delete variables._id;
delete variables._creatureId;
creature.variables = variables;
// Get all the targets and make sure we can edit them
let targets = [];
targetIds.forEach(targetId => {
let target = getCreature(targetId);
assertEditPermission(target, this.userId);
// add the variables to the target documents
const variables = getVariables(creatureId);
delete variables._id;
delete variables._creatureId;
target.variables = variables;
targets.push(target);
});
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({creature, targets, properties, ancestors, method: this, methodScope: scope});
// Recompute all involved creatures
Creatures.update({
_id: { $in: [creature._id, ...targetIds] }
}, {
$set: {dirty: true},
});
},
});
export default doAction;
export function doActionWork({
creature, targets, properties, ancestors, method, methodScope = {}, log
}){
// 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`);
}
// Get the triggers
const triggers = getPropertiesOfType(creature._id, 'trigger');
remove(triggers, trigger => trigger.event !== 'doActionProperty');
creature.triggers = groupBy(triggers, 'actionPropertyType');
for (let type in creature.triggers) {
creature.triggers[type] = groupBy(creature.triggers[type], 'timing')
}
// Create the log
if (!log) log = CreatureLogSchema.clean({
creatureId: creature._id,
creatureName: creature.name,
});
// Apply the top level property, it is responsible for applying its children
// recursively
const scope = {
...creature.variables,
...ancestorScope,
...methodScope
}
applyProperty(propertyForest[0], {
creature,
targets,
scope,
log,
});
// Insert the log
insertCreatureLogWork({log, creature, method});
}
// Assumes ancestors are in tree order already
function getAncestorScope(ancestors){
let scope = {};
ancestors.forEach(prop => {
scope[`#${prop.type}`] = prop;
});
return scope;
}