From 27665e0bdca4ff2ae5624a23b45c1f63794c5340 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sat, 26 Feb 2022 17:35:26 +0200 Subject: [PATCH] Finished roll check and roll attack buttons from stats page --- .../applyPropertyByType/applyAction.js | 20 +-- .../applyPropertyByType/applyDamage.js | 8 +- .../applyPropertyByType/applySavingThrow.js | 13 +- app/imports/api/engine/actions/doAction.js | 1 + app/imports/api/engine/actions/doCheck.js | 114 ++++++++++++++++++ app/imports/ui/components/RollPopup.vue | 2 +- app/imports/ui/components/VerticalHex.vue | 12 +- .../character/characterSheetTabs/StatsTab.vue | 3 + .../components/actions/ActionCard.vue | 5 +- .../components/attributes/AbilityListTile.vue | 89 ++++++++++---- .../components/attributes/AttributeCard.vue | 54 ++++++++- .../components/skills/SkillListTile.vue | 42 ++++++- .../ui/utility/numberToSignedString.js | 8 +- app/server/main.js | 1 + 14 files changed, 319 insertions(+), 53 deletions(-) create mode 100644 app/imports/api/engine/actions/doCheck.js diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyAction.js b/app/imports/api/engine/actions/applyPropertyByType/applyAction.js index bd437edb..d8daf1d1 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyAction.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyAction.js @@ -5,6 +5,7 @@ import applyProperty from '../applyProperty.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { adjustQuantityWork } from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; +import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; export default function applyAction(node, {creature, targets, scope, log}){ const prop = node.node; @@ -59,9 +60,9 @@ function applyAttackWithoutTarget({attack, scope, log}){ criticalMiss, } = rollAttack(attack, scope); let name = criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : 'To Hit'; - if (attack.advantage === 1 || scope['$attackAdvantage']){ + if (scope['$attackAdvantage'] === 1){ name += ' (Advantage)'; - } else if(attack.advantage === -1 || scope['$attackDisadvantage']){ + } else if(scope['$attackAdvantage'] === -1){ name += ' (Disadvantage)'; } log.content.push({ @@ -93,9 +94,9 @@ function applyAttackToTarget({attack, target, scope, log}){ let name = criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : result > armor ? 'Hit!' : 'Miss!'; - if (attack.advantage === 1 || scope['$attackAdvantage']){ + if (scope['$attackAdvantage'] === 1){ name += ' (Advantage)'; - } else if(attack.advantage === -1 || scope['$attackDisadvantage']){ + } else if(scope['$attackAdvantage'] === -1){ name += ' (Disadvantage)'; } @@ -121,28 +122,29 @@ function applyAttackToTarget({attack, target, scope, log}){ } function rollAttack(attack, scope){ + const rollModifierText = numberToSignedString(attack.value, true); let value, resultPrefix; if (attack.advantage === 1 || scope['$attackAdvantage']){ const [a, b] = rollDice(2, 20); if (a >= b) { value = a; - resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] + ${attack.value} = `; + resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; } else { value = b; - resultPrefix = `1d20 [ ~~${a}~~, ${b} ] + ${attack.value} = `; + resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; } } else if (attack.advantage === -1 || scope['$attackDisadvantage']){ const [a, b] = rollDice(2, 20); if (a <= b) { value = a; - resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] + ${attack.value} = `; + resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; } else { value = b; - resultPrefix = `1d20 [ ~~${a}~~, ${b} ] + ${attack.value} = `; + resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; } } else { value = rollDice(1, 20)[0]; - resultPrefix = `1d20 [${value}] + ${attack.value} = ` + resultPrefix = `1d20 [${value}] ${rollModifierText} = ` } scope['$attackRoll'] = {value}; const result = value + attack.value; diff --git a/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js b/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js index 2368b0cb..6c10a5ba 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applyDamage.js @@ -88,16 +88,16 @@ export default function applyDamage(node, { // Log the damage done if (target._id === creature._id){ // Target is same as self, log damage as such - logValue.push(damageDealt + suffix + ' to self'); + logValue.push(`**${damageDealt}** ${suffix} to self`); } else { - logValue.push('Dealt ' + damageDealt + suffix + ` ${target.name && ' to '}${target.name}`); + logValue.push(`Dealt **${damageDealt}** ${suffix} ${target.name && ' to '}${target.name}`); // Log the damage received on that creature's log as well insertCreatureLog.call({ log: { creatureId: target._id, content: [{ name, - value: 'Recieved ' + damageDealt + suffix, + value: `Recieved **${damageDealt}** ${suffix}`, }], } }); @@ -105,7 +105,7 @@ export default function applyDamage(node, { }); } else { // There are no targets, just log the result - logValue.push(damage + suffix); + logValue.push(`**${damage}** ${suffix}`); } log.content.push({ name: logName, diff --git a/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js b/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js index a9a7b179..b55525ed 100644 --- a/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js +++ b/app/imports/api/engine/actions/applyPropertyByType/applySavingThrow.js @@ -1,6 +1,7 @@ import rollDice from '/imports/parser/rollDice.js'; import recalculateCalculation from './shared/recalculateCalculation.js'; import applyProperty from '../applyProperty.js'; +import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; export default function applySavingThrow(node, {creature, targets, scope, log}){ const prop = node.node; @@ -46,29 +47,31 @@ export default function applySavingThrow(node, {creature, targets, scope, log}){ return applyChildren(); } + const rollModifierText = numberToSignedString(save.value, true); + let value, values, resultPrefix; if (save.advantage === 1){ const [a, b] = rollDice(2, 20); if (a >= b) { value = a; - resultPrefix = `Advantage: 1d20 [ ${a}, ~~${b}~~ ] + ${save.value} = `; + resultPrefix = `Advantage: 1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; } else { value = b; - resultPrefix = `Advantage: 1d20 [ ~~${a}~~, ${b} ] + ${save.value} = `; + resultPrefix = `Advantage: 1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; } } else if (save.advantage === -1){ const [a, b] = rollDice(2, 20); if (a <= b) { value = a; - resultPrefix = `Advantage: 1d20 [ ${a}, ~~${b}~~ ] + ${save.value} = `; + resultPrefix = `Disadvantage: 1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; } else { value = b; - resultPrefix = `Advantage: 1d20 [ ~~${a}~~, ${b} ] + ${save.value} = `; + resultPrefix = `Disadvantage: 1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; } } else { values = rollDice(1, 20); value = values[0]; - resultPrefix = `1d20 [ ${value} ] + ${save.value} = ` + resultPrefix = `1d20 [ ${value} ] ${rollModifierText} = ` } scope['$saveDiceRoll'] = {value}; const result = value + save.value || 0; diff --git a/app/imports/api/engine/actions/doAction.js b/app/imports/api/engine/actions/doAction.js index 35cf7d4f..83658f62 100644 --- a/app/imports/api/engine/actions/doAction.js +++ b/app/imports/api/engine/actions/doAction.js @@ -27,6 +27,7 @@ const doAction = new ValidatedMethod({ scope: { type: Object, blackbox: true, + optional: true, }, }).validator(), mixins: [RateLimiterMixin], diff --git a/app/imports/api/engine/actions/doCheck.js b/app/imports/api/engine/actions/doCheck.js new file mode 100644 index 00000000..ce66db02 --- /dev/null +++ b/app/imports/api/engine/actions/doCheck.js @@ -0,0 +1,114 @@ +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 CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js'; +import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; +import computeCreature from '/imports/api/engine/computeCreature.js'; +import rollDice from '/imports/parser/rollDice.js'; +import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; + +const doCheck = new ValidatedMethod({ + name: 'creatureProperties.doCheck', + validate: new SimpleSchema({ + propId: SimpleSchema.RegEx.Id, + scope: { + type: Object, + blackbox: true, + }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 10, + timeInterval: 5000, + }, + run({propId, scope}) { + const prop = CreatureProperties.findOne(propId); + const creature = getRootCreatureAncestor(prop); + + // Check permissions + assertEditPermission(creature, this.userId); + + // Do the check + doCheckWork({creature, prop, method: this, methodScope: scope}); + + // Recompute all involved creatures + computeCreature(creature._id); + }, +}); + +export default doCheck; + +export function doCheckWork({ + creature, prop, method, methodScope = {} +}){ + // Create the log + let log = CreatureLogSchema.clean({ + creatureId: creature._id, + creatureName: creature.name, + }); + + rollCheck({prop, log, methodScope}); + + // Insert the log + insertCreatureLogWork({log, creature, method}); +} + +function rollCheck({prop, log, methodScope}){ + // get the modifier for the roll + let rollModifier; + let logName = `${prop.name} check`; + if (prop.type === 'skill'){ + rollModifier = prop.value; + if (prop.skillType === 'save'){ + if (prop.name.match(/save/i)){ + logName = prop.name; + } else { + logName = prop.name ? `${prop.name} save` : 'Saving Throw'; + } + } + } else if (prop.type === 'attribute'){ + if (prop.attributeType === 'ability'){ + rollModifier = prop.modifier; + } else { + rollModifier = prop.value; + } + } else { + throw (`${prop.type} not supported for checks`); + } + + const rollModifierText = numberToSignedString(rollModifier, true); + + let value, values, resultPrefix; + if (methodScope['$checkAdvantage'] === 1){ + logName += ' (Advantage)'; + const [a, b] = rollDice(2, 20); + if (a >= b) { + value = a; + resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; + } else { + value = b; + resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; + } + } else if (methodScope['$checkAdvantage'] === -1){ + logName += ' (Disadvantage)'; + const [a, b] = rollDice(2, 20); + if (a <= b) { + value = a; + resultPrefix = `1d20 [ ${a}, ~~${b}~~ ] ${rollModifierText} = `; + } else { + value = b; + resultPrefix = `1d20 [ ~~${a}~~, ${b} ] ${rollModifierText} = `; + } + } else { + values = rollDice(1, 20); + value = values[0]; + resultPrefix = `1d20 [ ${value} ] ${rollModifierText} = ` + } + const result = (value + rollModifier) || 0; + log.content.push({ + name: logName, + value: `${resultPrefix} **${result}**`, + }); +} diff --git a/app/imports/ui/components/RollPopup.vue b/app/imports/ui/components/RollPopup.vue index 0afd1d85..0167bc1d 100644 --- a/app/imports/ui/components/RollPopup.vue +++ b/app/imports/ui/components/RollPopup.vue @@ -35,7 +35,7 @@ origin="center center" >
export default { + inject: { + theme: { + default: { + isDark: false, + }, + }, + }, props: { height: { type: Number, diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index 2c9265a6..8745e03a 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -399,6 +399,9 @@ required: true, }, }, + data(){return { + doCheckLoading: false, + }}, meteor: { creature(){ return Creatures.findOne(this.creatureId); diff --git a/app/imports/ui/properties/components/actions/ActionCard.vue b/app/imports/ui/properties/components/actions/ActionCard.vue index fadbfa20..655192a8 100644 --- a/app/imports/ui/properties/components/actions/ActionCard.vue +++ b/app/imports/ui/properties/components/actions/ActionCard.vue @@ -106,6 +106,7 @@ import ItemConsumedView from '/imports/ui/properties/components/actions/ItemCons import PropertyIcon from '/imports/ui/properties/shared/PropertyIcon.vue'; import RollPopup from '/imports/ui/components/RollPopup.vue'; import MarkdownText from '/imports/ui/components/MarkdownText.vue'; +import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js'; export default { components: { @@ -170,13 +171,13 @@ export default { doAction.call({ actionId: this.model._id, scope: { - $attackAdvantage: advantage === 1 || undefined, - $attackDisadvantage: advantage === -1 || undefined, + $attackAdvantage: advantage, } }, error => { this.doActionLoading = false; if (error){ console.error(error); + snackbar({text: error.reason}); } }); }, diff --git a/app/imports/ui/properties/components/attributes/AbilityListTile.vue b/app/imports/ui/properties/components/attributes/AbilityListTile.vue index 80921808..c4d748da 100644 --- a/app/imports/ui/properties/components/attributes/AbilityListTile.vue +++ b/app/imports/ui/properties/components/attributes/AbilityListTile.vue @@ -1,32 +1,46 @@