Added attribute damage and self damage results to actions and log.

This commit is contained in:
Stefan Zermatten
2020-11-12 12:57:48 +02:00
parent 3917f63d5e
commit 525b528d9a
10 changed files with 243 additions and 48 deletions

View File

@@ -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,

View File

@@ -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,
}
});
}
}

View File

@@ -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`,

View File

@@ -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': ''}`,
//});
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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');
}

View File

@@ -23,6 +23,11 @@ const AdjustmentSchema = new SimpleSchema({
type: String,
optional: true,
},
operation: {
type: String,
allowedValues: ['set', 'increment'],
defaultValue: 'increment',
},
});
export { AdjustmentSchema };

View File

@@ -6,7 +6,9 @@
<v-list-tile-action class="mr-4">
<div class="display-1 mod">
<template v-if="swapScoresAndMods">
{{ model.value }}
<span :class="{'primary--text': model.currentValue !== model.value}">
{{ model.currentValue }}
</span>
</template>
<template v-else>
{{ numberToSignedString(model.modifier) }}
@@ -17,7 +19,9 @@
{{ numberToSignedString(model.modifier) }}
</template>
<template v-else>
{{ model.value }}
<span :class="{'primary--text': model.currentValue !== model.value}">
{{ model.currentValue }}
</span>
</template>
</div>
</v-list-tile-action>

View File

@@ -19,6 +19,15 @@
@change="change('amount', ...arguments)"
/>
</div>
<smart-select
label="Operation"
class="mx-1"
style="flex-basis: 300px;"
:items="adjustmentOps"
:value="model.operation"
:error-messages="errors.operation"
@change="change('operation', ...arguments)"
/>
<smart-select
v-if="parentTarget !== 'self'"
label="Target"
@@ -52,6 +61,12 @@ export default {
default: undefined,
},
},
data(){return {
adjustmentOps: [
{text: 'Damage', value: 'increment'},
{text: 'Set', value: 'set'},
],
}},
computed: {
targetOptions(){
if (this.parentTarget === 'singleTarget') {