Added short and long rest buttons, closes #87
This commit is contained in:
@@ -7,6 +7,7 @@ import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js';
|
|||||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
||||||
|
|
||||||
import '/imports/api/creature/removeCreature.js';
|
import '/imports/api/creature/removeCreature.js';
|
||||||
|
import '/imports/api/creature/restCreature.js';
|
||||||
|
|
||||||
//set up the collection for creatures
|
//set up the collection for creatures
|
||||||
let Creatures = new Mongo.Collection('creatures');
|
let Creatures = new Mongo.Collection('creatures');
|
||||||
@@ -31,6 +32,13 @@ let CreatureSettingsSchema = new SimpleSchema({
|
|||||||
hideUnusedStats: {
|
hideUnusedStats: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
},
|
||||||
|
// How much each hitDice resets on a long rest
|
||||||
|
hitDiceResetMultiplier: {
|
||||||
|
type: Number,
|
||||||
|
optional: true,
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,15 @@ export default function getActiveProperties({
|
|||||||
filter = {},
|
filter = {},
|
||||||
options,
|
options,
|
||||||
includeUntoggled = false
|
includeUntoggled = false
|
||||||
|
}){
|
||||||
|
filter = getActivePropertyFilter({ancestorId, filter, includeUntoggled});
|
||||||
|
return CreatureProperties.find(filter, options).fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getActivePropertyFilter({
|
||||||
|
ancestorId,
|
||||||
|
filter = {},
|
||||||
|
includeUntoggled = false
|
||||||
}){
|
}){
|
||||||
if (!ancestorId){
|
if (!ancestorId){
|
||||||
throw 'Ancestor Id is required to get active properties'
|
throw 'Ancestor Id is required to get active properties'
|
||||||
@@ -14,9 +23,9 @@ export default function getActiveProperties({
|
|||||||
let disabledAncestorsFilter = {
|
let disabledAncestorsFilter = {
|
||||||
'ancestors.id': ancestorId,
|
'ancestors.id': ancestorId,
|
||||||
$or: [
|
$or: [
|
||||||
{disabled: true},
|
{disabled: true}, // Everything can be disabled
|
||||||
{equipped: false},
|
{equipped: false}, // Items can be equipped
|
||||||
{applied: false},
|
{applied: false}, // Buffs can be applied
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
if (!includeUntoggled){
|
if (!includeUntoggled){
|
||||||
@@ -48,5 +57,5 @@ export default function getActiveProperties({
|
|||||||
filter._id = {
|
filter._id = {
|
||||||
$nin: disabledAncestorIds,
|
$nin: disabledAncestorIds,
|
||||||
}
|
}
|
||||||
return CreatureProperties.find(filter, options).fetch();
|
return filter;
|
||||||
}
|
}
|
||||||
|
|||||||
103
app/imports/api/creature/restCreature.js
Normal file
103
app/imports/api/creature/restCreature.js
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
|
import Creatures from '/imports/api/creature/Creatures.js';
|
||||||
|
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
|
||||||
|
import getActiveProperties, { getActivePropertyFilter } from '/imports/api/creature/getActiveProperties.js';
|
||||||
|
import {assertEditPermission} from '/imports/api/creature/creaturePermissions.js';
|
||||||
|
|
||||||
|
const restCreature = new ValidatedMethod({
|
||||||
|
name: 'creature.methods.longRest',
|
||||||
|
validate: new SimpleSchema({
|
||||||
|
creatureId: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
},
|
||||||
|
restType: {
|
||||||
|
type: String,
|
||||||
|
allowedValues: ['shortRest', 'longRest'],
|
||||||
|
},
|
||||||
|
}).validator(),
|
||||||
|
run({creatureId, restType}) {
|
||||||
|
let creature = Creatures.findOne(creatureId, {
|
||||||
|
fields: {
|
||||||
|
owner: 1,
|
||||||
|
writers: 1,
|
||||||
|
settings: 1,
|
||||||
|
}
|
||||||
|
}) ;
|
||||||
|
// Need edit permissions
|
||||||
|
assertEditPermission(creature, this.userId);
|
||||||
|
|
||||||
|
// Long rests reset short rest properties as well
|
||||||
|
let resetFilter;
|
||||||
|
if (restType === 'shortRest'){
|
||||||
|
resetFilter = 'shortRest'
|
||||||
|
} else {
|
||||||
|
resetFilter = {$in: ['shortRest', 'longRest']}
|
||||||
|
}
|
||||||
|
// Only apply to active properties
|
||||||
|
let filter = getActivePropertyFilter({
|
||||||
|
filter: {reset: resetFilter},
|
||||||
|
ancestorId: creatureId,
|
||||||
|
includeUntoggled: true,
|
||||||
|
});
|
||||||
|
// update all attribute's damage
|
||||||
|
filter.type = 'attribute';
|
||||||
|
CreatureProperties.update(filter, {
|
||||||
|
$set: {damage: 0}
|
||||||
|
}, {
|
||||||
|
selector: {type: 'attribute'},
|
||||||
|
multi: true,
|
||||||
|
});
|
||||||
|
// Update all action-like properties' usesUsed
|
||||||
|
filter.type = {$in: [
|
||||||
|
'action',
|
||||||
|
'attack',
|
||||||
|
'spell'
|
||||||
|
]};
|
||||||
|
CreatureProperties.update(filter, {
|
||||||
|
$set: {usesUsed: 0}
|
||||||
|
}, {
|
||||||
|
selector: {type: 'action'},
|
||||||
|
multi: true,
|
||||||
|
});
|
||||||
|
// Reset half hit dice on a long rest, starting with the highest dice
|
||||||
|
if (restType === 'longRest'){
|
||||||
|
let hitDice = getActiveProperties({
|
||||||
|
ancestorId: creatureId,
|
||||||
|
filter: {type: 'attribute', attributeType: 'hitDice'},
|
||||||
|
options: {fields: {
|
||||||
|
hitDiceSize: 1,
|
||||||
|
damage: 1,
|
||||||
|
value: 1,
|
||||||
|
}},
|
||||||
|
});
|
||||||
|
// 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.value || 0), 0);
|
||||||
|
let resetMultiplier = 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, resultingDamage;
|
||||||
|
hitDice.forEach(hd => {
|
||||||
|
if (!recoverableHd) return;
|
||||||
|
amountToRecover = Math.min(recoverableHd, hd.damage);
|
||||||
|
recoverableHd -= amountToRecover;
|
||||||
|
resultingDamage = hd.damage - amountToRecover;
|
||||||
|
CreatureProperties.update(hd._id, {
|
||||||
|
$set: {damage: resultingDamage}
|
||||||
|
}, {
|
||||||
|
selector: {type: 'attribute'},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default restCreature;
|
||||||
@@ -87,6 +87,12 @@ let ComputedOnlyAttributeSchema = new SimpleSchema({
|
|||||||
type: SimpleSchema.oneOf(Number, String, Boolean),
|
type: SimpleSchema.oneOf(Number, String, Boolean),
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
},
|
||||||
|
// The computed value of the attribute minus the damage
|
||||||
|
currentValue: {
|
||||||
|
type: SimpleSchema.oneOf(Number, String, Boolean),
|
||||||
|
defaultValue: 0,
|
||||||
|
optional: true,
|
||||||
},
|
},
|
||||||
// The computed modifier, provided the attribute type is `ability`
|
// The computed modifier, provided the attribute type is `ability`
|
||||||
modifier: {
|
modifier: {
|
||||||
|
|||||||
@@ -47,9 +47,21 @@
|
|||||||
<v-switch
|
<v-switch
|
||||||
label="Hide redundant stats"
|
label="Hide redundant stats"
|
||||||
:input-value="model.settings.hideUnusedStats"
|
:input-value="model.settings.hideUnusedStats"
|
||||||
:error-messages="errors.hideUnusedStats"
|
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
@change="value => $emit('change', {path: ['settings','hideUnusedStats'], value: !!value || null})"
|
@change="value => $emit('change', {path: ['settings','hideUnusedStats'], value: !!value})"
|
||||||
|
/>
|
||||||
|
<text-field
|
||||||
|
label="Hit Dice reset multiplier"
|
||||||
|
hint="What fraction of your hit dice are reset every long rest"
|
||||||
|
placeholder="0.5"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.1"
|
||||||
|
:value="model.settings.hitDiceResetMultiplier"
|
||||||
|
:debounce-time="debounceTime"
|
||||||
|
:disabled="disabled"
|
||||||
|
@change="(value, ack) => $emit('change', {path: ['settings','hitDiceResetMultiplier'], value, ack})"
|
||||||
/>
|
/>
|
||||||
<!--
|
<!--
|
||||||
<v-switch
|
<v-switch
|
||||||
|
|||||||
51
app/imports/ui/creature/RestButton.vue
Normal file
51
app/imports/ui/creature/RestButton.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<v-btn
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="loading"
|
||||||
|
outline
|
||||||
|
style="width: 160px;"
|
||||||
|
@click="rest"
|
||||||
|
>
|
||||||
|
<v-icon left>
|
||||||
|
{{ type === 'shortRest' ? 'snooze' : 'bedtime' }}
|
||||||
|
</v-icon>
|
||||||
|
{{ type === 'shortRest' ? 'Short Rest' : 'Long Rest' }}
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import restCreature from '/imports/api/creature/restCreature.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props:{
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
creatureId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data(){return {
|
||||||
|
loading: false,
|
||||||
|
}},
|
||||||
|
methods: {
|
||||||
|
rest(){
|
||||||
|
this.loading = true;
|
||||||
|
restCreature.call({
|
||||||
|
creatureId: this.creatureId,
|
||||||
|
restType: this.type,
|
||||||
|
}, error => {
|
||||||
|
this.loading = false;
|
||||||
|
if (error){
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<div
|
<div
|
||||||
v-if="creature"
|
|
||||||
class="stats-tab ma-2"
|
class="stats-tab ma-2"
|
||||||
>
|
>
|
||||||
<div class="px-2 pt-2">
|
<div class="px-2 pt-2">
|
||||||
@@ -8,6 +7,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<column-layout>
|
<column-layout>
|
||||||
|
<div class="character-buttons">
|
||||||
|
<v-card>
|
||||||
|
<v-card-text class="layout column align-center">
|
||||||
|
<rest-button
|
||||||
|
:creature-id="creatureId"
|
||||||
|
type="shortRest"
|
||||||
|
/>
|
||||||
|
<rest-button
|
||||||
|
:creature-id="creatureId"
|
||||||
|
type="longRest"
|
||||||
|
/>
|
||||||
|
</v-card-text>
|
||||||
|
</v-card>
|
||||||
|
</div>
|
||||||
<div class="ability-scores">
|
<div class="ability-scores">
|
||||||
<v-card>
|
<v-card>
|
||||||
<v-list>
|
<v-list>
|
||||||
@@ -291,6 +304,7 @@
|
|||||||
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
|
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
|
||||||
import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue';
|
import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue';
|
||||||
import AttackListTile from '/imports/ui/properties/components/actions/AttackListTile.vue';
|
import AttackListTile from '/imports/ui/properties/components/actions/AttackListTile.vue';
|
||||||
|
import RestButton from '/imports/ui/creature/RestButton.vue';
|
||||||
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
|
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
|
||||||
|
|
||||||
const getProperties = function(creature, filter,){
|
const getProperties = function(creature, filter,){
|
||||||
@@ -321,6 +335,7 @@
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
RestButton,
|
||||||
AbilityListTile,
|
AbilityListTile,
|
||||||
AttributeCard,
|
AttributeCard,
|
||||||
ColumnLayout,
|
ColumnLayout,
|
||||||
|
|||||||
Reference in New Issue
Block a user