143 lines
4.3 KiB
JavaScript
143 lines
4.3 KiB
JavaScript
import SimpleSchema from 'simpl-schema';
|
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
|
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
|
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
|
import { doActionWork } from '/imports/api/engine/actions/doAction.js';
|
|
import computeCreature from '/imports/api/engine/computeCreature.js';
|
|
import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js';
|
|
|
|
const doAction = new ValidatedMethod({
|
|
name: 'creatureProperties.doCastSpell',
|
|
validate: new SimpleSchema({
|
|
spellId: SimpleSchema.RegEx.Id,
|
|
slotId: {
|
|
type: String,
|
|
regEx: SimpleSchema.RegEx.Id,
|
|
optional: true,
|
|
},
|
|
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({spellId, slotId, targetIds = [], scope = {}}) {
|
|
let spell = CreatureProperties.findOne(spellId);
|
|
// Check permissions
|
|
let creature = getRootCreatureAncestor(spell);
|
|
|
|
assertEditPermission(creature, this.userId);
|
|
|
|
// Get all the targets and make sure we can edit them
|
|
let targets = [];
|
|
targetIds.forEach(targetId => {
|
|
let target = Creatures.findOne(targetId);
|
|
assertEditPermission(target, this.userId);
|
|
targets.push(target);
|
|
});
|
|
|
|
// Fetch all the action's ancestor creatureProperties
|
|
const ancestorIds = [];
|
|
spell.ancestors.forEach(ref => {
|
|
if (ref.collection === 'creatureProperties') {
|
|
ancestorIds.push(ref.id);
|
|
}
|
|
});
|
|
|
|
// Get cursor of ancestors
|
|
const ancestors = CreatureProperties.find({
|
|
_id: {$in: ancestorIds},
|
|
}, {
|
|
sort: {order: 1},
|
|
});
|
|
|
|
// Get cursor of the properties
|
|
const properties = CreatureProperties.find({
|
|
$or: [{_id: spell._id}, {'ancestors.id': spell._id}],
|
|
removed: {$ne: true},
|
|
}, {
|
|
sort: {order: 1},
|
|
});
|
|
|
|
// Spend the appropriate slot
|
|
let slotLevel = spell.level || 0;
|
|
let slot;
|
|
if (slotId && !spell.castWithoutSpellSlots){
|
|
slot = CreatureProperties.findOne(slotId);
|
|
if (!slot){
|
|
throw new Meteor.Error('No slot',
|
|
'Slot not found to cast spell');
|
|
}
|
|
if (!slot.value){
|
|
throw new Meteor.Error('No slot',
|
|
'Slot depleted');
|
|
}
|
|
if (slot.attributeType !== 'spellSlot'){
|
|
throw new Meteor.Error('Not a slot',
|
|
'The given property is not a valid spell slot');
|
|
}
|
|
if (!slot.spellSlotLevel?.value){
|
|
throw new Meteor.Error('No slot level',
|
|
'Slot does not have a spell slot level');
|
|
}
|
|
if (slot.spellSlotLevel.value < spell.level){
|
|
throw new Meteor.Error('Slot too small',
|
|
'Slot is not large enough to cast spell');
|
|
}
|
|
slotLevel = slot.spellSlotLevel.value;
|
|
damagePropertyWork({
|
|
property: slot,
|
|
operation: 'increment',
|
|
value: 1,
|
|
});
|
|
}
|
|
|
|
scope['slotLevel'] = slotLevel;
|
|
|
|
// Post the slot level spent to the log
|
|
const log = CreatureLogSchema.clean({
|
|
creatureId: creature._id,
|
|
creatureName: creature.name,
|
|
});
|
|
if (slot?.spellSlotLevel?.value){
|
|
log.content.push({
|
|
name: `Casting using a level ${slotLevel} spell slot`
|
|
});
|
|
} else if (slotLevel) {
|
|
log.content.push({
|
|
name: `Casting at level ${slotLevel}`
|
|
});
|
|
}
|
|
|
|
// Do the action
|
|
doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope, log});
|
|
|
|
// Recompute all involved creatures
|
|
computeCreature(creature._id);
|
|
targets.forEach(target => {
|
|
computeCreature(target._id);
|
|
});
|
|
},
|
|
});
|
|
|
|
export default doAction;
|