Replaced damageProperty with new action engine

This commit is contained in:
ThaumRystra
2024-05-04 10:37:25 +02:00
parent 982897897f
commit c5f6ce81bd
9 changed files with 93 additions and 213 deletions

View File

@@ -1,6 +1,5 @@
import '/imports/api/creature/creatureProperties/methods/adjustQuantity'; import '/imports/api/creature/creatureProperties/methods/adjustQuantity';
import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary'; import '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary';
import '/imports/api/creature/creatureProperties/methods/damageProperty';
import '/imports/api/creature/creatureProperties/methods/duplicateProperty'; import '/imports/api/creature/creatureProperties/methods/duplicateProperty';
import '/imports/api/creature/creatureProperties/methods/equipItem'; import '/imports/api/creature/creatureProperties/methods/equipItem';
import '/imports/api/creature/creatureProperties/methods/insertProperty'; import '/imports/api/creature/creatureProperties/methods/insertProperty';

View File

@@ -1,5 +1,4 @@
import '/imports/api/creature/creatures/methods/insertCreature'; import '/imports/api/creature/creatures/methods/insertCreature';
import '/imports/api/creature/creatures/methods/removeCreature'; import '/imports/api/creature/creatures/methods/removeCreature';
import '/imports/api/creature/creatures/methods/restCreature';
import '/imports/api/creature/creatures/methods/updateCreature'; import '/imports/api/creature/creatures/methods/updateCreature';
import '/imports/api/creature/creatures/methods/changeAllowedLibraries'; import '/imports/api/creature/creatures/methods/changeAllowedLibraries';

View File

@@ -1,169 +0,0 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
import { union } from 'lodash';
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
import { getFilter } from '/imports/api/parenting/parentingFunctions';
const restCreature = new ValidatedMethod({
name: 'creature.methods.rest',
validate: new SimpleSchema({
creatureId: {
type: String,
regEx: SimpleSchema.RegEx.Id,
},
restType: {
type: String,
allowedValues: ['shortRest', 'longRest'],
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 5,
timeInterval: 5000,
},
run({ creatureId, restType }) {
// Get action context
const actionContext = new ActionContext(creatureId, [creatureId], this);
// Check permissions
assertEditPermission(actionContext.creature, this.userId);
// Join, sort, and apply before triggers
const beforeTriggers = union(
actionContext.triggers.anyRest?.before, actionContext.triggers[restType]?.before
).sort((a, b) => a.order - b.order);
applyTriggers(beforeTriggers, null, actionContext);
// Rest
actionContext.addLog({
name: restType === 'shortRest' ? 'Short rest' : 'Long rest',
});
doRestWork(restType, actionContext);
// Join, sort, and apply after triggers
const afterTriggers = union(
actionContext.triggers.anyRest?.after, actionContext.triggers[restType]?.after
).sort((a, b) => a.order - b.order);
applyTriggers(afterTriggers, null, actionContext);
// Insert log
actionContext.writeLog();
},
});
function doRestWork(restType, actionContext) {
const creatureId = actionContext.creature._id;
// Long rests reset short rest properties as well
let resetFilter;
if (restType === 'shortRest') {
resetFilter = 'shortRest'
} else {
resetFilter = { $in: ['shortRest', 'longRest'] }
}
resetProperties(creatureId, resetFilter, actionContext);
// Reset half hit dice on a long rest, starting with the highest dice
if (restType === 'longRest') {
resetHitDice(creatureId, actionContext);
}
}
export function resetProperties(creatureId, resetFilter, actionContext) {
// Only apply to active properties
const filter = {
...getFilter.descendantsOfRoot(creatureId),
reset: resetFilter,
removed: { $ne: true },
inactive: { $ne: true },
};
// update all attribute's damage
const attributeFilter = {
...filter,
type: 'attribute',
damage: { $nin: [0, undefined] },
}
CreatureProperties.find(attributeFilter).forEach(prop => {
damagePropertyWork({
prop,
operation: 'increment',
value: -prop.damage ?? 0,
actionContext,
logFunction(increment) {
actionContext.addLog({
name: prop.name,
value: increment < 0 ? `Restored ${-increment}` : `Removed ${-increment}`
});
}
});
});
// Update all action-like properties' usesUsed
const actionFilter = {
...filter,
type: {
$in: ['action', 'spell']
},
usesUsed: { $nin: [0, undefined] },
};
CreatureProperties.find(actionFilter, {
fields: { name: 1, usesUsed: 1 }
}).forEach(prop => {
actionContext.addLog({
name: prop.name,
value: prop.usesUsed >= 0 ? `Restored ${prop.usesUsed} uses` : `Removed ${-prop.usesUsed} uses`
});
});
CreatureProperties.update(actionFilter, {
$set: {
usesUsed: 0,
dirty: true,
}
}, {
selector: { type: 'action' },
multi: true,
});
}
function resetHitDice(creatureId, actionContext) {
let hitDice = CreatureProperties.find({
...getFilter.descendantsOfRoot(creatureId),
type: 'attribute',
attributeType: 'hitDice',
removed: { $ne: true },
inactive: { $ne: true },
}).fetch();
// Use a collator to do sorting in natural order
let collator = new Intl.Collator('en', {
numeric: true, sensitivity: 'base'
});
// Get the hit dice in decending order of hitDiceSize
let compare = (a, b) => collator.compare(b.hitDiceSize, a.hitDiceSize)
hitDice.sort(compare);
// Get the total number of hit dice that can be recovered this rest
let totalHd = hitDice.reduce((sum, hd) => sum + (hd.total || 0), 0);
let resetMultiplier = actionContext.creature.settings.hitDiceResetMultiplier || 0.5;
let recoverableHd = Math.max(Math.floor(totalHd * resetMultiplier), 1);
// recover each hit dice in turn until the recoverable amount is used up
let amountToRecover;
hitDice.forEach(hd => {
if (!recoverableHd) return;
amountToRecover = Math.min(recoverableHd, hd.damage ?? 0);
if (!amountToRecover) return;
recoverableHd -= amountToRecover;
damagePropertyWork({
prop: hd,
operation: 'increment',
value: -amountToRecover,
actionContext,
logFunction(increment) {
actionContext.addLog({
name: hd.name,
value: increment < 0 ? `Restored ${-increment} hit dice` : `Removed ${increment} hit dice`
});
}
});
});
}
export default restCreature;

View File

@@ -12,7 +12,7 @@
v-for="healthBar in properties.attribute.healthBar" v-for="healthBar in properties.attribute.healthBar"
:key="healthBar._id" :key="healthBar._id"
:model="healthBar" :model="healthBar"
@change="({ type, value }) => incrementChange(healthBar._id, { type, value: -value })" @change="({ type, value }) => incrementChange(healthBar._id, { type, value })"
@click="clickProperty({_id: healthBar._id})" @click="clickProperty({_id: healthBar._id})"
/> />
</v-card> </v-card>
@@ -614,6 +614,7 @@ export default {
}, },
incrementChange(_id, { type, value, ack }) { incrementChange(_id, { type, value, ack }) {
const model = CreatureProperties.findOne(_id); const model = CreatureProperties.findOne(_id);
if (type === 'increment') value = -value;
doAction(model, this.$store, model._id, { doAction(model, this.$store, model._id, {
subtaskFn: 'damageProp', subtaskFn: 'damageProp',
prop: model, prop: model,
@@ -621,7 +622,7 @@ export default {
params: { params: {
title: getPropertyTitle(model), title: getPropertyTitle(model),
operation: type, operation: type,
value: -value, value,
targetProp: model, targetProp: model,
} }
}).then(() =>{ }).then(() =>{

View File

@@ -72,7 +72,6 @@
<script lang="js"> <script lang="js">
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty';
import pushToProperty from '/imports/api/creature/creatureProperties/methods/pushToProperty'; import pushToProperty from '/imports/api/creature/creatureProperties/methods/pushToProperty';
import pullFromProperty from '/imports/api/creature/creatureProperties/methods/pullFromProperty'; import pullFromProperty from '/imports/api/creature/creatureProperties/methods/pullFromProperty';
import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty'; import softRemoveProperty from '/imports/api/creature/creatureProperties/methods/softRemoveProperty';
@@ -94,6 +93,7 @@ import Breadcrumbs from '/imports/client/ui/creature/creatureProperties/Breadcru
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode'; import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode';
import PropertyViewer from '/imports/client/ui/properties/shared/PropertyViewer.vue'; import PropertyViewer from '/imports/client/ui/properties/shared/PropertyViewer.vue';
import copyPropertyToLibrary from '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary'; import copyPropertyToLibrary from '/imports/api/creature/creatureProperties/methods/copyPropertyToLibrary';
import doAction from '/imports/client/ui/creature/actions/doAction';
export default { export default {
components: { components: {
@@ -177,7 +177,27 @@ export default {
updateCreatureProperty.call({_id: this.currentId, path, value}, ack); updateCreatureProperty.call({_id: this.currentId, path, value}, ack);
}, },
damage({operation, value, ack}){ damage({operation, value, ack}){
damageProperty.call({_id: this.currentId, operation, value}, ack); const model = this.model;
doAction(model, this.$store, model._id, {
subtaskFn: 'damageProp',
prop: model,
targetIds: [model.root.id],
params: {
title: getPropertyTitle(model),
operation: operation,
value,
targetProp: model,
}
}).then(() =>{
ack?.();
}).catch((error) => {
if (ack) {
ack(error);
} else {
snackbar({ text: error.reason || error.message || error.toString() });
console.error(error);
}
});
}, },
push({path, value, ack}){ push({path, value, ack}){
pushToProperty.call({_id: this.currentId, path, value}, ack); pushToProperty.call({_id: this.currentId, path, value}, ack);

View File

@@ -151,8 +151,9 @@ export default {
cancelEdit() { cancelEdit() {
this.editing = false; this.editing = false;
}, },
changeIncrementMenu(e) { changeIncrementMenu({ type, value }) {
this.$emit('change', e); if (type === 'increment') value = -value;
this.$emit('change', { type, value });
this.editing = false; this.editing = false;
} }
}, },

View File

@@ -64,9 +64,10 @@
</template> </template>
<script lang="js"> <script lang="js">
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty';
import numberToSignedString from '/imports/api/utility/numberToSignedString'; import numberToSignedString from '/imports/api/utility/numberToSignedString';
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue'; import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
import doAction from '/imports/client/ui/creature/actions/doAction';
import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle';
export default { export default {
inject: { inject: {
@@ -97,14 +98,24 @@ export default {
// return true // return true
return false; return false;
}, },
damageProperty({type, value, ack}) { damageProperty({ type, value, ack }) {
damageProperty.call({ const model = this.model;
_id: this.model._id, doAction(model, this.$store, model._id, {
operation: type, subtaskFn: 'damageProp',
value: value prop: model,
}, error => { targetIds: [model.root.id],
if (ack) ack(error); params: {
if (error) { title: getPropertyTitle(model),
operation: type,
value,
targetProp: model,
}
}).then(() =>{
ack?.();
}).catch((error) => {
if (ack) {
ack(error);
} else {
snackbar({ text: error.reason || error.message || error.toString() }); snackbar({ text: error.reason || error.message || error.toString() });
console.error(error); console.error(error);
} }

View File

@@ -13,7 +13,7 @@
v-else-if="model.attributeType === 'hitDice'" v-else-if="model.attributeType === 'hitDice'"
:model="model" :model="model"
@click="$emit('click')" @click="$emit('click')"
@change="({ type, value }) => damageProperty({type, value: -value})" @change="damageProperty"
/> />
<health-bar <health-bar
v-else-if="model.attributeType === 'healthBar'" v-else-if="model.attributeType === 'healthBar'"
@@ -30,7 +30,7 @@
v-else-if="model.attributeType === 'resource'" v-else-if="model.attributeType === 'resource'"
:model="model" :model="model"
@click="$emit('click')" @click="$emit('click')"
@change="({ type, value, ack }) => damageProperty({type, value: -value, ack})" @change="damageProperty"
@mouseover="hover = true" @mouseover="hover = true"
@mouseleave="hover = false" @mouseleave="hover = false"
/> />
@@ -62,8 +62,9 @@ import ResourceCardContent from '/imports/client/ui/properties/components/attrib
import AttributeCardContent from '/imports/client/ui/properties/components/attributes/AttributeCardContent.vue'; import AttributeCardContent from '/imports/client/ui/properties/components/attributes/AttributeCardContent.vue';
import CardHighlight from '/imports/client/ui/components/CardHighlight.vue'; import CardHighlight from '/imports/client/ui/components/CardHighlight.vue';
import FolderGroupChildren from '/imports/client/ui/properties/components/folders/folderGroupComponents/FolderGroupChildren.vue'; import FolderGroupChildren from '/imports/client/ui/properties/components/folders/folderGroupComponents/FolderGroupChildren.vue';
import doAction from '/imports/client/ui/creature/actions/doAction';
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty'; import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle';
export default { export default {
components: { components: {
@@ -91,20 +92,30 @@ export default {
hover: false, hover: false,
}}, }},
methods: { methods: {
damageProperty(change) { damageProperty({value, type, ack}) {
damageProperty.call({ const model = this.model;
_id: this.model._id, if (type === 'increment') value = -value;
operation: change.type, doAction(model, this.$store, model._id, {
value: change.value subtaskFn: 'damageProp',
}, e => { prop: model,
console.log(change); targetIds: [model.root.id],
change.ack?.(e); params: {
title: getPropertyTitle(model),
operation: type,
value,
targetProp: model,
}
}).then(() =>{
ack?.();
}).catch((error) => {
if (ack) {
ack(error);
} else {
snackbar({ text: error.reason || error.message || error.toString() });
console.error(error);
}
}); });
}, },
log({_id}) {
console.log(...arguments)
this.$emit('click-property', { _id });
}
} }
} }
</script> </script>

View File

@@ -141,12 +141,13 @@
import propertyViewerMixin from '/imports/client/ui/properties/viewers/shared/propertyViewerMixin' import propertyViewerMixin from '/imports/client/ui/properties/viewers/shared/propertyViewerMixin'
import numberToSignedString from '../../../../api/utility/numberToSignedString'; import numberToSignedString from '../../../../api/utility/numberToSignedString';
import AttributeEffect from '/imports/client/ui/properties/components/attributes/AttributeEffect.vue'; import AttributeEffect from '/imports/client/ui/properties/components/attributes/AttributeEffect.vue';
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty';
import IncrementButton from '/imports/client/ui/components/IncrementButton.vue'; import IncrementButton from '/imports/client/ui/components/IncrementButton.vue';
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
import getProficiencyIcon from '/imports/client/ui/utility/getProficiencyIcon'; import getProficiencyIcon from '/imports/client/ui/utility/getProficiencyIcon';
import {snackbar} from '/imports/client/ui/components/snackbars/SnackbarQueue'; import {snackbar} from '/imports/client/ui/components/snackbars/SnackbarQueue';
import sortEffects from '/imports/client/ui/utility/sortEffects'; import sortEffects from '/imports/client/ui/utility/sortEffects';
import doAction from '/imports/client/ui/creature/actions/doAction';
import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle';
export default { export default {
components: { components: {
@@ -208,18 +209,24 @@
data: {_id: id}, data: {_id: id},
}); });
}, },
damageProperty({type, value}) { damageProperty({ type, value }) {
const model = this.model;
this.damagePropertyLoading = true; this.damagePropertyLoading = true;
damageProperty.call({ doAction(model, this.$store, model._id, {
_id: this.model._id, subtaskFn: 'damageProp',
operation: type, prop: model,
value: value targetIds: [model.root.id],
}, error => { params: {
this.damagePropertyLoading = false; title: getPropertyTitle(model),
if (error){ operation: type,
snackbar({text: error.reason}); value,
console.error(error); targetProp: model,
} }
}).catch((error) => {
snackbar({ text: error.reason || error.message || error.toString() });
console.error(error);
}).finally(() => {
this.damagePropertyLoading = false;
}); });
}, },
}, },