Work on column-based print layout
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<div class="character-sheet-printed fill-height">
|
||||
<v-fade-transition mode="out-in">
|
||||
<div
|
||||
v-if="!$subReady.singleCharacter"
|
||||
key="character-loading"
|
||||
class="fill-height layout justify-center align-center"
|
||||
>
|
||||
<v-progress-circular
|
||||
indeterminate
|
||||
color="primary"
|
||||
size="64"
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="!creature">
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<h2 style="margin: 48px 28px 16px">
|
||||
Character not found
|
||||
</h2>
|
||||
<h3>
|
||||
Either this character does not exist, or you don't have permission
|
||||
to view it.
|
||||
</h3>
|
||||
</v-layout>
|
||||
</div>
|
||||
<v-theme-provider
|
||||
v-else
|
||||
light
|
||||
>
|
||||
<printed-stats :creature-id="creatureId" />
|
||||
<printed-features :creature-id="creatureId" />
|
||||
<printed-inventory :creature-id="creatureId" />
|
||||
<printed-spells
|
||||
v-if="!creature.settings.hideSpellsTab"
|
||||
:creature-id="creatureId"
|
||||
/>
|
||||
<printed-character :creature-id="creatureId" />
|
||||
</v-theme-provider>
|
||||
</v-fade-transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import PrintedStats from '/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue';
|
||||
import PrintedFeatures from '/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue';
|
||||
import PrintedInventory from '/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue';
|
||||
import PrintedSpells from '/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue';
|
||||
import PrintedCharacter from '/imports/ui/creature/character/printedCharacterSheet/PrintedCharacter.vue';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PrintedStats,
|
||||
PrintedFeatures,
|
||||
PrintedInventory,
|
||||
PrintedSpells,
|
||||
PrintedCharacter,
|
||||
},
|
||||
computed: {
|
||||
creatureId() {
|
||||
return this.$route.params.id
|
||||
}
|
||||
},
|
||||
reactiveProvide: {
|
||||
name: 'context',
|
||||
include: ['creatureId', 'editPermission'],
|
||||
},
|
||||
watch: {
|
||||
'creature.name'(value) {
|
||||
this.$store.commit('setPageTitle', value ? ('Print ' + value) : 'Print Character Sheet');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit('setPageTitle',
|
||||
(this.creature && this.creature.name) ?
|
||||
('Print ' + this.creature.name) :
|
||||
'Print Character Sheet'
|
||||
);
|
||||
this.nameObserver = Creatures.find({
|
||||
creatureId: this.creatureId,
|
||||
}, {
|
||||
fields: { name: 1 },
|
||||
}).observe({
|
||||
added: ({ name }) =>
|
||||
this.$store.commit('setPageTitle', name ? ('Print ' + name) : 'Print Character Sheet'),
|
||||
changed: ({ name }) =>
|
||||
this.$store.commit('setPageTitle', name ? ('Print ' + name) : 'Print Character Sheet'),
|
||||
});
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.nameObserver.stop();
|
||||
},
|
||||
meteor: {
|
||||
$subscribe: {
|
||||
'singleCharacter'() {
|
||||
return [this.creatureId];
|
||||
},
|
||||
},
|
||||
creature() {
|
||||
return Creatures.findOne(this.creatureId);
|
||||
},
|
||||
editPermission() {
|
||||
try {
|
||||
assertEditPermission(this.creature, Meteor.userId());
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.character-sheet-printed {
|
||||
background: white;
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div class="build-tab">
|
||||
<column-layout wide-columns>
|
||||
<div>
|
||||
<creature-summary :creature="creature" />
|
||||
</div>
|
||||
<div
|
||||
v-for="note in notes"
|
||||
:key="note._id"
|
||||
>
|
||||
<note-card
|
||||
:model="note"
|
||||
/>
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import NoteCard from '/imports/ui/properties/components/persona/NoteCard.vue';
|
||||
import CreatureSummary from '/imports/ui/creature/character/CreatureSummary.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
CreatureSummary,
|
||||
NoteCard,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
notes(){
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'note',
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
}, {
|
||||
sort: {order: 1},
|
||||
});
|
||||
},
|
||||
creature(){
|
||||
return Creatures.findOne(this.creatureId);
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
<template lang="html">
|
||||
<div class="features">
|
||||
<column-layout wide-columns>
|
||||
<div
|
||||
v-for="feature in features"
|
||||
:key="feature._id"
|
||||
>
|
||||
<feature-card
|
||||
:model="feature"
|
||||
:data-id="feature._id"
|
||||
@click="featureClicked(feature)"
|
||||
/>
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
FeatureCard,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
features() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'feature',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
featureClicked({ _id }) {
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `${_id}`,
|
||||
data: { _id },
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,233 @@
|
||||
<template lang="html">
|
||||
<div class="inventory">
|
||||
<column-layout wide-columns>
|
||||
<div>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-list-item>
|
||||
<v-list-item-avatar>
|
||||
<v-icon>$vuetify.icons.injustice</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
Weight Carried
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-list-item-title>
|
||||
{{ weightCarried }} lb
|
||||
</v-list-item-title>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
<v-list-item>
|
||||
<v-list-item-avatar>
|
||||
<v-icon>$vuetify.icons.cash</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
Net worth
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-list-item-title>
|
||||
<coin-value :value="variables && variables.valueTotal && variables.valueTotal.value|| 0" />
|
||||
</v-list-item-title>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
<v-list-item v-if="variables && variables.itemsAttuned && variables.itemsAttuned.value">
|
||||
<v-list-item-avatar>
|
||||
<v-icon>$vuetify.icons.spell</v-icon>
|
||||
</v-list-item-avatar>
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
Items attuned
|
||||
</v-list-item-title>
|
||||
</v-list-item-content>
|
||||
<v-list-item-action>
|
||||
<v-list-item-title>
|
||||
{{ variables.itemsAttuned.value }}
|
||||
</v-list-item-title>
|
||||
</v-list-item-action>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
<div>
|
||||
<toolbar-card transparent-toolbar>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Equipped
|
||||
</v-toolbar-title>
|
||||
<v-card-text class="px-0">
|
||||
<item-list
|
||||
equipment
|
||||
:items="equippedItems"
|
||||
:parent-ref="equipmentParentRef"
|
||||
/>
|
||||
</v-card-text>
|
||||
</toolbar-card>
|
||||
</div>
|
||||
<div>
|
||||
<toolbar-card transparent-toolbar>
|
||||
<v-toolbar-title slot="toolbar">
|
||||
Carried
|
||||
</v-toolbar-title>
|
||||
<v-card-text class="px-0">
|
||||
<item-list
|
||||
:items="carriedItems"
|
||||
:parent-ref="carriedParentRef"
|
||||
/>
|
||||
</v-card-text>
|
||||
</toolbar-card>
|
||||
</div>
|
||||
<div
|
||||
v-for="container in containersWithoutAncestorContainers"
|
||||
:key="container._id"
|
||||
>
|
||||
<container-card :model="container" />
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue';
|
||||
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
|
||||
import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue';
|
||||
import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js';
|
||||
import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js';
|
||||
import CoinValue from '/imports/ui/components/CoinValue.vue';
|
||||
import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js';
|
||||
import CreatureVariables from '../../../../api/creature/creatures/CreatureVariables';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
ContainerCard,
|
||||
ToolbarCard,
|
||||
ItemList,
|
||||
CoinValue,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
organize: false,
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
containers() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'container',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
});
|
||||
},
|
||||
creature() {
|
||||
return Creatures.findOne(this.creatureId, {
|
||||
fields: {
|
||||
color: 1,
|
||||
variables: 1,
|
||||
}
|
||||
});
|
||||
},
|
||||
variables() {
|
||||
return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {};
|
||||
},
|
||||
containersWithoutAncestorContainers() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.containerIds
|
||||
},
|
||||
type: 'container',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
});
|
||||
},
|
||||
carriedItems() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.containerIds
|
||||
},
|
||||
type: 'item',
|
||||
equipped: { $ne: true },
|
||||
removed: { $ne: true },
|
||||
deactivatedByAncestor: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
});
|
||||
},
|
||||
equippedItems() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
},
|
||||
type: 'item',
|
||||
equipped: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
});
|
||||
},
|
||||
equipmentParentRef() {
|
||||
return getParentRefByTag(
|
||||
this.creatureId, BUILT_IN_TAGS.equipment
|
||||
) || getParentRefByTag(
|
||||
this.creatureId, BUILT_IN_TAGS.inventory
|
||||
) || {
|
||||
id: this.creatureId,
|
||||
collection: 'creatures'
|
||||
};
|
||||
},
|
||||
carriedParentRef() {
|
||||
return getParentRefByTag(
|
||||
this.creatureId, BUILT_IN_TAGS.carried
|
||||
) || getParentRefByTag(
|
||||
this.creatureId, BUILT_IN_TAGS.inventory
|
||||
) || {
|
||||
id: this.creatureId,
|
||||
collection: 'creatures'
|
||||
};
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
containerIds() {
|
||||
return this.containers.map(container => container._id);
|
||||
},
|
||||
weightCarried() {
|
||||
return stripFloatingPointOddities(
|
||||
this.variables &&
|
||||
this.variables.weightCarried &&
|
||||
this.variables.weightCarried.value || 0
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clickProperty(_id) {
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `tree-node-${_id}`,
|
||||
data: { _id },
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template lang="html">
|
||||
<div class="spells">
|
||||
<column-layout wide-columns>
|
||||
<div v-if="spellsWithoutList.length">
|
||||
<v-card>
|
||||
<spell-list
|
||||
:spells="spellsWithoutList"
|
||||
:parent-ref="{id: creatureId, collection: 'creatures'}"
|
||||
/>
|
||||
</v-card>
|
||||
</div>
|
||||
<div
|
||||
v-for="spellList in spellListsWithoutAncestorSpellLists"
|
||||
:key="spellList._id"
|
||||
>
|
||||
<spellList-card
|
||||
:model="spellList"
|
||||
:organize="organize"
|
||||
/>
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import SpellListCard from '/imports/ui/properties/components/spells/SpellListCard.vue';
|
||||
import SpellList from '/imports/ui/properties/components/spells/SpellList.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
SpellList,
|
||||
SpellListCard,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
organize: false,
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
spellLists() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'spellList',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
},
|
||||
spellsWithoutList() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.spellListIds,
|
||||
},
|
||||
type: 'spell',
|
||||
removed: { $ne: true },
|
||||
deactivatedByAncestor: { $ne: true },
|
||||
deactivatedByToggle: { $ne: true },
|
||||
}, {
|
||||
sort: {
|
||||
level: 1,
|
||||
order: 1,
|
||||
}
|
||||
});
|
||||
},
|
||||
spellListsWithoutAncestorSpellLists() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.spellListIds,
|
||||
},
|
||||
type: 'spellList',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
spellListIds() {
|
||||
return this.spellLists.map(spellList => spellList._id);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clickProperty(_id) {
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `spell-list-tile-${_id}`,
|
||||
data: { _id },
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,572 @@
|
||||
<template lang="html">
|
||||
<div class="stats-tab ma-2">
|
||||
<health-bar-card-container :creature-id="creatureId" />
|
||||
|
||||
<column-layout>
|
||||
<div
|
||||
v-if="abilities.length"
|
||||
class="ability-scores"
|
||||
>
|
||||
<div class="layout flex column">
|
||||
<div
|
||||
v-for="ability in abilities"
|
||||
:key="ability._id"
|
||||
class="ability"
|
||||
>
|
||||
<div class="score">
|
||||
<div class="double-border top big-number">
|
||||
<template v-if="creature.settings.swapScoresAndMods">
|
||||
{{ ability.total }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ numberToSignedString(ability.modifier) }}
|
||||
</template>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<template v-if="creature.settings.swapScoresAndMods">
|
||||
{{ numberToSignedString(ability.modifier) }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ ability.total }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="double-border name label">
|
||||
{{ ability.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="toggle in toggles"
|
||||
:key="toggle._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="box double-border" />
|
||||
<div class="label double-border">
|
||||
{{ toggle.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="stat in stats"
|
||||
:key="stat._id"
|
||||
class="number-label"
|
||||
:class="stat.variableName == 'armor' && 'shield-number-label'"
|
||||
>
|
||||
<div
|
||||
:class="stat.variableName == 'armor' ? 'shield-border' : 'octogon-border'"
|
||||
class="number big-number"
|
||||
>
|
||||
{{ stat.value }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ stat.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="modifier in modifiers"
|
||||
:key="modifier._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octogon-border big-number">
|
||||
{{ numberToSignedString(modifier.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ modifier.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="check in checks"
|
||||
:key="check._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octogon-border big-number">
|
||||
{{ numberToSignedString(check.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ check.name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="hitDice.length"
|
||||
class="hit-dice m-2"
|
||||
>
|
||||
<div class="double-border">
|
||||
<div>
|
||||
<span class="label">
|
||||
Total:
|
||||
</span>
|
||||
<span
|
||||
v-for="hitDie in hitDice"
|
||||
:key="hitDie._id"
|
||||
style="margin-right: 4px;"
|
||||
>
|
||||
{{ hitDie.total }}{{ hitDie.hitDiceSize }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="height: 80px;" />
|
||||
<div
|
||||
style="text-align: center;"
|
||||
class="label"
|
||||
>
|
||||
Hit Dice
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="resource in resources"
|
||||
:key="resource._id"
|
||||
class="resource"
|
||||
>
|
||||
<div>{{ resource.total }}</div>
|
||||
<div>{{ resource.name }}</div>
|
||||
</div>
|
||||
|
||||
<damage-multiplier-card
|
||||
v-if="multipliers && multipliers.length"
|
||||
:multipliers="multipliers"
|
||||
/>
|
||||
|
||||
<div
|
||||
v-if="spellSlots && spellSlots.length"
|
||||
>
|
||||
<div>Spell Slots</div>
|
||||
<div
|
||||
v-for="spellSlot in spellSlots"
|
||||
:key="spellSlot._id"
|
||||
>
|
||||
<div>
|
||||
{{ spellSlot.name }}
|
||||
</div>
|
||||
<div
|
||||
v-if="spellSlot.total > 6"
|
||||
>
|
||||
{{ spellSlot.total }}
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
>
|
||||
<v-icon
|
||||
v-for="i in spellSlot.total"
|
||||
:key="i"
|
||||
>
|
||||
mdi-radiobox-blank
|
||||
</v-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="savingThrows.length"
|
||||
class="saving-throws"
|
||||
>
|
||||
<div>
|
||||
<v-list>
|
||||
<v-subheader>Saving Throws</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="save in savingThrows"
|
||||
:key="save._id"
|
||||
:model="save"
|
||||
:data-id="save._id"
|
||||
/>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="skills.length"
|
||||
class="skills"
|
||||
>
|
||||
<div>
|
||||
<v-list>
|
||||
<v-subheader>Skills</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="skill in skills"
|
||||
:key="skill._id"
|
||||
:model="skill"
|
||||
:data-id="skill._id"
|
||||
/>
|
||||
</v-list>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="action in actions"
|
||||
:key="action._id"
|
||||
class="action"
|
||||
>
|
||||
<action-card
|
||||
:model="action"
|
||||
:data-id="action._id"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="weapons && weapons.length"
|
||||
class="weapon-proficiencies"
|
||||
>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-subheader>
|
||||
Weapons
|
||||
</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="weapon in weapons"
|
||||
:key="weapon._id"
|
||||
hide-modifier
|
||||
:model="weapon"
|
||||
:data-id="weapon._id"
|
||||
/>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
<div
|
||||
v-if="armors && armors.length"
|
||||
class="armor-proficiencies"
|
||||
>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-subheader>
|
||||
Armor
|
||||
</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="armor in armors"
|
||||
:key="armor._id"
|
||||
hide-modifier
|
||||
:model="armor"
|
||||
:data-id="armor._id"
|
||||
/>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
<div
|
||||
v-if="tools && tools.length"
|
||||
class="tool-proficiencies"
|
||||
>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-subheader>
|
||||
Tools
|
||||
</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="tool in tools"
|
||||
:key="tool._id"
|
||||
hide-modifier
|
||||
:model="tool"
|
||||
:data-id="tool._id"
|
||||
/>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
<div
|
||||
v-if="languages && languages.length"
|
||||
class="language-proficiencies"
|
||||
>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-subheader>
|
||||
Languages
|
||||
</v-subheader>
|
||||
<skill-list-tile
|
||||
v-for="language in languages"
|
||||
:key="language._id"
|
||||
hide-modifier
|
||||
:model="language"
|
||||
:data-id="language._id"
|
||||
/>
|
||||
</v-list>
|
||||
</v-card>
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</column-layout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
|
||||
import DamageMultiplierCard from '/imports/ui/properties/components/damageMultipliers/DamageMultiplierCard.vue';
|
||||
import HealthBarCardContainer from '/imports/ui/properties/components/attributes/HealthBarCardContainer.vue';
|
||||
import SkillListTile from '/imports/ui/properties/components/skills/SkillListTile.vue';
|
||||
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||
|
||||
const getProperties = function (creature, filter, options = {
|
||||
sort: { order: 1 }
|
||||
}) {
|
||||
if (!creature) return;
|
||||
if (creature.settings.hideUnusedStats) {
|
||||
filter.hide = { $ne: true };
|
||||
}
|
||||
filter['ancestors.id'] = creature._id;
|
||||
filter.removed = { $ne: true };
|
||||
filter.inactive = { $ne: true };
|
||||
filter.overridden = { $ne: true };
|
||||
filter.$nor = [
|
||||
{ hideWhenTotalZero: true, total: 0 },
|
||||
{ hideWhenValueZero: true, value: 0 },
|
||||
];
|
||||
|
||||
return CreatureProperties.find(filter, options);
|
||||
};
|
||||
|
||||
const getAttributeOfType = function (creature, type) {
|
||||
return getProperties(creature, {
|
||||
type: 'attribute',
|
||||
attributeType: type,
|
||||
});
|
||||
};
|
||||
|
||||
const getSkillOfType = function (creature, type) {
|
||||
return getProperties(creature, {
|
||||
type: 'skill',
|
||||
skillType: type,
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ColumnLayout,
|
||||
DamageMultiplierCard,
|
||||
HealthBarCardContainer,
|
||||
SkillListTile,
|
||||
ActionCard,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
doCheckLoading: false,
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
creature() {
|
||||
return Creatures.findOne(this.creatureId, { fields: { settings: 1 } });
|
||||
},
|
||||
abilities() {
|
||||
return getAttributeOfType(this.creature, 'ability');
|
||||
},
|
||||
stats() {
|
||||
return getAttributeOfType(this.creature, 'stat');
|
||||
},
|
||||
toggles() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'toggle',
|
||||
removed: { $ne: true },
|
||||
deactivatedByAncestor: { $ne: true },
|
||||
showUI: true,
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
},
|
||||
modifiers() {
|
||||
return getAttributeOfType(this.creature, 'modifier');
|
||||
},
|
||||
resources() {
|
||||
return getAttributeOfType(this.creature, 'resource');
|
||||
},
|
||||
spellSlots() {
|
||||
return getAttributeOfType(this.creature, 'spellSlot');
|
||||
},
|
||||
hasSpells() {
|
||||
const cursor = getProperties(this.creature, {
|
||||
type: 'spell',
|
||||
})
|
||||
return cursor && cursor.count();
|
||||
},
|
||||
hitDice() {
|
||||
return getAttributeOfType(this.creature, 'hitDice');
|
||||
},
|
||||
checks() {
|
||||
return getSkillOfType(this.creature, 'check');
|
||||
},
|
||||
savingThrows() {
|
||||
return getSkillOfType(this.creature, 'save');
|
||||
},
|
||||
skills() {
|
||||
return getSkillOfType(this.creature, 'skill');
|
||||
},
|
||||
tools() {
|
||||
return getSkillOfType(this.creature, 'tool');
|
||||
},
|
||||
weapons() {
|
||||
return getSkillOfType(this.creature, 'weapon');
|
||||
},
|
||||
armors() {
|
||||
return getSkillOfType(this.creature, 'armor');
|
||||
},
|
||||
languages() {
|
||||
return getSkillOfType(this.creature, 'language');
|
||||
},
|
||||
actions() {
|
||||
return getProperties(this.creature, { type: 'action' });
|
||||
},
|
||||
appliedBuffs() {
|
||||
return getProperties(this.creature, { type: 'buff' });
|
||||
},
|
||||
multipliers() {
|
||||
return getProperties(this.creature, {
|
||||
type: 'damageMultiplier'
|
||||
}, {
|
||||
sort: { value: 1, order: 1 }
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
numberToSignedString,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.double-border {
|
||||
position: relative;
|
||||
padding: 11px 10px;
|
||||
}
|
||||
.double-border::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-image-source: url(/images/print/doubleLineImageBorder.png);
|
||||
border-image-slice: 110 126 fill;
|
||||
border-image-width: 16px;
|
||||
border-image-repeat: stretch;
|
||||
box-sizing: content-box;
|
||||
z-index: -1;
|
||||
}
|
||||
.octogon-border {
|
||||
position: relative;
|
||||
padding: 4px 20px;
|
||||
}
|
||||
.octogon-border::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-image: url(/images/print/octogonBorder.png) 124 118 fill;
|
||||
border-image-width: 22px;
|
||||
z-index: -1;
|
||||
}
|
||||
.shield-border {
|
||||
min-width: 64px !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
aspect-ratio: 0.87;
|
||||
padding: 12px;
|
||||
}
|
||||
.shield-border::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: url(/images/print/shieldBorder.png);
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
z-index: -1;
|
||||
}
|
||||
.shield-number-label {
|
||||
align-items: center !important;
|
||||
}
|
||||
.big-number {
|
||||
font-size: 20pt;
|
||||
}
|
||||
.ability {
|
||||
display: flex;
|
||||
align-items: start;
|
||||
margin: 4px 0;
|
||||
}
|
||||
.ability .score {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.ability .top {
|
||||
min-width: 64px;
|
||||
text-align: center;
|
||||
margin-bottom: -10px;
|
||||
padding: 14px;
|
||||
z-index: 1;
|
||||
}
|
||||
.ability .bottom {
|
||||
font-size: 10pt;
|
||||
position: relative;
|
||||
padding: 0 16px;
|
||||
z-index: 2;
|
||||
}
|
||||
.ability .bottom::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border: solid white;
|
||||
border-image-source: url(/images/print/upwardPointingBorder.png);
|
||||
border-image-slice: 0 85 fill;
|
||||
border-image-width: 0 16px;
|
||||
border-image-outset: 0px 0px;
|
||||
border-image-repeat: stretch;
|
||||
box-sizing: content-box;
|
||||
z-index: -1;
|
||||
}
|
||||
.ability .name {
|
||||
margin-top: 10px;
|
||||
margin-left: -16px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.number-label {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 10pt;
|
||||
font-variant: small-caps;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.number-label .label {
|
||||
margin-top: 4px;
|
||||
margin-left: -30px;
|
||||
padding-left: 34px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.number-label .number {
|
||||
min-width: 72px;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.number-label .box {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
margin-left: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -12,6 +12,7 @@ const LibraryCollectionToolbar = () => import('/imports/ui/library/LibraryCollec
|
||||
const CharacterSheetPage = () => import('/imports/ui/pages/CharacterSheetPage.vue');
|
||||
const CharacterSheetToolbar = () => import('/imports/ui/creature/character/CharacterSheetToolbar.vue');
|
||||
const CharacterSheetRightDrawer = () => import('/imports/ui/creature/character/CharacterSheetRightDrawer.vue');
|
||||
const CharacterSheetPrinted = () => import('/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue');
|
||||
const SignIn = () => import('/imports/ui/pages/SignIn.vue');
|
||||
const Register = () => import('/imports/ui/pages/Register.vue');
|
||||
const IconAdmin = () => import('/imports/ui/icons/IconAdmin.vue');
|
||||
@@ -177,6 +178,16 @@ RouterFactory.configure(router => {
|
||||
meta: {
|
||||
title: 'Character Sheet',
|
||||
},
|
||||
}, {
|
||||
name: 'printCharacterSheet',
|
||||
path: '/print-character/:id',
|
||||
alias: '/print-character/:id/:urlName',
|
||||
components: {
|
||||
default: CharacterSheetPrinted,
|
||||
},
|
||||
meta: {
|
||||
title: 'Print Character Sheet',
|
||||
},
|
||||
}, {
|
||||
path: '/tabletops',
|
||||
name: 'tabletops',
|
||||
|
||||
Reference in New Issue
Block a user