From 4a52c3af1963c01ec6befc7ec8eca4311a168c95 Mon Sep 17 00:00:00 2001 From: Thaum Rystra <9525416+ThaumRystra@users.noreply.github.com> Date: Wed, 8 May 2024 12:10:43 +0200 Subject: [PATCH] Fixed a lot of UI to match new parenting API --- .../methods/updateCreatureProperty.js | 2 + .../api/engine/action/ActionEngine.test.ts | 8 ++-- .../applyActionProperty.test.ts | 38 ++++++++++++++++++- .../applyProperties/applyActionProperty.ts | 14 +++---- .../functions/actionEngineTest.testFn.ts | 16 +++++--- .../api/engine/action/tasks/applyResetTask.ts | 11 ++---- .../tests/computeCalculations.testFn.js | 2 - .../api/files/userImages/methods/index.js | 4 +- .../userImages/methods/removeUserImage.ts | 2 +- .../client/ui/components/tree/TreeNode.vue | 3 +- .../components/PrintedAction.vue | 25 ++++++------ .../ui/creature/slots/SlotCardsToFill.vue | 11 +++--- app/imports/client/ui/files/UserImageCard.vue | 2 +- .../components/actions/ActionCard.vue | 24 ++++++------ .../actions/SelectItemToConsume.vue | 3 +- .../folderGroupComponents/SlotBuildTree.vue | 10 ++--- .../components/folders/tabFoldersMixin.js | 5 ++- .../spells/CastSpellWithSlotDialog.vue | 3 +- .../components/spells/SpellListCard.vue | 2 +- 19 files changed, 108 insertions(+), 77 deletions(-) diff --git a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js index 01ebb388..12c90905 100644 --- a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js @@ -15,6 +15,8 @@ const updateCreatureProperty = new ValidatedMethod({ case 'parent': case 'ancestors': case 'root': + case 'left': + case 'right': case 'parentId': case 'damage': throw new Meteor.Error('Permission denied', diff --git a/app/imports/api/engine/action/ActionEngine.test.ts b/app/imports/api/engine/action/ActionEngine.test.ts index 198691a7..b7737859 100644 --- a/app/imports/api/engine/action/ActionEngine.test.ts +++ b/app/imports/api/engine/action/ActionEngine.test.ts @@ -10,6 +10,7 @@ import EngineActions, { EngineAction } from '/imports/api/engine/action/EngineAc import applyAction from '/imports/api/engine/action/functions/applyAction'; import { LogContent, Removal, Update } from '/imports/api/engine/action/tasks/TaskResult'; import inputProvider from './functions/userInput/inputProviderForTests.testFn'; +import { removeAllCreaturesAndProps } from '/imports/api/engine/action/functions/actionEngineTest.testFn'; const creatureId = Random.id(); const targetId = Random.id(); @@ -19,11 +20,8 @@ describe('Interrupt action system', function () { this.timeout(8000); before(async function () { // Remove old data - await Promise.all([ - CreatureProperties.removeAsync({}), - Creatures.removeAsync({}), - CreatureVariables.removeAsync({}), - ]); + await removeAllCreaturesAndProps(); + // Add creatures await Promise.all([ Creatures.insertAsync({ diff --git a/app/imports/api/engine/action/applyProperties/applyActionProperty.test.ts b/app/imports/api/engine/action/applyProperties/applyActionProperty.test.ts index 5544423e..913feef3 100644 --- a/app/imports/api/engine/action/applyProperties/applyActionProperty.test.ts +++ b/app/imports/api/engine/action/applyProperties/applyActionProperty.test.ts @@ -16,7 +16,7 @@ const [ creatureId, targetCreatureId, targetCreature2Id, emptyActionId, selfActionId, attackActionId, usesActionId, attackMissId, attackNoTargetId, usesResourcesActionId, ammoId, resourceAttId, consumeAmmoId, consumeResourceId, noUsesActionId, insufficientResourcesActionId, - attributeResetByEventId, eventActionId, advantageAttackId, advantageEffectId + attributeResetByEventId, eventActionId, advantageAttackId, advantageEffectId, disadvantageAttackId, disadvantageEffectId, ] = randomIds; const actionTestCreature = { @@ -60,6 +60,20 @@ const actionTestCreature = { targetByTags: true, targetTags: ['hasAdvantage'], }, + // Attack that has Disadvantage + { + _id: disadvantageAttackId, + type: 'action', + attackRoll: { calculation: '0' }, + tags: ['hasDisadvantage'], + }, + { + _id: disadvantageEffectId, + type: 'effect', + operation: 'disadvantage', + targetByTags: true, + targetTags: ['hasDisadvantage'], + }, // Attack that has no target { _id: attackNoTargetId, @@ -333,6 +347,26 @@ describe('Apply Action Properties', function () { assert.deepEqual(allMutations(action), expectedMutations); }); + it('should make attack rolls that roll with disadvantage', async function () { + const prop = await CreatureProperties.findOneAsync(disadvantageAttackId); + assert.equal(prop.attackRoll.disadvantage, 1, 'The attack roll should have disadvantage'); + const action = await runActionById(disadvantageAttackId, [targetCreatureId]); + const expectedMutations: Mutation[] = [ + { + contents: [{ name: 'Action' }], + targetIds: [targetCreatureId], + }, { + contents: [{ + inline: true, + name: 'Hit! (Disadvantage)', + value: '1d20 [ 10, ~~11~~ ] + 0\n**10**', + }], + targetIds: [targetCreatureId], + } + ]; + assert.deepEqual(allMutations(action), expectedMutations); + }); + it('actions should consume resources', async function () { const action = await runActionById(usesResourcesActionId, []); const expectedMutations: Mutation[] = [ @@ -401,7 +435,7 @@ describe('Apply Action Properties', function () { contents: [ { inline: true, - name: 'Attribute damaged', + name: 'Attribute restored', value: '+13 Attribute Reset By testEvent Event', }, ], diff --git a/app/imports/api/engine/action/applyProperties/applyActionProperty.ts b/app/imports/api/engine/action/applyProperties/applyActionProperty.ts index 3f85a36b..e33030be 100644 --- a/app/imports/api/engine/action/applyProperties/applyActionProperty.ts +++ b/app/imports/api/engine/action/applyProperties/applyActionProperty.ts @@ -1,8 +1,7 @@ import { EngineAction } from '/imports/api/engine/action/EngineActions'; import { PropTask } from '../tasks/Task'; import TaskResult, { LogContent } from '../tasks/TaskResult'; -import { getPropertiesOfType, getVariables } from '/imports/api/engine/loadCreatures'; -import applyTask from '/imports/api/engine/action/tasks/applyTask'; +import { getVariables } from '/imports/api/engine/loadCreatures'; import getPropertyTitle from '/imports/api/utility/getPropertyTitle'; import recalculateInlineCalculations from '/imports/api/engine/action/functions/recalculateInlineCalculations'; import spendResources from '/imports/api/engine/action/functions/spendResources'; @@ -115,7 +114,7 @@ async function applyAttackToTarget( if (targetArmor !== undefined) { let name = criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : - result > targetArmor ? 'Hit!' : 'Miss!'; + result >= targetArmor ? 'Hit!' : 'Miss!'; if (advantage === 1) { name += ' (Advantage)'; } else if (advantage === -1) { @@ -170,18 +169,19 @@ async function applyAttackWithoutTarget(action, prop, attack, taskResult: TaskRe result, criticalHit, criticalMiss, + advantage, } = await rollAttack(attack, scope, taskResult.pushScope, userInput); let name = criticalHit ? 'Critical Hit!' : criticalMiss ? 'Critical Miss!' : 'To Hit'; - if (scope['~attackAdvantage']?.value === 1) { + if (advantage === 1) { name += ' (Advantage)'; - } else if (scope['~attackAdvantage']?.value === -1) { + } else if (advantage === -1) { name += ' (Disadvantage)'; } if (!criticalMiss) { - scope['~attackHit'] = { value: true } + taskResult.pushScope['~attackHit'] = { value: true } } if (!criticalHit) { - scope['~attackMiss'] = { value: true }; + taskResult.pushScope['~attackMiss'] = { value: true }; } taskResult.mutations.push({ contents: [{ diff --git a/app/imports/api/engine/action/functions/actionEngineTest.testFn.ts b/app/imports/api/engine/action/functions/actionEngineTest.testFn.ts index 485347c4..fa474d68 100644 --- a/app/imports/api/engine/action/functions/actionEngineTest.testFn.ts +++ b/app/imports/api/engine/action/functions/actionEngineTest.testFn.ts @@ -13,11 +13,17 @@ import inputProvider from './userInput/inputProviderForTests.testFn'; * Removes all creatures, properties, and creatureVariable documents from the database */ export async function removeAllCreaturesAndProps() { - return Promise.all([ - CreatureProperties.removeAsync({}), - Creatures.removeAsync({}), - CreatureVariables.removeAsync({}), - ]); + if (Meteor.isServer) { + return Promise.all([ + CreatureProperties.removeAsync({}), + Creatures.removeAsync({}), + CreatureVariables.removeAsync({}), + ]); + } else { + CreatureProperties.find({}).forEach(doc => CreatureProperties.remove(doc._id)); + Creatures.find({}).forEach(doc => Creatures.remove(doc._id)); + CreatureVariables.find({}).forEach((doc: any) => CreatureVariables.remove(doc._id)); + } } /** diff --git a/app/imports/api/engine/action/tasks/applyResetTask.ts b/app/imports/api/engine/action/tasks/applyResetTask.ts index 580a8e36..f3bdea79 100644 --- a/app/imports/api/engine/action/tasks/applyResetTask.ts +++ b/app/imports/api/engine/action/tasks/applyResetTask.ts @@ -17,20 +17,15 @@ export default async function applyResetTask( throw new Meteor.Error('wrong-number-of-targets', `Must reset the properties of a single creature at a time, ${task.targetIds.length} targets were provided`) } - // Print a title for the event - let name: string; + // Print a title for rest events switch (task.eventName) { case 'shortRest': - name = 'Short Rest'; + result.appendLog({ name: 'Short Rest' }, task.targetIds); break; case 'longRest': - name = 'Long Rest'; - break; - default: - name = task.eventName; + result.appendLog({ name: 'Long Rest' }, task.targetIds); break; } - result.appendLog({ name }, task.targetIds); // Reset the properties by this event name await resetProperties(task, action, result, userInput); diff --git a/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js b/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js index ceb006f1..ee749e4d 100644 --- a/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js +++ b/app/imports/api/engine/computation/computeComputation/tests/computeCalculations.testFn.js @@ -55,7 +55,6 @@ var testProperties = [ clean({ _id: 'attackAction', type: 'action', - ancestors: [{ id: 'charId' }], attackRoll: { calculation: '3' }, @@ -86,7 +85,6 @@ var testProperties = [ clean({ _id: 'attackAction2', type: 'action', - ancestors: [{ id: 'charId' }], attackRoll: { calculation: '3' }, diff --git a/app/imports/api/files/userImages/methods/index.js b/app/imports/api/files/userImages/methods/index.js index abb0d9b6..3458d456 100644 --- a/app/imports/api/files/userImages/methods/index.js +++ b/app/imports/api/files/userImages/methods/index.js @@ -1,3 +1 @@ -import '/imports/api/creature/archive/methods/archiveCreatureToFile'; -import '/imports/api/creature/archive/methods/restoreCreatureFromFile'; -import '/imports/api/creature/archive/methods/removeArchiveCreature'; +import './removeUserImage'; diff --git a/app/imports/api/files/userImages/methods/removeUserImage.ts b/app/imports/api/files/userImages/methods/removeUserImage.ts index a9463bc2..a7e3f0f8 100644 --- a/app/imports/api/files/userImages/methods/removeUserImage.ts +++ b/app/imports/api/files/userImages/methods/removeUserImage.ts @@ -5,7 +5,7 @@ import { incrementFileStorageUsed } from '/imports/api/users/methods/updateFileS import UserImages from '/imports/api/files/userImages/UserImages'; const removeArchiveCreature = new ValidatedMethod({ - name: 'ArchiveCreatureFiles.methods.removeArchiveCreature', + name: 'ArchiveCreatureFiles.methods.removeUserImage', validate: new SimpleSchema({ 'fileId': { type: String, diff --git a/app/imports/client/ui/components/tree/TreeNode.vue b/app/imports/client/ui/components/tree/TreeNode.vue index 940679be..e42082ca 100644 --- a/app/imports/client/ui/components/tree/TreeNode.vue +++ b/app/imports/client/ui/components/tree/TreeNode.vue @@ -149,8 +149,7 @@ export default { }, watch: { 'node._ancestorOfMatchedDocument'(value) { - this.expanded = !!value || - some(this.selectedNode?.ancestors, ref => ref.id === this.node._id); + this.expanded = !!value || isAncestor(this.node, this.selectedNode); }, 'selectedNode.parentId'() { this.expanded = isAncestor(this.node, this.selectedNode) || this.expanded; diff --git a/app/imports/client/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue b/app/imports/client/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue index 7f7bd0c1..e3165e5a 100644 --- a/app/imports/client/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue +++ b/app/imports/client/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue @@ -88,7 +88,7 @@ import numberToSignedString from '/imports/api/utility/numberToSignedString'; import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue'; import MarkdownText from '/imports/client/ui/components/MarkdownText.vue'; import TreeNodeList from '/imports/client/ui/components/tree/TreeNodeList.vue'; -import { docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions'; +import { getFilter, docsToForest as nodeArrayToTree } from '/imports/api/parenting/parentingFunctions'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import { some } from 'lodash'; @@ -169,32 +169,31 @@ export default { }, meteor: { children() { - throw 'TODO: rewrite with nested sets' - const indicesOfTerminatingProps = []; - const decendants = CreatureProperties.find({ - 'ancestors.id': this.model._id, + const rangesToExclude = []; + const descendants = CreatureProperties.find({ + ...getFilter.descendants(this.model), 'removed': { $ne: true }, }, { sort: {left: 1} }).map(prop => { - // Get all the props we don't want to show the decendants of and + // Get all the props we don't want to show the descendants of and // where they might appear in the ancestor list if (prop.type === 'buff' || prop.type === 'folder') { - indicesOfTerminatingProps.push({ - id: prop._id, - ancestorIndex: prop.ancestors.length, + rangesToExclude.push({ + left: prop.left, + right: prop.right, }); } return prop; }).filter(prop => { // Filter out folders entirely if (prop.type === 'folder') return false; - // Filter out decendants of terminating props - return !some(indicesOfTerminatingProps, buffIndex => { - return prop.ancestors[buffIndex.ancestorIndex]?.id === buffIndex.id; + // Filter out descendants of terminating props + return !some(rangesToExclude, range => { + return prop.left > range.left && prop.right < range.right; }); }); - return nodeArrayToTree(decendants); + return nodeArrayToTree(descendants); }, }, } diff --git a/app/imports/client/ui/creature/slots/SlotCardsToFill.vue b/app/imports/client/ui/creature/slots/SlotCardsToFill.vue index 4414dd29..71da9799 100644 --- a/app/imports/client/ui/creature/slots/SlotCardsToFill.vue +++ b/app/imports/client/ui/creature/slots/SlotCardsToFill.vue @@ -38,6 +38,7 @@ import SlotCard from '/imports/client/ui/creature/slots/SlotCard.vue'; import PointBuyCard from '/imports/client/ui/properties/components/pointBuy/PointBuyCard.vue'; import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty'; import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue'; +import { getFilter } from '/imports/api/parenting/parentingFunctions'; export default { components: { @@ -74,7 +75,7 @@ export default { meteor: { slots() { const folderIds = CreatureProperties.find({ - 'ancestors.id': this.context.creatureId, + ...getFilter.descendantsOfRoot(this.context.creatureId), type: 'folder', hideStatsGroup: true, removed: { $ne: true }, @@ -82,10 +83,8 @@ export default { }, { fields: { _id: 1 } }).map(folder => folder._id); return CreatureProperties.find({ - 'ancestors.id': { - $eq: this.context.creatureId, - $nin: folderIds, - }, + ...getFilter.descendantsOfRoot(this.context.creatureId), + 'parentId': { $nin: folderIds }, type: 'propertySlot', ignored: { $ne: true }, $and: [ @@ -108,8 +107,8 @@ export default { }, pointBuys(){ return CreatureProperties.find({ + ...getFilter.descendantsOfRoot(this.context.creatureId), type: 'pointBuy', - 'ancestors.id': this.context.creatureId, ignored: { $ne: true }, removed: {$ne: true}, inactive: {$ne: true}, diff --git a/app/imports/client/ui/files/UserImageCard.vue b/app/imports/client/ui/files/UserImageCard.vue index 5307451d..f0e0fdee 100644 --- a/app/imports/client/ui/files/UserImageCard.vue +++ b/app/imports/client/ui/files/UserImageCard.vue @@ -34,7 +34,7 @@