Refactored actions and let actions apply buffs to self
This commit is contained in:
5
app/imports/api/creature/actions/applyAction.js
Normal file
5
app/imports/api/creature/actions/applyAction.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import spendResources from '/imports/api/creature/actions/spendResources.js'
|
||||
|
||||
export default function applyAction({prop}){
|
||||
spendResources(prop);
|
||||
}
|
||||
61
app/imports/api/creature/actions/applyBuff.js
Normal file
61
app/imports/api/creature/actions/applyBuff.js
Normal file
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
setLineageOfDocs,
|
||||
renewDocIds
|
||||
} from '/imports/api/parenting/parenting.js';
|
||||
import {setDocToLastOrder} from '/imports/api/parenting/order.js';
|
||||
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
|
||||
|
||||
export default function applyBuff({
|
||||
prop,
|
||||
children,
|
||||
creature,
|
||||
targets = [],
|
||||
//actionContext,
|
||||
}){
|
||||
let buffTargets = prop.target === 'self' ? [creature] : targets;
|
||||
|
||||
//let scope = {
|
||||
// ...creature.variables,
|
||||
// ...actionContext,
|
||||
//};
|
||||
|
||||
// TODO
|
||||
// If the target is not self, walk through all decendants and replace
|
||||
// variables in calculations with their values from the creature scope
|
||||
// If the target is self, replace all the target.x references with just x
|
||||
|
||||
// Then copy the decendants of the buff to the targets
|
||||
prop.applied = true;
|
||||
let propList = [prop];
|
||||
function addChildrenToPropList(children){
|
||||
children.forEach(child => {
|
||||
propList.push(child.node);
|
||||
addChildrenToPropList(child.children);
|
||||
});
|
||||
}
|
||||
addChildrenToPropList(children);
|
||||
let oldParent = {
|
||||
id: prop.parent.id,
|
||||
collection: prop.parent.collection,
|
||||
};
|
||||
buffTargets.forEach(target => {
|
||||
copyNodeListToTarget(propList, target, oldParent);
|
||||
});
|
||||
}
|
||||
|
||||
function copyNodeListToTarget(propList, target, oldParent){
|
||||
let ancestry = [{collection: 'creatures', id: target._id}];
|
||||
setLineageOfDocs({
|
||||
docArray: propList,
|
||||
newAncestry: ancestry,
|
||||
oldParent,
|
||||
});
|
||||
renewDocIds({
|
||||
docArray: propList,
|
||||
});
|
||||
setDocToLastOrder({
|
||||
collection: CreatureProperties,
|
||||
doc: propList[0],
|
||||
});
|
||||
CreatureProperties.batchInsert(propList);
|
||||
}
|
||||
19
app/imports/api/creature/actions/applyDamage.js
Normal file
19
app/imports/api/creature/actions/applyDamage.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
||||
|
||||
export default function applyDamage({
|
||||
prop,
|
||||
creature,
|
||||
targets,
|
||||
actionContext
|
||||
}){
|
||||
let damageTargets = prop.target === 'self' ? [creature] : targets;
|
||||
let scope = {
|
||||
...creature.variables,
|
||||
...actionContext,
|
||||
};
|
||||
let {result, errors} = evaluateString(prop.amount, scope);
|
||||
if (Meteor.isClient) errors.forEach(e => console.error(e));
|
||||
if (Number.isFinite(result)) {
|
||||
damageTargets.forEach()
|
||||
}
|
||||
}
|
||||
62
app/imports/api/creature/actions/applyProperties.js
Normal file
62
app/imports/api/creature/actions/applyProperties.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import applyAction from '/imports/api/creature/actions/applyAction.js';
|
||||
//import applyDamage from '/imports/api/creature/actions/applyDamage.js';
|
||||
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
|
||||
){
|
||||
return false;
|
||||
}
|
||||
switch (prop.type){
|
||||
case 'action':
|
||||
case 'spell':
|
||||
case 'attack':
|
||||
applyAction(options);
|
||||
return true;
|
||||
case 'damage':
|
||||
// applyDamage(options);
|
||||
return true;
|
||||
case 'adjustment':
|
||||
// applyAdjustment(options);
|
||||
return true;
|
||||
case 'buff':
|
||||
applyBuff(options);
|
||||
return false;
|
||||
case 'roll':
|
||||
// applyRoll(options);
|
||||
return true;
|
||||
case 'savingThrow':
|
||||
// applySavingThrow(options);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export default function applyProperties({
|
||||
forest,
|
||||
creature,
|
||||
targets,
|
||||
actionContext
|
||||
}){
|
||||
forest.forEach(child => {
|
||||
let walkChildren = applyProperty({
|
||||
prop: child.node,
|
||||
children: child.children,
|
||||
creature,
|
||||
targets,
|
||||
actionContext
|
||||
});
|
||||
if (walkChildren){
|
||||
applyProperties({
|
||||
forest: child.children,
|
||||
creature,
|
||||
targets,
|
||||
actionContext
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,88 +1,61 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties, { getCreature, damagePropertyWork, adjustQuantityWork } from '/imports/api/creature/CreatureProperties.js';
|
||||
import CreatureProperties, { getCreature } from '/imports/api/creature/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js';
|
||||
import { nodesToTree } from '/imports/api/parenting/parenting.js';
|
||||
import applyProperties from '/imports/api/creature/actions/applyProperties.js';
|
||||
|
||||
const doAction = new ValidatedMethod({
|
||||
name: 'creatureProperties.doAction',
|
||||
validate: new SimpleSchema({
|
||||
actionId: SimpleSchema.RegEx.Id,
|
||||
targetId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 10,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({actionId}) {
|
||||
run({actionId, targetId}) {
|
||||
let action = CreatureProperties.findOne(actionId);
|
||||
// Check permissions
|
||||
let creature = getCreature(action);
|
||||
assertEditPermission(creature, this.userId);
|
||||
doActionWork(action);
|
||||
let target = undefined;
|
||||
if (targetId) {
|
||||
target = getCreature(targetId);
|
||||
assertEditPermission(target, this.userId);
|
||||
}
|
||||
doActionWork({action, creature, target});
|
||||
// Note this only recomputes the top-level creature, not the nearest one
|
||||
recomputeCreatureByDoc(creature);
|
||||
if (target){
|
||||
recomputeCreatureByDoc(target);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
function doActionWork(action){
|
||||
spendResources(action);
|
||||
}
|
||||
|
||||
function spendResources(action){
|
||||
// Check Uses
|
||||
if (action.usesUsed >= action.usesResult){
|
||||
throw new Meteor.Error('Insufficient Uses',
|
||||
'This action has no uses left');
|
||||
}
|
||||
// Resources
|
||||
if (action.insufficientResources){
|
||||
throw new Meteor.Error('Insufficient Resources',
|
||||
'This creature doesn\'t have sufficient resources to perform this action');
|
||||
}
|
||||
// Items
|
||||
let itemQuantityAdjustments = [];
|
||||
action.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId){
|
||||
throw new Meteor.Error('Ammo not selected',
|
||||
'No ammo was selected for this action');
|
||||
}
|
||||
let item = CreatureProperties.findOne(itemConsumed.itemId);
|
||||
if (!item || item.ancestors[0].id !== action.ancestors[0].id){
|
||||
throw new Meteor.Error('Ammo not found',
|
||||
'The action\'s ammo was not found on the creature');
|
||||
}
|
||||
if (!item.equipped){
|
||||
throw new Meteor.Error('Ammo not equipped',
|
||||
'The selected ammo is not equipped');
|
||||
}
|
||||
if (!itemConsumed.quantity) return;
|
||||
itemQuantityAdjustments.push({
|
||||
property: item,
|
||||
operation: 'increment',
|
||||
value: itemConsumed.quantity,
|
||||
});
|
||||
function doActionWork({action, creature, target}){
|
||||
let actionContext = {};
|
||||
let decendantForest = nodesToTree({
|
||||
collection: CreatureProperties,
|
||||
ancestorId: action._id,
|
||||
});
|
||||
// No more errors should be thrown after this line
|
||||
// Now that we have confirmed that there are no errors, do actual work
|
||||
//Items
|
||||
itemQuantityAdjustments.forEach(adjustQuantityWork);
|
||||
// Use uses
|
||||
CreatureProperties.update(action._id, {
|
||||
$inc: {usesUsed: 1}
|
||||
}, {
|
||||
selector: action
|
||||
});
|
||||
// Damage stats
|
||||
action.resources.attributesConsumed.forEach(attConsumed => {
|
||||
if (!attConsumed.quantity) return;
|
||||
let stat = CreatureProperties.findOne(attConsumed.statId);
|
||||
damagePropertyWork({
|
||||
property: stat,
|
||||
operation: 'increment',
|
||||
value: attConsumed.quantity,
|
||||
});
|
||||
let startingForest = [{
|
||||
node: action,
|
||||
children: decendantForest,
|
||||
}];
|
||||
applyProperties({
|
||||
forest: startingForest,
|
||||
creature,
|
||||
target,
|
||||
actionContext
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
57
app/imports/api/creature/actions/spendResources.js
Normal file
57
app/imports/api/creature/actions/spendResources.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import CreatureProperties, { damagePropertyWork, adjustQuantityWork } from '/imports/api/creature/CreatureProperties.js';
|
||||
|
||||
export default function spendResources(action){
|
||||
// Check Uses
|
||||
if (action.usesUsed >= action.usesResult){
|
||||
throw new Meteor.Error('Insufficient Uses',
|
||||
'This action has no uses left');
|
||||
}
|
||||
// Resources
|
||||
if (action.insufficientResources){
|
||||
throw new Meteor.Error('Insufficient Resources',
|
||||
'This creature doesn\'t have sufficient resources to perform this action');
|
||||
}
|
||||
// Items
|
||||
let itemQuantityAdjustments = [];
|
||||
action.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId){
|
||||
throw new Meteor.Error('Ammo not selected',
|
||||
'No ammo was selected for this action');
|
||||
}
|
||||
let item = CreatureProperties.findOne(itemConsumed.itemId);
|
||||
if (!item || item.ancestors[0].id !== action.ancestors[0].id){
|
||||
throw new Meteor.Error('Ammo not found',
|
||||
'The action\'s ammo was not found on the creature');
|
||||
}
|
||||
if (!item.equipped){
|
||||
throw new Meteor.Error('Ammo not equipped',
|
||||
'The selected ammo is not equipped');
|
||||
}
|
||||
if (!itemConsumed.quantity) return;
|
||||
itemQuantityAdjustments.push({
|
||||
property: item,
|
||||
operation: 'increment',
|
||||
value: itemConsumed.quantity,
|
||||
});
|
||||
});
|
||||
// No more errors should be thrown after this line
|
||||
// Now that we have confirmed that there are no errors, do actual work
|
||||
//Items
|
||||
itemQuantityAdjustments.forEach(adjustQuantityWork);
|
||||
// Use uses
|
||||
CreatureProperties.update(action._id, {
|
||||
$inc: {usesUsed: 1}
|
||||
}, {
|
||||
selector: action
|
||||
});
|
||||
// Damage stats
|
||||
action.resources.attributesConsumed.forEach(attConsumed => {
|
||||
if (!attConsumed.quantity) return;
|
||||
let stat = CreatureProperties.findOne(attConsumed.statId);
|
||||
damagePropertyWork({
|
||||
property: stat,
|
||||
operation: 'increment',
|
||||
value: attConsumed.quantity,
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user