Improved Effect and Proficiency UI in attribute and skill viewers
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
<template lang="html">
|
||||
<div class="breadcrumbs layout align-center wrap">
|
||||
<div
|
||||
class="breadcrumbs layout align-center wrap"
|
||||
:class="{'no-icons': noIcons}"
|
||||
>
|
||||
<template v-for="(prop, index) in props">
|
||||
<v-icon
|
||||
v-if="index !== 0"
|
||||
@@ -7,12 +10,25 @@
|
||||
>
|
||||
chevron_right
|
||||
</v-icon>
|
||||
<span
|
||||
v-if="noLinks"
|
||||
:key="prop._id"
|
||||
>
|
||||
<tree-node-view
|
||||
:model="prop"
|
||||
class="breadcrumb-tree-node-view"
|
||||
/>
|
||||
</span>
|
||||
<a
|
||||
v-else
|
||||
:key="prop._id"
|
||||
:data-id="`breadcrumb-${prop._id}`"
|
||||
@click="click(prop._id)"
|
||||
>
|
||||
<tree-node-view :model="prop" />
|
||||
<tree-node-view
|
||||
:model="prop"
|
||||
class="breadcrumb-tree-node-view"
|
||||
/>
|
||||
</a>
|
||||
</template>
|
||||
</div>
|
||||
@@ -31,6 +47,8 @@
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
noLinks: Boolean,
|
||||
noIcons: Boolean,
|
||||
},
|
||||
computed:{
|
||||
props(){
|
||||
@@ -74,5 +92,14 @@
|
||||
.breadcrumbs {
|
||||
margin-bottom: 16px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.no-icons {
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="css">
|
||||
.no-icons .breadcrumb-tree-node-view .v-icon {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
<template lang="html">
|
||||
<v-list-item
|
||||
class="effect-viewer layout align-center"
|
||||
v-on="!hideBreadcrumbs ? {click} : {}"
|
||||
>
|
||||
<div class="effect-icon">
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-icon
|
||||
class="mx-2"
|
||||
style="cursor: default;"
|
||||
large
|
||||
v-on="on"
|
||||
>
|
||||
{{ effectIcon }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>{{ operation }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<div
|
||||
class="text-h4 effect-value mr-2"
|
||||
>
|
||||
{{ displayedValue }}
|
||||
</div>
|
||||
<div class="layout column my-2">
|
||||
<div class="text-body-1 mb-1">
|
||||
{{ model.name || operation }}
|
||||
</div>
|
||||
<div v-if="!hideBreadcrumbs">
|
||||
<breadcrumbs
|
||||
:model="model"
|
||||
class="text-caption"
|
||||
no-links
|
||||
no-icons
|
||||
style="margin-bottom: 0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js';
|
||||
import getEffectIcon from '/imports/ui/utility/getEffectIcon.js';
|
||||
import Breadcrumbs from '/imports/ui/creature/creatureProperties/Breadcrumbs.vue';
|
||||
import { isFinite } from 'lodash';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Breadcrumbs,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
props: {
|
||||
hideBreadcrumbs: Boolean
|
||||
},
|
||||
computed: {
|
||||
hasClickListener(){
|
||||
return this.$listeners && this.$listeners.click
|
||||
},
|
||||
resolvedValue(){
|
||||
return this.model.result !== undefined ? this.model.result : this.model.calculation;
|
||||
},
|
||||
effectIcon(){
|
||||
let value = this.resolvedValue;
|
||||
return getEffectIcon(this.model.operation, value);
|
||||
},
|
||||
operation(){
|
||||
switch(this.model.operation) {
|
||||
case 'base': return 'Base value';
|
||||
case 'add': return 'Add';
|
||||
case 'mul': return 'Multiply';
|
||||
case 'min': return 'Minimum';
|
||||
case 'max': return 'Maximum';
|
||||
case 'advantage': return 'Advantage';
|
||||
case 'disadvantage': return 'Disadvantage';
|
||||
case 'passiveAdd': return 'Passive bonus';
|
||||
case 'fail': return 'Always fail';
|
||||
case 'conditional': return 'Conditional benefit' ;
|
||||
default: return '';
|
||||
}
|
||||
},
|
||||
showValue(){
|
||||
switch(this.model.operation) {
|
||||
case 'base': return true;
|
||||
case 'add': return true;
|
||||
case 'mul': return true;
|
||||
case 'min': return true;
|
||||
case 'max': return true;
|
||||
case 'advantage': return false;
|
||||
case 'disadvantage': return false;
|
||||
case 'passiveAdd': return true;
|
||||
case 'fail': return false;
|
||||
case 'conditional': return false;
|
||||
default: return false;
|
||||
}
|
||||
},
|
||||
displayedValue(){
|
||||
let value = this.resolvedValue;
|
||||
switch(this.model.operation) {
|
||||
case 'base': return value;
|
||||
case 'add': return isFinite(value) ? Math.abs(value) : value;
|
||||
case 'mul': return value;
|
||||
case 'min': return value;
|
||||
case 'max': return value;
|
||||
case 'advantage': return;
|
||||
case 'disadvantage': return;
|
||||
case 'passiveAdd': return isFinite(value) ? Math.abs(value) : value;
|
||||
case 'fail': return;
|
||||
case 'conditional': return;
|
||||
default: return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
click(e){
|
||||
this.$emit('click', e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.icon, .effect-icon {
|
||||
min-width: 30px;
|
||||
}
|
||||
.icon {
|
||||
color: inherit !important;
|
||||
}
|
||||
.net-effect {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.effect-value {
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
107
app/imports/ui/properties/components/skills/SkillProficiency.vue
Normal file
107
app/imports/ui/properties/components/skills/SkillProficiency.vue
Normal file
@@ -0,0 +1,107 @@
|
||||
<template lang="html">
|
||||
<v-list-item
|
||||
class="proficiency-viewer layout align-center"
|
||||
v-on="!hideBreadcrumbs ? {click} : {}"
|
||||
>
|
||||
<div class="effect-icon">
|
||||
<v-tooltip bottom>
|
||||
<template #activator="{ on }">
|
||||
<v-icon
|
||||
class="mx-2"
|
||||
style="cursor: default;"
|
||||
large
|
||||
v-on="on"
|
||||
>
|
||||
{{ icon }}
|
||||
</v-icon>
|
||||
</template>
|
||||
<span>{{ proficiencyText }}</span>
|
||||
</v-tooltip>
|
||||
</div>
|
||||
<div
|
||||
class="text-h4 effect-value mr-2"
|
||||
>
|
||||
{{ proficiencyValue }}
|
||||
</div>
|
||||
<div class="layout column my-2">
|
||||
<div class="text-body-1 mb-1">
|
||||
{{ model.name || proficiencyText }}
|
||||
</div>
|
||||
<div v-if="!hideBreadcrumbs">
|
||||
<breadcrumbs
|
||||
:model="model"
|
||||
class="text-caption"
|
||||
no-links
|
||||
no-icons
|
||||
style="margin-bottom: 0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</v-list-item>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js';
|
||||
import Breadcrumbs from '/imports/ui/creature/creatureProperties/Breadcrumbs.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Breadcrumbs,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
props: {
|
||||
hideBreadcrumbs: Boolean,
|
||||
proficiencyBonus: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
icon(){
|
||||
if (this.model.value == 0.5){
|
||||
return 'brightness_2';
|
||||
} else if (this.model.value == 1) {
|
||||
return 'brightness_1'
|
||||
} else if (this.model.value == 2){
|
||||
return 'album'
|
||||
} else {
|
||||
return 'radio_button_unchecked';
|
||||
}
|
||||
},
|
||||
proficiencyText(){
|
||||
switch (this.model.value){
|
||||
case 0.5: return 'Half proficiency bonus';
|
||||
case 1: return 'Proficient';
|
||||
case 2: return 'Double proficiency bonus';
|
||||
default: return '';
|
||||
}
|
||||
},
|
||||
proficiencyValue(){
|
||||
if (!this.proficiencyBonus) return;
|
||||
return Math.ceil(this.model.value * this.proficiencyBonus);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click(e){
|
||||
this.$emit('click', e);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.icon, .effect-icon {
|
||||
min-width: 30px;
|
||||
}
|
||||
.icon {
|
||||
color: inherit !important;
|
||||
}
|
||||
.net-effect {
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.effect-value {
|
||||
min-width: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -43,27 +43,30 @@
|
||||
:calculations="model.descriptionCalculations"
|
||||
:inactive="model.inactive"
|
||||
/>
|
||||
|
||||
<effect-viewer
|
||||
v-if="context.creatureId && model.baseValueCalculation"
|
||||
:model="{
|
||||
name: 'Base value',
|
||||
result: model.baseValue,
|
||||
operation: 'base'
|
||||
}"
|
||||
/>
|
||||
<effect-viewer
|
||||
v-for="effect in effects"
|
||||
:key="effect._id"
|
||||
:model="effect"
|
||||
/>
|
||||
<v-list>
|
||||
<attribute-effect
|
||||
v-for="effect in baseEffects"
|
||||
:key="effect._id"
|
||||
:model="effect"
|
||||
:hide-breadcrumbs="effect._id === model._id"
|
||||
:data-id="effect._id"
|
||||
@click="effect._id !== model._id && clickEffect(effect._id)"
|
||||
/>
|
||||
<attribute-effect
|
||||
v-for="effect in effects"
|
||||
:key="effect._id"
|
||||
:model="effect"
|
||||
:data-id="effect._id"
|
||||
@click="clickEffect(effect._id)"
|
||||
/>
|
||||
</v-list>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'
|
||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||
import EffectViewer from '/imports/ui/properties/viewers/EffectViewer.vue';
|
||||
import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
|
||||
export default {
|
||||
@@ -71,7 +74,7 @@
|
||||
context: { default: {} }
|
||||
},
|
||||
components: {
|
||||
EffectViewer,
|
||||
AttributeEffect,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
computed: {
|
||||
@@ -87,8 +90,37 @@
|
||||
},
|
||||
methods: {
|
||||
numberToSignedString,
|
||||
clickEffect(id){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `${id}`,
|
||||
data: {_id: id},
|
||||
});
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
baseEffects(){
|
||||
if (this.context.creatureId){
|
||||
let creatureId = this.context.creatureId;
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
type: 'attribute',
|
||||
variableName: this.model.variableName,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
}).map( prop => ({
|
||||
_id: prop._id,
|
||||
name: 'Attribute base value',
|
||||
operation: 'base',
|
||||
calculation: prop.baseValueCalculation,
|
||||
result: prop.baseValue,
|
||||
stats: [prop.variableName],
|
||||
ancestors: prop.ancestors,
|
||||
}) ).filter(effect => effect.result);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
effects(){
|
||||
if (this.context.creatureId){
|
||||
let creatureId = this.context.creatureId;
|
||||
|
||||
@@ -17,19 +17,19 @@
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js';
|
||||
import ProficiencyIcon from '/imports/ui/properties/shared/ProficiencyIcon.vue';
|
||||
export default {
|
||||
components: {
|
||||
ProficiencyIcon,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
computed: {
|
||||
proficiencyText(){
|
||||
switch (this.model.value){
|
||||
case 0.5: return 'Half proficiency bonus';
|
||||
case 1: return 'Proficient';
|
||||
case 2: return 'Double proficiency bonus';
|
||||
components: {
|
||||
ProficiencyIcon,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
computed: {
|
||||
proficiencyText(){
|
||||
switch (this.model.value){
|
||||
case 0.5: return 'Half proficiency bonus';
|
||||
case 1: return 'Proficient';
|
||||
case 2: return 'Double proficiency bonus';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -40,18 +40,44 @@
|
||||
:inactive="model.inactive"
|
||||
/>
|
||||
|
||||
<effect-viewer
|
||||
v-if="context.creatureId && model.baseValue"
|
||||
:model="{
|
||||
name: 'Base value',
|
||||
result: model.baseValue,
|
||||
operation: 'base'
|
||||
}"
|
||||
<attribute-effect
|
||||
v-for="effect in baseEffects"
|
||||
:key="effect._id"
|
||||
:model="effect"
|
||||
:hide-breadcrumbs="effect._id === model._id"
|
||||
:data-id="effect._id"
|
||||
@click="effect._id !== model._id && clickEffect(effect._id)"
|
||||
/>
|
||||
<effect-viewer
|
||||
<attribute-effect
|
||||
v-if="ability"
|
||||
:key="ability._id"
|
||||
:model="ability"
|
||||
:data-id="ability._id"
|
||||
@click="clickEffect(ability._id)"
|
||||
/>
|
||||
<attribute-effect
|
||||
v-for="effect in effects"
|
||||
:key="effect._id"
|
||||
:model="effect"
|
||||
:data-id="effect._id"
|
||||
@click="clickEffect(effect._id)"
|
||||
/>
|
||||
<skill-proficiency
|
||||
v-for="proficiency in baseProficiencies"
|
||||
:key="proficiency._id"
|
||||
:model="proficiency"
|
||||
:proficiency-bonus="proficiencyBonus"
|
||||
:hide-breadcrumbs="proficiency._id === model._id"
|
||||
:data-id="proficiency._id"
|
||||
@click="clickEffect(proficiency._id)"
|
||||
/>
|
||||
<skill-proficiency
|
||||
v-for="proficiency in proficiencies"
|
||||
:key="proficiency._id"
|
||||
:model="proficiency"
|
||||
:proficiency-bonus="proficiencyBonus"
|
||||
:data-id="proficiency._id"
|
||||
@click="clickEffect(proficiency._id)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -60,11 +86,14 @@
|
||||
import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js';
|
||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||
import EffectViewer from '/imports/ui/properties/viewers/EffectViewer.vue';
|
||||
import AttributeEffect from '/imports/ui/properties/components/attributes/AttributeEffect.vue';
|
||||
import SkillProficiency from '/imports/ui/properties/components/skills/SkillProficiency.vue';
|
||||
import Creatures from '/imports/api/creature/Creatures.js';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EffectViewer,
|
||||
AttributeEffect,
|
||||
SkillProficiency,
|
||||
},
|
||||
mixins: [propertyViewerMixin],
|
||||
inject: {
|
||||
@@ -94,8 +123,37 @@ export default {
|
||||
methods: {
|
||||
numberToSignedString,
|
||||
isFinite: Number.isFinite,
|
||||
clickEffect(id){
|
||||
this.$store.commit('pushDialogStack', {
|
||||
component: 'creature-property-dialog',
|
||||
elementId: `${id}`,
|
||||
data: {_id: id},
|
||||
});
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
baseEffects(){
|
||||
if (this.context.creatureId){
|
||||
let creatureId = this.context.creatureId;
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
type: 'attribute',
|
||||
variableName: this.model.variableName,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
}).map( prop => ({
|
||||
_id: prop._id,
|
||||
name: 'Skill base value',
|
||||
operation: 'base',
|
||||
calculation: prop.baseValueCalculation,
|
||||
result: prop.baseValue,
|
||||
stats: [prop.variableName],
|
||||
ancestors: prop.ancestors,
|
||||
}) ).filter(effect => effect.result);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
effects(){
|
||||
if (this.context.creatureId){
|
||||
let creatureId = this.context.creatureId;
|
||||
@@ -109,6 +167,70 @@ export default {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
baseProficiencies(){
|
||||
if (this.context.creatureId){
|
||||
let creatureId = this.context.creatureId;
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
type: 'skill',
|
||||
variableName: this.model.variableName,
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
}).map( prop => ({
|
||||
_id: prop._id,
|
||||
name: 'Skill base proficiency',
|
||||
value: prop.baseProficiency,
|
||||
stats: [prop.variableName],
|
||||
ancestors: prop.ancestors,
|
||||
}) ).filter(prof => prof.value);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
proficiencies(){
|
||||
let creatureId = this.context.creatureId;
|
||||
if (creatureId){
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
stats: this.model.variableName,
|
||||
type: 'proficiency',
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
ability(){
|
||||
let creatureId = this.context.creatureId;
|
||||
let ability = this.model.ability;
|
||||
if (!creatureId || !ability) return;
|
||||
let abilityProp = CreatureProperties.findOne({
|
||||
'ancestors.id': creatureId,
|
||||
variableName: ability,
|
||||
type: 'attribute',
|
||||
removed: {$ne: true},
|
||||
inactive: {$ne: true},
|
||||
overridden: {$ne: true},
|
||||
});
|
||||
if (!abilityProp) return;
|
||||
return {
|
||||
_id: abilityProp._id,
|
||||
name: abilityProp.name,
|
||||
operation: 'base',
|
||||
result: abilityProp.modifier,
|
||||
stats: [this.model.variableName],
|
||||
ancestors: abilityProp.ancestors,
|
||||
}
|
||||
},
|
||||
proficiencyBonus(){
|
||||
let creatureId = this.context.creatureId;
|
||||
if (!creatureId) return;
|
||||
let creature = Creatures.findOne(creatureId)
|
||||
return creature &&
|
||||
creature.variables.proficiencyBonus &&
|
||||
creature.variables.proficiencyBonus.currentValue;
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user