Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b088a2d433 | ||
|
|
8aa5ee81d5 | ||
|
|
ef26153bb2 | ||
|
|
77597e8056 | ||
|
|
ee1b876259 | ||
|
|
12fbca5c78 | ||
|
|
da6fb55ca0 | ||
|
|
8551e318c2 | ||
|
|
f175cffab8 | ||
|
|
2bca582af6 | ||
|
|
5815c7ca34 | ||
|
|
c237162475 | ||
|
|
e87772c2a3 | ||
|
|
704314a7eb | ||
|
|
7ffd0bf61d | ||
|
|
69b3ba781d | ||
|
|
bf8eb52a96 | ||
|
|
684d672028 | ||
|
|
fb98544ae1 | ||
|
|
ec8b9c209c | ||
|
|
bee90a7a80 | ||
|
|
5ad0de9eb7 | ||
|
|
0b377fcb71 | ||
|
|
1f26fbf00e | ||
|
|
bb1e9624ad | ||
|
|
bda446858e | ||
|
|
e19e91f7e0 | ||
|
|
bac9fc98dd |
@@ -83,13 +83,13 @@ export function resetProperties(creatureId, resetFilter, actionContext) {
|
||||
const attributeFilter = {
|
||||
...filter,
|
||||
type: 'attribute',
|
||||
damage: { $ne: 0 },
|
||||
damage: { $nin: [0, undefined] },
|
||||
}
|
||||
CreatureProperties.find(attributeFilter).forEach(prop => {
|
||||
damagePropertyWork({
|
||||
prop,
|
||||
operation: 'increment',
|
||||
value: -prop.damage,
|
||||
value: -prop.damage ?? 0,
|
||||
actionContext,
|
||||
logFunction(increment) {
|
||||
actionContext.addLog({
|
||||
@@ -105,7 +105,7 @@ export function resetProperties(creatureId, resetFilter, actionContext) {
|
||||
type: {
|
||||
$in: ['action', 'spell']
|
||||
},
|
||||
usesUsed: { $ne: 0 },
|
||||
usesUsed: { $nin: [0, undefined] },
|
||||
};
|
||||
CreatureProperties.find(actionFilter, {
|
||||
fields: { name: 1, usesUsed: 1 }
|
||||
|
||||
@@ -165,7 +165,7 @@ function rollAttack(attack, scope) {
|
||||
}
|
||||
scope['$attackDiceRoll'] = { value };
|
||||
const result = value + attack.value;
|
||||
scope['$attackRoll'] = { result };
|
||||
scope['$attackRoll'] = { value: result };
|
||||
const { criticalHit, criticalMiss } = applyCrits(value, scope);
|
||||
return { resultPrefix, result, value, criticalHit, criticalMiss };
|
||||
}
|
||||
|
||||
@@ -39,17 +39,6 @@ export default {
|
||||
|
||||
.column-layout>div,
|
||||
.column-layout>span>div {
|
||||
/*
|
||||
Table and width set because firefox does not support break-inside: avoid
|
||||
*/
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
transform: translateX(0);
|
||||
-webkit-transform: translateX(0);
|
||||
-webkit-column-break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
padding: 4px;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<v-icon
|
||||
class="handle"
|
||||
v-bind="$attrs"
|
||||
@click.stop="() => { }"
|
||||
@touchstart.native.stop="() => { }"
|
||||
@click.native="e => { }"
|
||||
@touchstart.native.stop="e => { }"
|
||||
@touchend.native="portalEvent"
|
||||
>
|
||||
mdi-drag
|
||||
|
||||
@@ -218,30 +218,30 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
changeShowTreeTab(value) {
|
||||
let currentTab = this.$store.getters.tabNameById(this.model._id);
|
||||
if (!value && currentTab === 'tree') {
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{ id: this.model._id, tab: 'build' }
|
||||
);
|
||||
}
|
||||
this.$emit('change', {
|
||||
path: ['settings', 'showTreeTab'],
|
||||
value: !!value
|
||||
});
|
||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||
if (!value && currentTab === 5) {
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{ id: this.model._id, tab: 4 }
|
||||
);
|
||||
}
|
||||
},
|
||||
changeHideSpellsTab(value) {
|
||||
let currentTab = this.$store.getters.tabNameById(this.model._id);
|
||||
if (!value && currentTab === 'spells') {
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{ id: this.model._id, tab: 'actions' }
|
||||
);
|
||||
}
|
||||
this.$emit('change', {
|
||||
path: ['settings', 'hideSpellsTab'],
|
||||
value: !value
|
||||
});
|
||||
let currentTab = this.$store.getters.tabById(this.model._id);
|
||||
if (!value && currentTab === 3) {
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{ id: this.model._id, tab: 4 }
|
||||
);
|
||||
}
|
||||
},
|
||||
allUserLibrariesChange(value, ack) {
|
||||
toggleAllUserLibraries.call({
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:class="{
|
||||
'empty': !hasChildren,
|
||||
}"
|
||||
:data-id="`build-tree-node-${node._id}`"
|
||||
:data-id="`tree-node-${node._id}`"
|
||||
>
|
||||
<div
|
||||
class="layout align-center justify-start tree-node-title"
|
||||
|
||||
@@ -195,7 +195,7 @@ export default {
|
||||
} else {
|
||||
this.$store.commit(
|
||||
'setTabForCharacterSheet',
|
||||
{id: creatureId, tab: 5}
|
||||
{id: creatureId, tab: 'build'}
|
||||
);
|
||||
this.$emit('pop', creatureId);
|
||||
defer(() => {
|
||||
|
||||
@@ -47,9 +47,6 @@
|
||||
<v-tab-item>
|
||||
<stats-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<features-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<actions-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
@@ -59,6 +56,9 @@
|
||||
<v-tab-item>
|
||||
<inventory-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<features-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
<v-tab-item>
|
||||
<character-tab :creature-id="creatureId" />
|
||||
</v-tab-item>
|
||||
@@ -96,22 +96,22 @@
|
||||
<span>Stats</span>
|
||||
<v-icon>mdi-chart-box</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span>Features</span>
|
||||
<v-icon>mdi-text</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span>Actions</span>
|
||||
<v-icon>mdi-lightning-bolt</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span v-if="!creature.settings.hideSpellsTab">Spells</span>
|
||||
<v-btn v-if="!creature.settings.hideSpellsTab">
|
||||
<span>Spells</span>
|
||||
<v-icon>mdi-fire</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span>Inventory</span>
|
||||
<v-icon>mdi-cube</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span>Features</span>
|
||||
<v-icon>mdi-text</v-icon>
|
||||
</v-btn>
|
||||
<v-btn>
|
||||
<span>Journal</span>
|
||||
<v-icon>mdi-book-open-variant</v-icon>
|
||||
|
||||
@@ -39,27 +39,45 @@
|
||||
<v-icon>mdi-dots-vertical</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list v-if="editPermission">
|
||||
<v-list-item @click="deleteCharacter">
|
||||
<v-list>
|
||||
<v-list-item :to="printUrl">
|
||||
<v-list-item-title>
|
||||
<v-icon>mdi-delete</v-icon> Delete
|
||||
<v-icon left>
|
||||
mdi-printer
|
||||
</v-icon> Print
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showCharacterForm">
|
||||
<template v-if="editPermission">
|
||||
<v-list-item @click="deleteCharacter">
|
||||
<v-list-item-title>
|
||||
<v-icon left>
|
||||
mdi-delete
|
||||
</v-icon> Delete
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showCharacterForm">
|
||||
<v-list-item-title>
|
||||
<v-icon left>
|
||||
mdi-pencil
|
||||
</v-icon> Edit details
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showShareDialog">
|
||||
<v-list-item-title>
|
||||
<v-icon left>
|
||||
mdi-share-variant
|
||||
</v-icon> Sharing
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</template>
|
||||
<v-list-item
|
||||
v-else
|
||||
@click="unshareWithMe"
|
||||
>
|
||||
<v-list-item-title>
|
||||
<v-icon>mdi-pencil</v-icon> Edit details
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
<v-list-item @click="showShareDialog">
|
||||
<v-list-item-title>
|
||||
<v-icon>mdi-share-variant</v-icon> Sharing
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
<v-list v-else>
|
||||
<v-list-item @click="unshareWithMe">
|
||||
<v-list-item-title>
|
||||
<v-icon>mdi-delete</v-icon> Unshare with me
|
||||
<v-icon left>
|
||||
mdi-delete
|
||||
</v-icon> Unshare with me
|
||||
</v-list-item-title>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -101,9 +119,6 @@
|
||||
<v-tab>
|
||||
Stats
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Features
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Actions
|
||||
</v-tab>
|
||||
@@ -113,6 +128,9 @@
|
||||
<v-tab>
|
||||
Inventory
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Features
|
||||
</v-tab>
|
||||
<v-tab>
|
||||
Journal
|
||||
</v-tab>
|
||||
@@ -144,6 +162,7 @@ import isDarkColor from '/imports/client/ui/utility/isDarkColor.js';
|
||||
import CharacterSheetFab from '/imports/client/ui/creature/character/CharacterSheetFab.vue';
|
||||
import getThemeColor from '/imports/client/ui/utility/getThemeColor.js';
|
||||
import SharedIcon from '/imports/client/ui/components/SharedIcon.vue';
|
||||
import getCreatureUrlName from '/imports/api/creature/creatures/getCreatureUrlName.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -167,6 +186,9 @@ export default {
|
||||
isDark() {
|
||||
return isDarkColor(this.toolbarColor);
|
||||
},
|
||||
printUrl() {
|
||||
return `/print-character/${this.creature._id}/${getCreatureUrlName(this.creature)}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
|
||||
@@ -58,8 +58,20 @@ export default {
|
||||
}},
|
||||
meteor: {
|
||||
actions() {
|
||||
return CreatureProperties.find({
|
||||
const folderIds = CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: folderIds,
|
||||
},
|
||||
type: 'action',
|
||||
actionType: { $ne: 'event' },
|
||||
removed: { $ne: true },
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
<build-tree-node-list
|
||||
:children="slotBuildTree"
|
||||
class="mx-2"
|
||||
@selected="_id => propertyClicked({_id, prefix: 'build-tree-node-'})"
|
||||
@selected="_id => propertyClicked({_id, prefix: 'tree-node-'})"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
@@ -56,8 +56,20 @@ export default {
|
||||
},
|
||||
meteor: {
|
||||
features() {
|
||||
return CreatureProperties.find({
|
||||
const folderIds = CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: folderIds,
|
||||
},
|
||||
type: 'feature',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
|
||||
@@ -141,9 +141,22 @@ export default {
|
||||
};
|
||||
},
|
||||
meteor: {
|
||||
containers() {
|
||||
folderIds() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
},
|
||||
containers() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.folderIds,
|
||||
},
|
||||
type: 'container',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
@@ -166,7 +179,7 @@ export default {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.containerIds
|
||||
$nin: [...this.containerIds, ...this.folderIds],
|
||||
},
|
||||
type: 'container',
|
||||
removed: { $ne: true },
|
||||
@@ -179,7 +192,7 @@ export default {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.containerIds
|
||||
$nin: [...this.containerIds, ...this.folderIds],
|
||||
},
|
||||
type: 'item',
|
||||
equipped: { $ne: true },
|
||||
|
||||
@@ -60,8 +60,20 @@ export default {
|
||||
},
|
||||
meteor: {
|
||||
notes(){
|
||||
return CreatureProperties.find({
|
||||
const folderIds = CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: folderIds,
|
||||
},
|
||||
type: 'note',
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
@remove="softRemove"
|
||||
/>
|
||||
<div
|
||||
v-if="spellSlots && spellSlots.length || hasSpells"
|
||||
v-if="hasSpellSlots || hasSpells"
|
||||
class="spell-slots"
|
||||
>
|
||||
<spell-slot-card
|
||||
@@ -77,9 +77,33 @@ export default {
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
spellSlots() {
|
||||
folderIds() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
type: 'folder',
|
||||
groupStats: true,
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
},
|
||||
hasSpellSlots() {
|
||||
return !!CreatureProperties.findOne({
|
||||
'ancestors.id': this.creatureId,
|
||||
inactive: { $ne: true },
|
||||
removed: { $ne: true },
|
||||
overridden: { $ne: true },
|
||||
level: { $ne: 0 },
|
||||
type: 'attribute',
|
||||
attributeType: 'spellSlot',
|
||||
});
|
||||
},
|
||||
spellSlots() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.folderIds,
|
||||
},
|
||||
inactive: { $ne: true },
|
||||
removed: { $ne: true },
|
||||
overridden: { $ne: true },
|
||||
@@ -89,11 +113,16 @@ export default {
|
||||
{ hideWhenTotalZero: true, total: 0 },
|
||||
{ hideWhenValueZero: true, value: 0 },
|
||||
],
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
},
|
||||
spellLists() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.folderIds,
|
||||
},
|
||||
type: 'spellList',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
@@ -113,7 +142,7 @@ export default {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.spellListIds,
|
||||
$nin: [...this.spellListIds, ...this.folderIds],
|
||||
},
|
||||
type: 'spell',
|
||||
removed: { $ne: true },
|
||||
@@ -130,7 +159,7 @@ export default {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.creatureId,
|
||||
$nin: this.spellListIds,
|
||||
$nin: [...this.spellListIds, ...this.folderIds],
|
||||
},
|
||||
type: 'spellList',
|
||||
removed: { $ne: true },
|
||||
|
||||
@@ -161,13 +161,13 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="properties.hitDice && properties.hitDice.length"
|
||||
v-if="properties.attribute.hitDice && properties.attribute.hitDice.length"
|
||||
class="hit-dice"
|
||||
>
|
||||
<v-card>
|
||||
<v-list>
|
||||
<v-subheader>Hit Dice</v-subheader>
|
||||
<template v-for="(hitDie, index) in hitDice">
|
||||
<template v-for="(hitDie, index) in properties.attribute.hitDice">
|
||||
<v-divider
|
||||
v-if="index !== 0"
|
||||
:key="hitDie._id + 'divider'"
|
||||
@@ -427,10 +427,10 @@ const propertyHandlers = {
|
||||
folder(prop) {
|
||||
let skipChildren;
|
||||
let propPath = null;
|
||||
if (prop.hideStatsGroup) {
|
||||
return { skipChildren: true}
|
||||
if (prop.groupStats && prop.hideStatsGroup) {
|
||||
skipChildren = true;
|
||||
}
|
||||
if (prop.tab === 'stats') {
|
||||
if (prop.groupStats && prop.tab === 'stats') {
|
||||
propPath = ['folder', prop.location]
|
||||
}
|
||||
return { skipChildren, propPath }
|
||||
@@ -546,7 +546,7 @@ export default {
|
||||
if (creature.settings.hideUnusedStats) {
|
||||
filter.hide = { $ne: true };
|
||||
}
|
||||
const allProps = CreatureProperties.find(filter, { sort: { order: 1 } });
|
||||
const allProps = CreatureProperties.find(filter, { sort: { order: -1 } });
|
||||
const forest = nodeArrayToTree(allProps);
|
||||
const properties = { folder: {}, attribute: {}, skill: {} };
|
||||
walkDown(forest, node => {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
{{ creature.alignment }} {{ background }}
|
||||
</div>
|
||||
<dir v-if="race || creature.gender">
|
||||
{{ race }} {{ creature.gender }}
|
||||
{{ creature.gender }} {{ race }}
|
||||
</dir>
|
||||
<div v-if="level && classes && classes.length === 1">
|
||||
Level {{ level }} {{ classes[0].name }}
|
||||
@@ -236,9 +236,41 @@ export default {
|
||||
color: black;
|
||||
font-size: 11pt;
|
||||
}
|
||||
|
||||
.character-sheet-printed * {
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
cursor: unset !important;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.character-sheet-printed .column-layout, .character-sheet-printed .column-layout.wide-columns {
|
||||
position:relative;
|
||||
width: 100%;
|
||||
widows: 0;
|
||||
orphans: 0;
|
||||
-webkit-column-fill: balance-all;
|
||||
column-fill: balance-all;
|
||||
}
|
||||
|
||||
.character-sheet-printed .column-layout>div {
|
||||
position:relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
backface-visibility: hidden;
|
||||
-webkit-backface-visibility: hidden;
|
||||
-webkit-column-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.character-sheet-printed .column-layout>div>* {
|
||||
-webkit-column-break-inside: avoid;
|
||||
break-inside: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.character-sheet-printed .inactive {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@@ -253,8 +285,6 @@ export default {
|
||||
background-image: url(/crown-dice-logo-cropped-transparent.png);
|
||||
background-size: contain;
|
||||
background-position: 0 center;
|
||||
print-color-adjust: exact;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
.character-sheet-printed .v-divider {
|
||||
@@ -265,7 +295,6 @@ export default {
|
||||
.character-sheet-printed .double-border {
|
||||
position: relative;
|
||||
padding: 11px 10px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.character-sheet-printed .double-border::before {
|
||||
@@ -286,8 +315,8 @@ export default {
|
||||
.character-sheet-printed .octagon-border {
|
||||
position: relative;
|
||||
padding: 4px 20px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
.character-sheet-printed .octagon-border::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
@@ -326,6 +355,9 @@ export default {
|
||||
}
|
||||
}
|
||||
@media print {
|
||||
.v-main, .v-application, .v-application--wrap, .character-sheet-printed {
|
||||
display: block;
|
||||
}
|
||||
header {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<v-app-bar
|
||||
app
|
||||
class="character-sheet-printed-toolbar"
|
||||
:color="toolbarColor"
|
||||
:dark="isDark"
|
||||
:light="!isDark"
|
||||
clipped-right
|
||||
:extended="$vuetify.breakpoint.smAndUp"
|
||||
:tabs="$vuetify.breakpoint.smAndUp"
|
||||
dense
|
||||
>
|
||||
<v-app-bar-nav-icon @click="toggleDrawer" />
|
||||
<v-btn
|
||||
icon
|
||||
:to="characterUrl"
|
||||
>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<v-toolbar-title>
|
||||
<v-fade-transition mode="out-in">
|
||||
<div :key="$store.state.pageTitle">
|
||||
{{ $store.state.pageTitle }}
|
||||
</div>
|
||||
</v-fade-transition>
|
||||
</v-toolbar-title>
|
||||
<v-spacer />
|
||||
<div
|
||||
slot="extension"
|
||||
style="width: 100%"
|
||||
>
|
||||
<v-btn
|
||||
class="print-fab"
|
||||
color="accent"
|
||||
elevation="4"
|
||||
fab
|
||||
@click="print"
|
||||
>
|
||||
<v-icon>mdi-printer</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
</v-app-bar>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
import { mapMutations } from 'vuex';
|
||||
import isDarkColor from '/imports/client/ui/utility/isDarkColor.js';
|
||||
import getThemeColor from '/imports/client/ui/utility/getThemeColor.js';
|
||||
import getCreatureUrlName from '/imports/api/creature/creatures/getCreatureUrlName.js';
|
||||
|
||||
export default {
|
||||
inject: {
|
||||
context: { default: {} }
|
||||
},
|
||||
computed: {
|
||||
creatureId() {
|
||||
return this.$route.params.id;
|
||||
},
|
||||
toolbarColor() {
|
||||
if (this.creature && this.creature.color) {
|
||||
return this.creature.color;
|
||||
} else {
|
||||
return getThemeColor('secondary');
|
||||
}
|
||||
},
|
||||
isDark() {
|
||||
return isDarkColor(this.toolbarColor);
|
||||
},
|
||||
characterUrl() {
|
||||
if (!this.creature) return;
|
||||
return `/character/${this.creature._id}/${getCreatureUrlName(this.creature)}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
'toggleDrawer',
|
||||
]),
|
||||
print() {
|
||||
print();
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
creature() {
|
||||
return Creatures.findOne(this.creatureId);
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.print-fab {
|
||||
position: absolute;
|
||||
bottom: -24px;
|
||||
right: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -62,7 +62,7 @@
|
||||
>
|
||||
<div
|
||||
:key="container._id"
|
||||
class="span-all container-header"
|
||||
class="span-all"
|
||||
>
|
||||
<printed-container
|
||||
class="octagon-border"
|
||||
@@ -232,22 +232,6 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.octagon-border {
|
||||
position: relative;
|
||||
padding: 4px 20px;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
.octagon-border::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-image: url(/images/print/octagonBorder.png) 124 118 fill;
|
||||
border-image-width: 22px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 14pt;
|
||||
@@ -262,9 +246,4 @@ export default {
|
||||
.inventory-stat > .v-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.container-header {
|
||||
page-break-after: avoid;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,97 +2,104 @@
|
||||
<div class="stats">
|
||||
<column-layout>
|
||||
<div
|
||||
v-if="abilities.length"
|
||||
class="ability-scores"
|
||||
v-for="ability in abilities"
|
||||
:key="ability._id"
|
||||
>
|
||||
<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
|
||||
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="double-border name label">
|
||||
{{ ability.name }}
|
||||
<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
|
||||
v-for="toggle in toggles"
|
||||
:key="toggle._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="box double-border" />
|
||||
<div class="label double-border">
|
||||
{{ toggle.name }}
|
||||
<div
|
||||
class="number-label"
|
||||
>
|
||||
<div class="box double-border" />
|
||||
<div class="label double-border">
|
||||
{{ toggle.name }}
|
||||
</div>
|
||||
</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' : 'octagon-border'"
|
||||
class="number big-number"
|
||||
class="number-label"
|
||||
:class="stat.variableName == 'armor' && 'shield-number-label'"
|
||||
>
|
||||
{{ stat.value }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ stat.name }}
|
||||
<div
|
||||
:class="stat.variableName == 'armor' ? 'shield-border' : 'octagon-border'"
|
||||
class="number big-number"
|
||||
>
|
||||
{{ stat.value }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ stat.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="modifier in modifiers"
|
||||
:key="modifier._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octagon-border big-number">
|
||||
{{ numberToSignedString(modifier.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ modifier.name }}
|
||||
<div
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octagon-border big-number">
|
||||
{{ numberToSignedString(modifier.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ modifier.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-for="check in checks"
|
||||
:key="check._id"
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octagon-border big-number">
|
||||
{{ numberToSignedString(check.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ check.name }}
|
||||
<div
|
||||
class="number-label"
|
||||
>
|
||||
<div class="number octagon-border big-number">
|
||||
{{ numberToSignedString(check.value) }}
|
||||
</div>
|
||||
<div class="label double-border">
|
||||
{{ check.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="healthBar in healthBars"
|
||||
:key="healthBar._id"
|
||||
class="m-2"
|
||||
>
|
||||
<div class="double-border">
|
||||
<div class="label">
|
||||
@@ -115,7 +122,6 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="hitDice.length"
|
||||
class="hit-dice m-2"
|
||||
>
|
||||
<div class="double-border">
|
||||
<div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="inventory-container">
|
||||
<div class="d-flex justify-center">
|
||||
<property-icon
|
||||
class="ml-2"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template lang="html">
|
||||
<v-list-item-content style="min-height: 60px;">
|
||||
<v-list-item-content :style="dense ? undefined : 'min-height: 60px;'">
|
||||
<v-list-item-title class="d-flex align-center">
|
||||
<div
|
||||
v-if="!renaming"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template lang="html">
|
||||
<v-list
|
||||
expand
|
||||
:nav="nav"
|
||||
:dense="dense"
|
||||
class="creature-folder-list"
|
||||
>
|
||||
<creature-list
|
||||
@@ -14,7 +16,7 @@
|
||||
v-for="folder in folders"
|
||||
:key="folder._id"
|
||||
v-model="openFolders[folder._id]"
|
||||
group="folder"
|
||||
:dense="dense"
|
||||
>
|
||||
<template #activator>
|
||||
<creature-folder-header
|
||||
@@ -60,6 +62,7 @@ export default {
|
||||
default: undefined,
|
||||
},
|
||||
dense: Boolean,
|
||||
nav: Boolean,
|
||||
},
|
||||
data(){return{
|
||||
openFolders: {},
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
<v-list-item
|
||||
v-bind="$attrs"
|
||||
:class="isSelected && 'primary--text v-list-item--active'"
|
||||
:dense="dense"
|
||||
v-on="selection ? { click() {$emit('click')} } : {}"
|
||||
>
|
||||
<v-list-item-avatar
|
||||
:color="isSelected ? 'red darken-1' : model.color || 'grey'"
|
||||
:size="dense ? 30 : undefined"
|
||||
class="white--text"
|
||||
style="transition: background 0.3s;"
|
||||
>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
class="slot-card d-flex flex-column"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
@click="$emit('click')"
|
||||
@click="fillSlot"
|
||||
>
|
||||
<card-highlight
|
||||
:active="hover"
|
||||
@@ -28,7 +28,7 @@
|
||||
<v-btn
|
||||
icon
|
||||
color="accent"
|
||||
@click.stop="$emit('ignore')"
|
||||
@click.stop="ignoreProp"
|
||||
>
|
||||
<v-icon>mdi-close</v-icon>
|
||||
</v-btn>
|
||||
@@ -39,6 +39,9 @@
|
||||
<script lang="js">
|
||||
import CardHighlight from '/imports/client/ui/components/CardHighlight.vue';
|
||||
import PropertyDescription from '/imports/client/ui/properties/viewers/shared/PropertyDescription.vue';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.js';
|
||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -51,6 +54,9 @@ export default {
|
||||
isDark: false,
|
||||
},
|
||||
},
|
||||
context: {
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
@@ -72,5 +78,45 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
fillSlot() {
|
||||
const slotId = this.model._id;
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'slot-fill-dialog',
|
||||
elementId: `slot-card-${slotId}`,
|
||||
data: {
|
||||
slotId,
|
||||
creatureId: this.context.creatureId,
|
||||
},
|
||||
callback(nodeIds){
|
||||
if (!nodeIds || !nodeIds.length) return;
|
||||
insertPropertyFromLibraryNode.call({
|
||||
nodeIds,
|
||||
parentRef: {
|
||||
'id': slotId,
|
||||
'collection': 'creatureProperties',
|
||||
},
|
||||
}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
snackbar({text: error.reason || error.message || error.toString()});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
ignoreProp(){
|
||||
updateCreatureProperty.call({
|
||||
_id: this.model._id,
|
||||
path: ['ignored'],
|
||||
value: true
|
||||
}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
snackbar({text: error.reason || error.message || error.toString()});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<column-layout wide-columns class="slots-to-fill">
|
||||
<column-layout
|
||||
wide-columns
|
||||
class="slots-to-fill"
|
||||
>
|
||||
<v-fade-transition
|
||||
group
|
||||
leave-absolute
|
||||
@@ -39,7 +42,6 @@ import SlotCard from '/imports/client/ui/creature/slots/SlotCard.vue';
|
||||
import PointBuyCard from '/imports/client/ui/properties/components/pointBuy/PointBuyCard.vue';
|
||||
import ColumnLayout from '/imports/client/ui/components/ColumnLayout.vue';
|
||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js';
|
||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.js';
|
||||
|
||||
export default {
|
||||
@@ -64,31 +66,6 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
fillSlot(slotId){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'slot-fill-dialog',
|
||||
elementId: `slot-card-${slotId}`,
|
||||
data: {
|
||||
slotId,
|
||||
creatureId: this.context.creatureId,
|
||||
},
|
||||
callback(nodeIds){
|
||||
if (!nodeIds || !nodeIds.length) return;
|
||||
insertPropertyFromLibraryNode.call({
|
||||
nodeIds,
|
||||
parentRef: {
|
||||
'id': slotId,
|
||||
'collection': 'creatureProperties',
|
||||
},
|
||||
}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
snackbar({text: error.reason || error.message || error.toString()});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
editPointBuy(_id){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
@@ -101,10 +78,21 @@ export default {
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
slots(){
|
||||
return CreatureProperties.find({
|
||||
type: 'propertySlot',
|
||||
slots() {
|
||||
const folderIds = CreatureProperties.find({
|
||||
'ancestors.id': this.context.creatureId,
|
||||
type: 'folder',
|
||||
hideStatsGroup: true,
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, { fields: { _id: 1 } }).map(folder => folder._id);
|
||||
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: this.context.creatureId,
|
||||
$nin: folderIds,
|
||||
},
|
||||
type: 'propertySlot',
|
||||
ignored: { $ne: true },
|
||||
$and: [
|
||||
{
|
||||
|
||||
@@ -63,21 +63,14 @@
|
||||
},
|
||||
watch: {
|
||||
dialogs(newDialogs) {
|
||||
let el = document.documentElement;
|
||||
const el = document.documentElement;
|
||||
if (newDialogs.length) {
|
||||
this.top = el.scrollTop;
|
||||
if (el.scrollHeight > el.clientHeight){
|
||||
el.style.position = 'fixed';
|
||||
el.style.top = `${-this.top}px`;
|
||||
el.style.left = 0;
|
||||
el.style.right = 0;
|
||||
el.style.overflowY = 'scroll';
|
||||
el.style.overflowY = 'hidden';
|
||||
el.scrollTop = this.top;
|
||||
}
|
||||
} else {
|
||||
el.style.position = null;
|
||||
el.style.top = null;
|
||||
el.style.left = null;
|
||||
el.style.right = null;
|
||||
el.style.overflowY = null;
|
||||
el.scrollTop = this.top;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
Sign in
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<v-list>
|
||||
<v-list
|
||||
nav
|
||||
class="links"
|
||||
>
|
||||
<v-list-item v-if="signedIn">
|
||||
<v-list-item-content>
|
||||
<v-list-item-title>
|
||||
@@ -91,7 +94,7 @@ export default {
|
||||
let isLoggedIn = !!Meteor.userId();
|
||||
let links = [
|
||||
{ title: 'Home', icon: 'mdi-home', to: '/' },
|
||||
{ title: 'Characters', icon: 'mdi-account-group', to: '/characterList', requireLogin: true },
|
||||
{ title: 'Characters', icon: 'mdi-account-group', to: '/character-list', requireLogin: true },
|
||||
{ title: 'Library', icon: 'mdi-library-shelves', to: '/library', requireLogin: true },
|
||||
//{title: 'Tabletops', icon: 'api', to: '/tabletops', requireLogin: true},
|
||||
//{title: 'Friends', icon: 'people', to: '/friends', requireLogin: true},
|
||||
@@ -138,3 +141,9 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.links .v-list-item:not(:last-child):not(:only-child) {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div>
|
||||
<section>
|
||||
<v-parallax
|
||||
src="/images/paper-dice-crown-with-candy.png"
|
||||
height="400"
|
||||
src="/images/paper-dice-crown.webp"
|
||||
height="300"
|
||||
>
|
||||
<v-layout
|
||||
column
|
||||
@@ -12,7 +12,7 @@
|
||||
class="white--text"
|
||||
>
|
||||
<p
|
||||
class="white--text ma-2 text-h5 text-center"
|
||||
class="white--text ma-2 text-center"
|
||||
style="max-width: 1200px;"
|
||||
>
|
||||
DiceCloud is a single-developer project started in 2014 with the aim of
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
style="height: 100%"
|
||||
>
|
||||
<v-container>
|
||||
<v-row justify="center">
|
||||
<v-row
|
||||
justify="center"
|
||||
class="mb-16"
|
||||
>
|
||||
<v-col
|
||||
cols="12"
|
||||
xl="8"
|
||||
|
||||
@@ -1,29 +1,75 @@
|
||||
<template>
|
||||
<div
|
||||
class="content layout column justify-space-between"
|
||||
class="home content layout column justify-space-between"
|
||||
style="min-height: 100%;"
|
||||
>
|
||||
<section
|
||||
class="py-12 px-4"
|
||||
>
|
||||
<v-row
|
||||
align="end"
|
||||
justify="center"
|
||||
class="mb-8"
|
||||
>
|
||||
<v-col
|
||||
class="text-center"
|
||||
cols="12"
|
||||
>
|
||||
<h1 class="text-h4 mb-4">
|
||||
Free, Auditable, real-time character tracking for 5th edition
|
||||
</h1>
|
||||
<h4 class="subheading">
|
||||
Spend less time shuffling paper, and more time playing the game
|
||||
</h4>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-layout
|
||||
v-if="!signedIn"
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<v-btn
|
||||
color="accent"
|
||||
rounded
|
||||
large
|
||||
to="/register"
|
||||
class="mr-4"
|
||||
>
|
||||
Register
|
||||
</v-btn>
|
||||
<v-btn
|
||||
color="accent"
|
||||
rounded
|
||||
outlined
|
||||
large
|
||||
to="/sign-in"
|
||||
>
|
||||
Sign In
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
v-else
|
||||
align-center
|
||||
justify-center
|
||||
>
|
||||
<v-btn
|
||||
color="accent"
|
||||
rounded
|
||||
large
|
||||
to="/character-list"
|
||||
class="mr-4"
|
||||
>
|
||||
My Characters
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</section>
|
||||
<section>
|
||||
<v-parallax
|
||||
src="/images/paper-dice-crown.png"
|
||||
src="/images/crown-dice-on-ipad.webp"
|
||||
height="300"
|
||||
>
|
||||
<v-layout
|
||||
column
|
||||
align-center
|
||||
justify-center
|
||||
class="white--text"
|
||||
>
|
||||
<h1 class="white--text mb-2 text-h4 text-center">
|
||||
DiceCloud - Free, Auditable, real-time character tracking for 5th edition
|
||||
</h1>
|
||||
<div class="subheading mb-3 text-center">
|
||||
Spend less time shuffling paper, and more time playing the game
|
||||
</div>
|
||||
</v-layout>
|
||||
</v-parallax>
|
||||
/>
|
||||
</section>
|
||||
<section class="text-center">
|
||||
<section class="text-center py-8 px-4">
|
||||
<v-layout
|
||||
wrap
|
||||
justify-space-around
|
||||
@@ -43,7 +89,7 @@
|
||||
Free, open source, community funded
|
||||
</h3>
|
||||
<p>
|
||||
DiceCloud is free to use. Its hosting is funded via Patreon,
|
||||
DiceCloud is free to use, funded via Patreon,
|
||||
and the source code is available on Github under a GPL license.
|
||||
</p>
|
||||
</v-layout>
|
||||
@@ -58,11 +104,11 @@
|
||||
mdi-ballot-outline
|
||||
</v-icon>
|
||||
<h3 class="mb-2">
|
||||
Character sheets optimised for one ruleset
|
||||
Custom everything
|
||||
</h3>
|
||||
<p>
|
||||
By having a narrrow scope, DiceCloud can be the best at what it
|
||||
does: being a fully automated character tracker
|
||||
Add new ability scores, skills, health-bars, and stats to your character.
|
||||
The entire sheet is under your control.
|
||||
</p>
|
||||
</v-layout>
|
||||
<v-layout
|
||||
@@ -76,31 +122,37 @@
|
||||
mdi-file-tree-outline
|
||||
</v-icon>
|
||||
<h3 class="mb-2">
|
||||
Inventory manager
|
||||
Advanced Character Engine
|
||||
</h3>
|
||||
<p>
|
||||
Equiping items changes your characters stats automatically.
|
||||
Characters are computed in real-time based on their equipment,
|
||||
features, and buffs.
|
||||
</p>
|
||||
</v-layout>
|
||||
</v-layout>
|
||||
</section>
|
||||
<section
|
||||
v-if="!signedIn"
|
||||
class="ma-5"
|
||||
>
|
||||
<v-layout
|
||||
align-center
|
||||
justify-space-around
|
||||
>
|
||||
<v-btn
|
||||
color="accent"
|
||||
rounded
|
||||
large
|
||||
to="/sign-in"
|
||||
<section class="pa-8">
|
||||
<v-row>
|
||||
<v-col
|
||||
v-for="(card, index) in highlightCards"
|
||||
:key="index"
|
||||
v-bind="cols"
|
||||
>
|
||||
Sign In
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<v-card
|
||||
tile
|
||||
:elevation="0"
|
||||
>
|
||||
<v-img
|
||||
class="white--text align-end"
|
||||
:src="'/images/screenshots/' + card.img"
|
||||
gradient="to bottom, rgba(0,0,0,0), rgba(0,0,0,.5)"
|
||||
height="360px"
|
||||
>
|
||||
<v-card-title v-text="card.text" />
|
||||
</v-img>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</section>
|
||||
<section class="text-center grey darken-3 white--text pa-5">
|
||||
<h1>
|
||||
@@ -120,11 +172,16 @@
|
||||
]"
|
||||
:key="btn.name"
|
||||
:href="btn.link"
|
||||
text
|
||||
outlined
|
||||
large
|
||||
color="primary"
|
||||
dark
|
||||
>
|
||||
{{ btn.name }}
|
||||
<v-icon
|
||||
right
|
||||
>
|
||||
mdi-open-in-new
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
</section>
|
||||
@@ -133,6 +190,25 @@
|
||||
|
||||
<script lang="js">
|
||||
export default {
|
||||
data() {return {
|
||||
cols: {
|
||||
cols: 12,
|
||||
sm: 6,
|
||||
md: 4,
|
||||
lg: 3,
|
||||
xl: 2,
|
||||
},
|
||||
highlightCards: [
|
||||
{ text: 'Automated actions', img: 'actions.webp' },
|
||||
{ text: 'Auditable stats', img: 'auditable.webp' },
|
||||
{ text: 'Dice rolling', img: 'automated-dice-rolls.webp' },
|
||||
{ text: 'Hackable character builder', img: 'build-system.webp' },
|
||||
{ text: 'Drag and drop inventory manager', img: 'inventory.webp' },
|
||||
{ text: 'Custom libraries of content', img: 'libraries-of-content.webp' },
|
||||
{ text: 'Discord webhooks', img: 'send-to-discord.webp' },
|
||||
{ text: 'Printed character sheets', img: 'printing.webp' },
|
||||
],
|
||||
}},
|
||||
meteor: {
|
||||
signedIn() {
|
||||
return Meteor.userId();
|
||||
@@ -145,4 +221,7 @@ export default {
|
||||
.selling-points>* {
|
||||
max-width: 400px;
|
||||
}
|
||||
.dark-gradient {
|
||||
background: linear-gradient(0deg, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 100%);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -39,7 +39,10 @@
|
||||
>
|
||||
Reset Password
|
||||
</v-btn>
|
||||
<div class="error--text">
|
||||
<div
|
||||
v-if="error"
|
||||
class="error--text"
|
||||
>
|
||||
{{ error }}
|
||||
</div>
|
||||
<v-layout>
|
||||
@@ -59,6 +62,13 @@
|
||||
Register
|
||||
</v-btn>
|
||||
</v-layout>
|
||||
<div class="text-caption mt-4 px-4">
|
||||
<p>
|
||||
DiceCloud Version 2 requires a new account to use.
|
||||
</p><p>
|
||||
Version 1 is still available at <a href="https://v1.dicecloud.com">v1.dicecloud.com</a>
|
||||
</p>
|
||||
</div>
|
||||
</v-layout>
|
||||
</v-form>
|
||||
<v-divider class="ma-4" />
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
:model="prop"
|
||||
:data-id="prop._id"
|
||||
@click="$emit('click-property', {_id: prop._id})"
|
||||
@sub-click="_id => $emit('sub-click', _id)"
|
||||
@click-property="e => $emit('click-property', e)"
|
||||
@sub-click="e => $emit('sub-click', e)"
|
||||
@remove="$emit('remove', prop._id)"
|
||||
/>
|
||||
</v-card>
|
||||
@@ -27,15 +28,15 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
|
||||
import propComponents from '/imports/client/ui/properties/components/folders/propertyComponentIndex.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
...propComponents,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
Object.assign(this.$options.components, propComponents);
|
||||
},
|
||||
meteor: {
|
||||
properties() {
|
||||
const props = [];
|
||||
@@ -50,6 +51,7 @@ export default {
|
||||
deactivatedByAncestor: { $ne: true },
|
||||
},
|
||||
{
|
||||
type: { $ne: 'toggle' },
|
||||
inactive: { $ne: true }
|
||||
},
|
||||
],
|
||||
@@ -60,7 +62,7 @@ export default {
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
}).forEach(prop => {
|
||||
if (this.$options.components[prop.type]) {
|
||||
if (propComponents[prop.type]) {
|
||||
props.push(prop);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,44 +1,55 @@
|
||||
<template>
|
||||
<div class="attribute">
|
||||
<ability-list-tile
|
||||
v-if="model.attributeType === 'ability'"
|
||||
<div>
|
||||
<div
|
||||
class="attribute"
|
||||
:data-id="dataId"
|
||||
>
|
||||
<ability-list-tile
|
||||
v-if="model.attributeType === 'ability'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
/>
|
||||
<hit-dice-list-tile
|
||||
v-else-if="model.attributeType === 'hitDice'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@change="({ type, value }) => damageProperty({type, value: -value})"
|
||||
/>
|
||||
<health-bar
|
||||
v-else-if="model.attributeType === 'healthBar'"
|
||||
:model="model"
|
||||
@change="damageProperty"
|
||||
@click="$emit('click')"
|
||||
/>
|
||||
<spell-slot-list-tile
|
||||
v-else-if="model.attributeType === 'spellSlot'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
/>
|
||||
<resource-card-content
|
||||
v-else-if="model.attributeType === 'resource'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@change="({ type, value }) => damageProperty({type, value: -value})"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
/>
|
||||
<attribute-card-content
|
||||
v-else-if="model.attributeType !== 'utility'"
|
||||
class="pointer"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
/>
|
||||
<card-highlight :active="hover" />
|
||||
</div>
|
||||
<folder-group-children
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@click-property="e => $emit('click-property', e)"
|
||||
@sub-click="e => $emit('sub-click', e)"
|
||||
@remove="e => $emit('remove', e)"
|
||||
/>
|
||||
<hit-dice-list-tile
|
||||
v-else-if="model.attributeType === 'hitDice'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@change="({ type, value }) => damageProperty({type, value: -value})"
|
||||
/>
|
||||
<health-bar
|
||||
v-else-if="model.attributeType === 'healthBar'"
|
||||
:model="model"
|
||||
@change="damageProperty"
|
||||
@click="$emit('click')"
|
||||
/>
|
||||
<spell-slot-list-tile
|
||||
v-else-if="model.attributeType === 'spellSlot'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
/>
|
||||
<resource-card-content
|
||||
v-else-if="model.attributeType === 'resource'"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@change="({ type, value }) => damageProperty({type, value: -value})"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
/>
|
||||
<attribute-card-content
|
||||
v-else-if="model.attributeType !== 'utility'"
|
||||
class="pointer"
|
||||
:model="model"
|
||||
@click="$emit('click')"
|
||||
@mouseover="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
/>
|
||||
<card-highlight :active="hover" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -50,6 +61,7 @@ import SpellSlotListTile from '/imports/client/ui/properties/components/attribut
|
||||
import ResourceCardContent from '/imports/client/ui/properties/components/attributes/ResourceCardContent.vue';
|
||||
import AttributeCardContent from '/imports/client/ui/properties/components/attributes/AttributeCardContent.vue';
|
||||
import CardHighlight from '/imports/client/ui/components/CardHighlight.vue';
|
||||
import FolderGroupChildren from '/imports/client/ui/properties/components/folders/folderGroupComponents/FolderGroupChildren.vue';
|
||||
|
||||
import damageProperty from '/imports/api/creature/creatureProperties/methods/damageProperty.js';
|
||||
|
||||
@@ -62,12 +74,17 @@ export default {
|
||||
ResourceCardContent,
|
||||
AttributeCardContent,
|
||||
CardHighlight,
|
||||
FolderGroupChildren,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
dataId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -81,6 +98,10 @@ export default {
|
||||
value: change.value
|
||||
});
|
||||
},
|
||||
log({_id}) {
|
||||
console.log(...arguments)
|
||||
this.$emit('click-property', { _id });
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="properties && properties.length"
|
||||
>
|
||||
<component
|
||||
:is="prop.type"
|
||||
v-for="prop in properties"
|
||||
:key="prop._id"
|
||||
:model="prop"
|
||||
:data-id="prop._id"
|
||||
@click="$emit('click-property', {_id: prop._id})"
|
||||
@click-property="(e) => $emit('click-property', e)"
|
||||
@sub-click="(e) => $emit('sub-click', e)"
|
||||
@remove="$emit('remove', prop._id)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import propComponents from '/imports/client/ui/properties/components/folders/propertyComponentIndex.js';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
beforeCreate() {
|
||||
Object.assign(this.$options.components, propComponents);
|
||||
},
|
||||
meteor: {
|
||||
properties() {
|
||||
const props = [];
|
||||
CreatureProperties.find({
|
||||
'parent.id': this.model._id,
|
||||
removed: { $ne: true },
|
||||
overridden: { $ne: true },
|
||||
$or: [
|
||||
{
|
||||
type: 'toggle',
|
||||
showUI: true,
|
||||
deactivatedByAncestor: { $ne: true },
|
||||
},
|
||||
{
|
||||
type: { $ne: 'toggle' },
|
||||
inactive: { $ne: true },
|
||||
},
|
||||
],
|
||||
$nor: [
|
||||
{ hideWhenTotalZero: true, total: 0 },
|
||||
{ hideWhenValueZero: true, value: 0 },
|
||||
],
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
}).forEach(prop => {
|
||||
if (propComponents[prop.type]) {
|
||||
props.push(prop);
|
||||
}
|
||||
});
|
||||
return props;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<build-tree-node-list
|
||||
:children="slotBuildTree"
|
||||
class="mx-2"
|
||||
@selected="_id => $emit('sub-click', _id)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js';
|
||||
import BuildTreeNodeList from '/imports/client/ui/creature/buildTree/BuildTreeNodeList.vue';
|
||||
|
||||
function traverse(tree, callback, parents = []){
|
||||
tree.forEach(node => {
|
||||
callback(node, parents);
|
||||
traverse(node.children, callback, [...parents, node]);
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
components: {
|
||||
BuildTreeNodeList,
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
slotBuildTree() {
|
||||
const slots = CreatureProperties.find({
|
||||
$and: [ {
|
||||
$or: [
|
||||
{ 'ancestors.id': this.model._id, },
|
||||
{ '_id': this.model._id, },
|
||||
],
|
||||
}, {
|
||||
$or:
|
||||
[
|
||||
{ 'slotCondition.value': { $nin: [false, 0, ''] } },
|
||||
{ 'slotCondition.value': { $exists: false } },
|
||||
{ 'slotCondition': { $exists: false } },
|
||||
],
|
||||
} ],
|
||||
type: { $in: ['propertySlot', 'pointBuy'] },
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 }
|
||||
});
|
||||
const slotIds = slots.map(s => s._id);
|
||||
const slotChildren = CreatureProperties.find({
|
||||
'parent.id': { $in: slotIds },
|
||||
removed: { $ne: true },
|
||||
}, {
|
||||
sort: { order: 1 },
|
||||
});
|
||||
const tree = nodeArrayToTree([
|
||||
...slots.fetch(),
|
||||
...slotChildren.fetch()
|
||||
]);
|
||||
traverse(tree, (child, parents) => {
|
||||
const model = child.node;
|
||||
const isSlotWithSpace = model.type === 'propertySlot' && (
|
||||
model.spaceLeft > 0 ||
|
||||
!model.quantityExpected ||
|
||||
model.quantityExpected.value === 0
|
||||
);
|
||||
if (isSlotWithSpace) {
|
||||
model._canFill = true;
|
||||
parents.forEach(node => {
|
||||
node.node._descendantCanFill = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
return tree;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -17,16 +17,17 @@ import item from '/imports/client/ui/properties/components/inventory/ItemListTil
|
||||
import note from '/imports/client/ui/properties/components/persona/NoteCard.vue';
|
||||
//import pointBuy from '';
|
||||
//import proficiency from '';
|
||||
//import propertySlot from '';
|
||||
import propertySlot from '/imports/client/ui/properties/components/folders/folderGroupComponents/SlotBuildTree.vue';
|
||||
//import reference from '';
|
||||
//import roll from '';
|
||||
//import savingThrow from '';
|
||||
import skill from '/imports/client/ui/properties/components/skills/SkillListTile.vue';
|
||||
//import slotFiller from '';
|
||||
//import spellList from '';
|
||||
//import spell from '';
|
||||
import spellList from '/imports/client/ui/properties/components/spells/SpellListCard.vue';
|
||||
import spell from '/imports/client/ui/properties/components/spells/SpellListTile.vue';
|
||||
import toggle from '/imports/client/ui/properties/components/toggles/ToggleCard.vue';
|
||||
//import trigger from '';
|
||||
import FolderGroupChildren from '/imports/client/ui/properties/components/folders/folderGroupComponents/FolderGroupChildren.vue';
|
||||
|
||||
export default {
|
||||
action,
|
||||
@@ -43,19 +44,19 @@ export default {
|
||||
//damageMultiplier,
|
||||
//effect,
|
||||
feature,
|
||||
//folder,
|
||||
// folder // Like actions, we don't show sub-folders
|
||||
item,
|
||||
note,
|
||||
//pointBuy,
|
||||
//proficiency,
|
||||
//propertySlot,
|
||||
propertySlot,
|
||||
//reference,
|
||||
//roll,
|
||||
//savingThrow,
|
||||
skill,
|
||||
//slotFiller,
|
||||
//spellList,
|
||||
//spell,
|
||||
slotFiller: FolderGroupChildren,
|
||||
spellList,
|
||||
spell,
|
||||
toggle,
|
||||
//trigger,
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<v-expand-transition>
|
||||
<div v-if="model.groupStats">
|
||||
<smart-switch
|
||||
label="Hide children from stats tab"
|
||||
label="Hide children from their default locations"
|
||||
:value="model.hideStatsGroup"
|
||||
:error-messages="errors.hideStatsGroup"
|
||||
@change="change('hideStatsGroup', ...arguments)"
|
||||
|
||||
@@ -13,6 +13,7 @@ const CharacterSheetPage = () => import('/imports/client/ui/pages/CharacterSheet
|
||||
const CharacterSheetToolbar = () => import('/imports/client/ui/creature/character/CharacterSheetToolbar.vue');
|
||||
const CharacterSheetRightDrawer = () => import('/imports/client/ui/creature/character/CharacterSheetRightDrawer.vue');
|
||||
const CharacterSheetPrinted = () => import('/imports/client/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue');
|
||||
const CharacterSheetPrintedToolbar = () => import('/imports/client/ui/creature/character/printedCharacterSheet/CharacterSheetPrintedToolbar.vue');
|
||||
const SignIn = () => import('/imports/client/ui/pages/SignIn.vue');
|
||||
const Register = () => import('/imports/client/ui/pages/Register.vue');
|
||||
const IconAdmin = () => import('/imports/client/ui/icons/IconAdmin.vue');
|
||||
@@ -185,11 +186,14 @@ RouterFactory.configure(router => {
|
||||
alias: '/print-character/:id/:urlName',
|
||||
components: {
|
||||
default: CharacterSheetPrinted,
|
||||
toolbar: CharacterSheetPrintedToolbar,
|
||||
},
|
||||
meta: {
|
||||
title: 'Print Character Sheet',
|
||||
},
|
||||
}, {
|
||||
},
|
||||
/* Not ready for prime time <3
|
||||
{
|
||||
path: '/tabletops',
|
||||
name: 'tabletops',
|
||||
component: Tabletops,
|
||||
@@ -203,7 +207,9 @@ RouterFactory.configure(router => {
|
||||
rightDrawer: TabletopRightDrawer,
|
||||
},
|
||||
beforeEnter: ensureLoggedIn,
|
||||
}, {
|
||||
},
|
||||
*/
|
||||
{
|
||||
path: '/friends',
|
||||
components: {
|
||||
default: NotImplemented,
|
||||
|
||||
37
app/imports/client/ui/styles/body.css
Normal file
@@ -0,0 +1,37 @@
|
||||
html {
|
||||
--scrollbarBG: #f0f0f0;
|
||||
--thumbBG: #cdcdcd;
|
||||
scrollbar-gutter: stable;
|
||||
background-color: var(--scrollbarBG);
|
||||
}
|
||||
|
||||
html:has(#app.theme--dark) {
|
||||
--scrollbarBG: #212121;
|
||||
--thumbBG: #404040;
|
||||
}
|
||||
|
||||
#app.theme--dark {
|
||||
--scrollbarBG: #212121;
|
||||
--thumbBG: #404040;
|
||||
}
|
||||
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--thumbBG) var(--scrollbarBG);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: var(--scrollbarBG);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--thumbBG);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import './body.css';
|
||||
import './cardColors.css';
|
||||
import './cardTitles.css';
|
||||
import './centeredInputs.css';
|
||||
|
||||
@@ -2,8 +2,8 @@ import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import dialogStackStore from '/imports/client/ui/dialogStack/dialogStackStore.js';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||
const tabs = ['stats', 'features', 'actions', 'spells', 'inventory', 'journal', 'build', 'tree'];
|
||||
const tabsWithoutSpells = ['stats', 'features', 'actions', 'inventory', 'journal', 'build', 'tree'];
|
||||
const tabs = ['stats', 'actions', 'spells', 'inventory', 'features', 'journal', 'build', 'tree'];
|
||||
const tabsWithoutSpells = ['stats', 'actions', 'inventory', 'features', 'journal', 'build', 'tree'];
|
||||
|
||||
Vue.use(Vuex);
|
||||
const store = new Vuex.Store({
|
||||
@@ -50,6 +50,19 @@ const store = new Vuex.Store({
|
||||
document.title = value;
|
||||
},
|
||||
setTabForCharacterSheet(state, { tab, id }) {
|
||||
// Convert tab names to tab numbers
|
||||
if (typeof tab === 'string') {
|
||||
const creature = Creatures.findOne(id);
|
||||
if (creature?.settings?.hideSpellsTab) {
|
||||
tab = tabsWithoutSpells.indexOf(tab);
|
||||
} else {
|
||||
tab = tabs.indexOf(tab);
|
||||
}
|
||||
if (!(tab > -1)) {
|
||||
throw 'Could not find requested tab';
|
||||
}
|
||||
console.log('resolved: ', tab);
|
||||
}
|
||||
Vue.set(state.characterSheetTabs, id, tab);
|
||||
},
|
||||
setShowDetailsDialog(state, value) {
|
||||
|
||||
@@ -30,7 +30,7 @@ const STORAGE_LIMITS = Object.freeze({
|
||||
rollCount: 64,
|
||||
statsToTarget: 64,
|
||||
tagCount: 64,
|
||||
writersCount: 20,
|
||||
writersCount: 32,
|
||||
libraryCollectionCount: 32,
|
||||
pointBuyRowsCount: 32,
|
||||
});
|
||||
|
||||
114
app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dicecloud",
|
||||
"version": "2.0.43",
|
||||
"version": "2.0.45",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -56,11 +56,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.20.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz",
|
||||
"integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==",
|
||||
"version": "7.20.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.6.tgz",
|
||||
"integrity": "sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.10"
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"@chenfengyuan/vue-countdown": {
|
||||
@@ -185,14 +185,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz",
|
||||
"integrity": "sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.44.0.tgz",
|
||||
"integrity": "sha512-j5ULd7FmmekcyWeArx+i8x7sdRHzAtXTkmDPthE4amxZOWKFK7bomoJ4r7PJ8K7PoMzD16U8MmuZFAonr1ERvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.43.0",
|
||||
"@typescript-eslint/type-utils": "5.43.0",
|
||||
"@typescript-eslint/utils": "5.43.0",
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/type-utils": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"debug": "^4.3.4",
|
||||
"ignore": "^5.2.0",
|
||||
"natural-compare-lite": "^1.4.0",
|
||||
@@ -222,14 +222,14 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.43.0.tgz",
|
||||
"integrity": "sha512-2iHUK2Lh7PwNUlhFxxLI2haSDNyXvebBO9izhjhMoDC+S3XI9qt2DGFUsiJ89m2k7gGYch2aEpYqV5F/+nwZug==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.44.0.tgz",
|
||||
"integrity": "sha512-H7LCqbZnKqkkgQHaKLGC6KUjt3pjJDx8ETDqmwncyb6PuoigYajyAwBGz08VU/l86dZWZgI4zm5k2VaKqayYyA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.43.0",
|
||||
"@typescript-eslint/types": "5.43.0",
|
||||
"@typescript-eslint/typescript-estree": "5.43.0",
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -245,23 +245,23 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz",
|
||||
"integrity": "sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.44.0.tgz",
|
||||
"integrity": "sha512-2pKml57KusI0LAhgLKae9kwWeITZ7IsZs77YxyNyIVOwQ1kToyXRaJLl+uDEXzMN5hnobKUOo2gKntK9H1YL8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.43.0",
|
||||
"@typescript-eslint/visitor-keys": "5.43.0"
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/type-utils": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz",
|
||||
"integrity": "sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.44.0.tgz",
|
||||
"integrity": "sha512-A1u0Yo5wZxkXPQ7/noGkRhV4J9opcymcr31XQtOzcc5nO/IHN2E2TPMECKWYpM3e6olWEM63fq/BaL1wEYnt/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/typescript-estree": "5.43.0",
|
||||
"@typescript-eslint/utils": "5.43.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"@typescript-eslint/utils": "5.44.0",
|
||||
"debug": "^4.3.4",
|
||||
"tsutils": "^3.21.0"
|
||||
},
|
||||
@@ -278,19 +278,19 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.43.0.tgz",
|
||||
"integrity": "sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.44.0.tgz",
|
||||
"integrity": "sha512-Tp+zDnHmGk4qKR1l+Y1rBvpjpm5tGXX339eAlRBDg+kgZkz9Bw+pqi4dyseOZMsGuSH69fYfPJCBKBrbPCxYFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz",
|
||||
"integrity": "sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.44.0.tgz",
|
||||
"integrity": "sha512-M6Jr+RM7M5zeRj2maSfsZK2660HKAJawv4Ud0xT+yauyvgrsHu276VtXlKDFnEmhG+nVEd0fYZNXGoAgxwDWJw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.43.0",
|
||||
"@typescript-eslint/visitor-keys": "5.43.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/visitor-keys": "5.44.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -319,16 +319,16 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.43.0.tgz",
|
||||
"integrity": "sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.44.0.tgz",
|
||||
"integrity": "sha512-fMzA8LLQ189gaBjS0MZszw5HBdZgVwxVFShCO3QN+ws3GlPkcy9YuS3U4wkT6su0w+Byjq3mS3uamy9HE4Yfjw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/json-schema": "^7.0.9",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@typescript-eslint/scope-manager": "5.43.0",
|
||||
"@typescript-eslint/types": "5.43.0",
|
||||
"@typescript-eslint/typescript-estree": "5.43.0",
|
||||
"@typescript-eslint/scope-manager": "5.44.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"@typescript-eslint/typescript-estree": "5.44.0",
|
||||
"eslint-scope": "^5.1.1",
|
||||
"eslint-utils": "^3.0.0",
|
||||
"semver": "^7.3.7"
|
||||
@@ -355,12 +355,12 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.43.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz",
|
||||
"integrity": "sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==",
|
||||
"version": "5.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.44.0.tgz",
|
||||
"integrity": "sha512-a48tLG8/4m62gPFbJ27FxwCOqPKxsb8KC3HkmYoq2As/4YyjQl1jDbRr1s63+g4FS/iIehjmN3L5UjmKva1HzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.43.0",
|
||||
"@typescript-eslint/types": "5.44.0",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -530,9 +530,9 @@
|
||||
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.1258.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1258.0.tgz",
|
||||
"integrity": "sha512-siqNFXlhJZVN1BizPZebJViFXtTUPgcA+yLfHKl2eC4Ied7kE7spOjZmAzpuiGUTzFagk1oWCaJ1Hit4llIoGg==",
|
||||
"version": "2.1262.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1262.0.tgz",
|
||||
"integrity": "sha512-XbaK/XUIxwLEBnHANhJ0RTZtiU288lFRj5FllSihQ5Kb0fibKyW8kJFPsY+NzzDezLH5D3WdGbTKb9fycn5TbA==",
|
||||
"requires": {
|
||||
"buffer": "4.9.2",
|
||||
"events": "1.1.1",
|
||||
@@ -1575,9 +1575,9 @@
|
||||
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
|
||||
},
|
||||
"ignore": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
|
||||
"integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz",
|
||||
"integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA=="
|
||||
},
|
||||
"ignore-styles": {
|
||||
"version": "5.0.1",
|
||||
@@ -1850,9 +1850,9 @@
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.2.tgz",
|
||||
"integrity": "sha512-JjBTFTAvuTgANXx82a5vzK9JLSMoV6V3LBVn4Uhdso6t7vXrGx7g1Cd2r6NYSsxrYbQGFCMqBDhFHyK5q2UvcQ=="
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.2.3.tgz",
|
||||
"integrity": "sha512-slWRdJkbTZ+PjkyJnE30Uid64eHwbwa1Q25INCAYfZlK4o6ylagBy/Le9eWntqJFoFT93ikUKMv47GZ4gTwHkw=="
|
||||
},
|
||||
"merge2": {
|
||||
"version": "1.4.1",
|
||||
@@ -3018,9 +3018,9 @@
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.10",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz",
|
||||
"integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw=="
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"regexpp": {
|
||||
"version": "3.2.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dicecloud",
|
||||
"version": "2.0.45",
|
||||
"version": "2.0.49",
|
||||
"description": "Unofficial Online Realtime D&D 5e App",
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
@@ -20,10 +20,10 @@
|
||||
"npm": "6.13.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.1",
|
||||
"@babel/runtime": "^7.20.6",
|
||||
"@chenfengyuan/vue-countdown": "^1.1.5",
|
||||
"@tozd/vue-observer-utils": "^0.5.0",
|
||||
"aws-sdk": "^2.1258.0",
|
||||
"aws-sdk": "^2.1262.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"chroma-js": "^2.4.2",
|
||||
"css-box-shadow": "^1.0.0-3",
|
||||
@@ -31,10 +31,10 @@
|
||||
"ddp-rate-limiter-mixin": "^1.1.10",
|
||||
"discord.js": "^12.5.3",
|
||||
"dompurify": "^2.4.1",
|
||||
"ignore": "^5.2.0",
|
||||
"ignore": "^5.2.1",
|
||||
"ignore-styles": "^5.0.1",
|
||||
"lodash": "^4.17.20",
|
||||
"marked": "^4.2.2",
|
||||
"marked": "^4.2.3",
|
||||
"meteor-node-stubs": "^1.2.5",
|
||||
"minify-css-string": "^1.0.0",
|
||||
"moo": "^0.5.2",
|
||||
@@ -58,8 +58,8 @@
|
||||
"vuex": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.43.0",
|
||||
"@typescript-eslint/parser": "^5.43.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.44.0",
|
||||
"@typescript-eslint/parser": "^5.44.0",
|
||||
"@vue/compiler-dom": "^3.2.45",
|
||||
"chai": "^4.3.7",
|
||||
"eslint": "^7.32.0",
|
||||
|
||||
BIN
app/public/images/crown-dice-on-ipad.webp
Normal file
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 582 KiB |
BIN
app/public/images/paper-dice-crown.webp
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/public/images/screenshots/actions.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/public/images/screenshots/auditable.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/public/images/screenshots/automated-dice-rolls.webp
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
app/public/images/screenshots/build-system.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/public/images/screenshots/inventory.webp
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
app/public/images/screenshots/libraries-of-content.webp
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
app/public/images/screenshots/printing.webp
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
app/public/images/screenshots/send-to-discord.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |