From 525b528d9a348b1cadd889edcb8685f2cd924ac8 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 12 Nov 2020 12:57:48 +0200 Subject: [PATCH] Added attribute damage and self damage results to actions and log. --- .../api/creature/CreatureProperties.js | 93 +++++++++++++++++++ .../api/creature/actions/applyAdjustment.js | 56 +++++++++++ .../api/creature/actions/applyAttack.js | 2 +- .../api/creature/actions/applyDamage.js | 64 ++++++++++--- .../api/creature/actions/applyProperties.js | 3 +- .../api/creature/computation/combineStat.js | 6 +- .../computation/computeEndStepProperty.js | 39 +++----- app/imports/api/properties/Adjustments.js | 5 + .../components/attributes/AbilityListTile.vue | 8 +- .../ui/properties/forms/AdjustmentForm.vue | 15 +++ 10 files changed, 243 insertions(+), 48 deletions(-) create mode 100644 app/imports/api/creature/actions/applyAdjustment.js diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js index 4e5b90de..a2ae63c4 100644 --- a/app/imports/api/creature/CreatureProperties.js +++ b/app/imports/api/creature/CreatureProperties.js @@ -255,6 +255,7 @@ export function damagePropertyWork({property, operation, value}){ }, { selector: property }); + return currentValue - damage; } else if (operation === 'increment'){ let currentValue = property.value - (property.damage || 0); let currentDamage = property.damage; @@ -268,9 +269,47 @@ export function damagePropertyWork({property, operation, value}){ }, { selector: property }); + return increment; } } +const damagePropertiesByName = new ValidatedMethod({ + name: 'CreatureProperties.damagePropertiesByName', + validate: new SimpleSchema({ + creatureId: SimpleSchema.RegEx.Id, + variableName: { + type: String, + }, + operation: { + type: String, + allowedValues: ['set', 'increment'] + }, + value: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 20, + timeInterval: 5000, + }, + run({creatureId, variableName, operation, value}) { + // Check permissions + assertEditPermission(creatureId, this.userId); + CreatureProperties.find({ + 'ancestors.id': creatureId, + variableName, + removed: {$ne: false}, + inactive: {$ne: true}, + }).forEach(property => { + // Check if property can take damage + let schema = CreatureProperties.simpleSchema(property); + if (!schema.allowsKey('damage')) return; + // Damage the property + damagePropertyWork({property: property, operation, value}) + }); + recomputeCreature.call({charId: creatureId}); + } +}) + const damageProperty = new ValidatedMethod({ name: 'creatureProperties.damage', validate: new SimpleSchema({ @@ -303,6 +342,58 @@ const damageProperty = new ValidatedMethod({ }, }); +const dealDamage = new ValidatedMethod({ + name: 'creatureProperties.dealDamage', + validate: new SimpleSchema({ + creatureId: SimpleSchema.RegEx.Id, + damageType: { + type: String, + }, + amount: Number, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 20, + timeInterval: 5000, + }, + run({creatureId, damageType, amount}) { + let creature = Creatures.findOne(creatureId, { + fields: { + damageMultipliers: 1, + owner: 1, + readers: 1, + writers: 1, + }, + }); + // Check permissions + assertEditPermission(creatureId, this.userId); + let healthBars = CreatureProperties.find({ + 'ancestors.id': creatureId, + type: 'attribute', + attributeType:'healthBar', + removed: {$ne: true}, + inactive: {$ne: true}, + }, { + sort: {order: -1}, + }); + let multiplier = creature.damageMultipliers[damageType]; + if (multiplier === undefined) multiplier = 1; + let totalDamage = Math.floor(amount * multiplier); + let damageLeft = totalDamage; + healthBars.forEach(healthBar => { + if (damageLeft === 0) return; + let damageAdded = damagePropertyWork({ + property: healthBar, + operation: 'increment', + value: damageLeft, + }); + damageLeft -= damageAdded; + }); + recomputeCreature.call({charId: creatureId}); + return totalDamage; + }, +}); + export function adjustQuantityWork({property, operation, value}){ // Check if property has quantity let schema = CreatureProperties.simpleSchema(property); @@ -476,6 +567,8 @@ export { duplicateProperty, insertPropertyFromLibraryNode, updateProperty, + dealDamage, + damagePropertiesByName, damageProperty, adjustQuantity, selectAmmoItem, diff --git a/app/imports/api/creature/actions/applyAdjustment.js b/app/imports/api/creature/actions/applyAdjustment.js new file mode 100644 index 00000000..d63020bb --- /dev/null +++ b/app/imports/api/creature/actions/applyAdjustment.js @@ -0,0 +1,56 @@ +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.js'; + +export default function applyAdjustment({ + prop, + creature, + targets, + actionContext +}){ + let damageTargets = prop.target === 'self' ? [creature] : targets; + let scope = { + ...creature.variables, + ...actionContext, + }; + 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, + }}); + } + } catch (e){ + return insertCreatureLog.call({ log: { + text: e.toString(), + creatureId: creature._id, + }}); + } + if (damageTargets) { + damageTargets.forEach(target => { + if (prop.target === 'each'){ + result = evaluateString(prop.amount, scope, 'reduce'); + } + damagePropertiesByName.call({ + creatureId: target._id, + variableName: prop.stat, + operation: prop.operation || 'increment', + value: result + }); + insertCreatureLog.call({ + log: { + text: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''} ${-result}`, + creatureId: target._id, + } + }); + }); + } else { + insertCreatureLog.call({ + log: { + text: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''} ${-result}`, + creatureId: creature._id, + } + }); + } +} diff --git a/app/imports/api/creature/actions/applyAttack.js b/app/imports/api/creature/actions/applyAttack.js index 236b7c62..3da3d099 100644 --- a/app/imports/api/creature/actions/applyAttack.js +++ b/app/imports/api/creature/actions/applyAttack.js @@ -8,7 +8,7 @@ export default function applyAttack({ //targets, //actionContext }){ - let result = roll(1, 20) + prop.rollBonusResult; + let result = roll(1, 20)[0] + prop.rollBonusResult; insertCreatureLog.call({ log: { text: `${prop.name} attack. ${result} to hit`, diff --git a/app/imports/api/creature/actions/applyDamage.js b/app/imports/api/creature/actions/applyDamage.js index 86a211b2..47a0648c 100644 --- a/app/imports/api/creature/actions/applyDamage.js +++ b/app/imports/api/creature/actions/applyDamage.js @@ -1,27 +1,63 @@ import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js'; - -//if (Meteor.isServer){ -// var sendWebhook = require('/imports/server/discord/webhook.js').default; -//} +import {insertCreatureLog} from '/imports/api/creature/log/CreatureLogs.js'; +import { dealDamage } from '/imports/api/creature/CreatureProperties.js'; export default function applyDamage({ prop, creature, - //targets, + targets, actionContext }){ - //let damageTargets = prop.target === 'self' ? [creature] : targets; + let damageTargets = prop.target === 'self' ? [creature] : targets; let scope = { ...creature.variables, ...actionContext, }; - let {result, errors} = evaluateString(prop.amount, scope, 'reduce'); - if (Meteor.isClient){ - errors.forEach(e => console.error(e)); - console.log(`${result} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`); + try { + var {result, errors} = evaluateString(prop.amount, scope, 'reduce'); + if (typeof result !== 'number') { + return insertCreatureLog.call({ log: { + text: errors.join(', '), + creatureId: creature._id, + }}); + } + } catch (e){ + return insertCreatureLog.call({ log: { + text: e.toString(), + creatureId: creature._id, + }}); + } + if (damageTargets) { + damageTargets.forEach(target => { + if (prop.target === 'each'){ + result = evaluateString(prop.amount, scope, 'reduce'); + } + let damageDealt = dealDamage.call({ + creatureId: target._id, + damageType: prop.damageType, + amount: result, + }); + insertCreatureLog.call({ + log: { + text: `Recieved ${damageDealt} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`, + creatureId: target._id, + } + }); + if (target._id !== creature._id){ + insertCreatureLog.call({ + log: { + text: `Dealt ${damageDealt} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`, + creatureId: creature._id, + } + }); + } + }); + } else { + insertCreatureLog.call({ + log: { + text: `${result} ${prop.damageType}${prop.damageType !== 'healing'? ' damage': ''}`, + creatureId: creature._id, + } + }); } - //if (Meteor.isServer) sendWebhook({ - // webhook: creature.webhook, - // message: `${result} ${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 c3c95296..fc66f053 100644 --- a/app/imports/api/creature/actions/applyProperties.js +++ b/app/imports/api/creature/actions/applyProperties.js @@ -1,4 +1,5 @@ import applyAction from '/imports/api/creature/actions/applyAction.js'; +import applyAdjustment from '/imports/api/creature/actions/applyAdjustment.js'; import applyAttack from '/imports/api/creature/actions/applyAttack.js'; import applyDamage from '/imports/api/creature/actions/applyDamage.js'; import applyBuff from '/imports/api/creature/actions/applyBuff.js'; @@ -26,7 +27,7 @@ function applyProperty(options){ applyDamage(options); return true; case 'adjustment': - // applyAdjustment(options); + applyAdjustment(options); return true; case 'buff': applyBuff(options); diff --git a/app/imports/api/creature/computation/combineStat.js b/app/imports/api/creature/computation/combineStat.js index cbea7567..efef3df9 100644 --- a/app/imports/api/creature/computation/combineStat.js +++ b/app/imports/api/creature/computation/combineStat.js @@ -33,15 +33,15 @@ function combineAttribute(stat, aggregator, memo){ stat.value = getAggregatorResult(stat, aggregator); stat.baseValue = aggregator.statBaseValue; stat.baseValueErrors = aggregator.baseValueErrors; - if (stat.attributeType === 'ability') { - stat.modifier = Math.floor((stat.value - 10) / 2); - } if (stat.attributeType === 'spellSlot'){ let {result, context} = evaluateCalculation(stat.spellSlotLevelCalculation, memo); stat.spellSlotLevelValue = result.value; stat.spellSlotLevelErrors = context.errors; } stat.currentValue = stat.value - (stat.damage || 0); + if (stat.attributeType === 'ability') { + stat.modifier = Math.floor((stat.currentValue - 10) / 2); + } stat.hide = aggregator.hasNoEffects && stat.baseValue === undefined || undefined diff --git a/app/imports/api/creature/computation/computeEndStepProperty.js b/app/imports/api/creature/computation/computeEndStepProperty.js index eceb8fd4..dbf7113d 100644 --- a/app/imports/api/creature/computation/computeEndStepProperty.js +++ b/app/imports/api/creature/computation/computeEndStepProperty.js @@ -67,43 +67,28 @@ function computeAction(prop, memo){ }); } -function computeAttack(prop, memo){ - // Roll bonus - let {result, context} = evaluateCalculation(prop.rollBonus, memo); - prop.rollBonusResult = result.value; +function computePropertyField(prop, memo, fieldName, fn){ + let {result, context} = evaluateCalculation(prop[fieldName], memo, fn); + prop[`${fieldName}Result`] = result.value; if (context.errors.length){ - prop.rollBonusErrors = context.errors; + prop[`${fieldName}Errors`] = context.errors; } else { - delete prop.rollBonusErrors; + delete prop[`${fieldName}Errors`]; } } +function computeAttack(prop, memo){ + computePropertyField(prop, memo, 'rollBonus'); +} + function computeSavingThrow(prop, memo){ - let {result, context} = evaluateCalculation(prop.dc, memo); - prop.dcResult = result.value; - if (context.errors.length){ - prop.dcErrors = context.errors; - } else { - delete prop.dcErrors; - } + computePropertyField(prop, memo, 'dc'); } function computeSpellList(prop, memo){ - let {result, context} = evaluateCalculation(prop.maxPrepared, memo); - prop.maxPreparedResult = result.value; - if (context.errors.length){ - prop.maxPreparedErrors = context.errors; - } else { - delete prop.maxPreparedErrors; - } + computePropertyField(prop, memo, 'maxPrepared'); } function computeSlot(prop, memo){ - let {result, context} = evaluateCalculation(prop.slotCondition, memo); - prop.slotConditionResult = result.value; - if (context.errors.length){ - prop.slotConditionErrors = context.errors; - } else { - delete prop.slotConditionErrors; - } + computePropertyField(prop, memo, 'slotCondition'); } diff --git a/app/imports/api/properties/Adjustments.js b/app/imports/api/properties/Adjustments.js index 400160c8..42a6ce1e 100644 --- a/app/imports/api/properties/Adjustments.js +++ b/app/imports/api/properties/Adjustments.js @@ -23,6 +23,11 @@ const AdjustmentSchema = new SimpleSchema({ type: String, optional: true, }, + operation: { + type: String, + allowedValues: ['set', 'increment'], + defaultValue: 'increment', + }, }); export { AdjustmentSchema }; diff --git a/app/imports/ui/properties/components/attributes/AbilityListTile.vue b/app/imports/ui/properties/components/attributes/AbilityListTile.vue index 5fedfbe5..411483de 100644 --- a/app/imports/ui/properties/components/attributes/AbilityListTile.vue +++ b/app/imports/ui/properties/components/attributes/AbilityListTile.vue @@ -6,7 +6,9 @@
diff --git a/app/imports/ui/properties/forms/AdjustmentForm.vue b/app/imports/ui/properties/forms/AdjustmentForm.vue index 183a72c3..a5912097 100644 --- a/app/imports/ui/properties/forms/AdjustmentForm.vue +++ b/app/imports/ui/properties/forms/AdjustmentForm.vue @@ -19,6 +19,15 @@ @change="change('amount', ...arguments)" /> +