mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
Attributes UI Refactoring (#9887)
* include class bonus in sorting * wip - show more information in the attributes grid * attributes tooltip + dialog redesign * fix stat calculation * fix spacings * show class in equip-gear-modal * fix buy-modal attributes-grid, clean up css * show attributes popover in profile-stats overview * add class / purchase type colors to colors.scss - replace colors by variable - clean up * translate strings
This commit is contained in:
@@ -61,3 +61,12 @@ $green-100: #5AEAB2;
|
||||
$green-500: #A6FFDF;
|
||||
|
||||
$suggested-item-color: #D5C8FF;
|
||||
|
||||
$healer-color: #cf8229;
|
||||
$rogue-color: #4F2A93;
|
||||
$warrior-color: #B01515;
|
||||
$wizard-color: #1f6ea2;
|
||||
|
||||
$gems-color: #24CC8F;
|
||||
$gold-color: #FFA623;
|
||||
$hourglass-color: #2995CD;
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.icon-24 {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
|
||||
.icon-10 {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
|
||||
@@ -36,17 +36,3 @@
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.popover-content-attr {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&-key {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&-val {
|
||||
color: $green-10;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,3 +68,8 @@ h4 {
|
||||
font-size: 14px;
|
||||
line-height: 1.43;
|
||||
}
|
||||
|
||||
.textCondensed {
|
||||
font-family: 'Roboto Condensed', sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
169
website/client/components/inventory/equipment/attributesGrid.vue
Normal file
169
website/client/components/inventory/equipment/attributesGrid.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template lang="pug">
|
||||
div.attributes-group
|
||||
.popover-content-attr(v-for="attr in ATTRIBUTES", :key="attr")
|
||||
.group-content
|
||||
span.popover-content-attr-cell.key(:class="{'hasValue': hasSumValue(attr) }") {{ `${$t(attr)}: ` }}
|
||||
span.popover-content-attr-cell.label.value(:class="{'green': hasSumValue(attr) }") {{ `${stats.sum[attr]}` }}
|
||||
span.popover-content-attr-cell.label.bold(:class="{'hasValue': hasGearValue(attr) }") {{ $t('gear') }}:
|
||||
span.popover-content-attr-cell.label(:class="{'hasValue': hasGearValue(attr) }") {{ stats.gear[attr] }}
|
||||
span.popover-content-attr-cell.label.bold(:class="{'hasValue': hasClassBonus(attr) }") {{ $t('classEquipBonus') }}:
|
||||
span.popover-content-attr-cell.label(:class="{'hasValue': hasClassBonus(attr) }") {{ `${stats.classBonus[attr]}` }}
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
|
||||
.attributes-group {
|
||||
border-radius: 4px;
|
||||
// unless we have a way to give a popover an id or class, it needs expand the attributes area
|
||||
margin: -12px -16px;
|
||||
display:flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.popover-content-attr {
|
||||
font-weight: bold;
|
||||
width: calc(50% - 1px);
|
||||
background-color: $gray-50;
|
||||
|
||||
&:nth-of-type(even) {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
&:nth-child(1), &:nth-child(2) {
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.group-content {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.popover-content-attr-cell {
|
||||
width: 70%;
|
||||
text-align: left;
|
||||
|
||||
&:nth-of-type(even) {
|
||||
text-align: right;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&.key {
|
||||
color: $white;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
&.label {
|
||||
font-size: 10px;
|
||||
line-height: 1.2;
|
||||
color: $gray-300;
|
||||
}
|
||||
|
||||
&.label.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&.label.value {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
text-align: right;
|
||||
|
||||
&.green {
|
||||
color: $green-10;
|
||||
|
||||
&:before {
|
||||
content: '+';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
|
||||
.group-content {
|
||||
padding: 8px 17px;
|
||||
}
|
||||
|
||||
.popover-content-attr {
|
||||
background-color: #f4f4f4;
|
||||
|
||||
&:nth-of-type(even) {
|
||||
margin-left: 1px;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.popover-content-attr-cell {
|
||||
&.key {
|
||||
color: $gray-400;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 1.25;
|
||||
|
||||
&.hasValue {
|
||||
color: $gray-50;
|
||||
}
|
||||
}
|
||||
|
||||
&.label {
|
||||
color: $gray-400;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
|
||||
&.hasValue {
|
||||
color: $gray-200;
|
||||
}
|
||||
}
|
||||
|
||||
&.label.value {
|
||||
|
||||
&.green {
|
||||
color: $green-10;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import statsMixin from 'client/mixins/stats';
|
||||
|
||||
export default {
|
||||
mixins: [statsMixin],
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||
user: 'user.data',
|
||||
flatGear: 'content.gear.flat',
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
hasSumValue (attr) {
|
||||
return this.stats.sum[attr] > 0;
|
||||
},
|
||||
hasGearValue (attr) {
|
||||
return this.stats.gear[attr] > 0;
|
||||
},
|
||||
hasClassBonus (attr) {
|
||||
return this.stats.classBonus[attr] > 0;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -8,15 +8,25 @@ div
|
||||
div(v-else)
|
||||
h4.popover-content-title {{ itemText }}
|
||||
.popover-content-text {{ itemNotes }}
|
||||
.popover-content-attr(v-for="attr in ATTRIBUTES", :key="attr")
|
||||
span.popover-content-attr-key {{ `${$t(attr)}: ` }}
|
||||
span.popover-content-attr-val {{ `+${item[attr]}` }}
|
||||
attributesGrid(:item="item")
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.popover-content-text {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
import attributesGrid from './attributesGrid';
|
||||
import statsMixin from 'client/mixins/stats';
|
||||
|
||||
export default {
|
||||
mixins: [statsMixin],
|
||||
components: {
|
||||
attributesGrid,
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
@@ -25,6 +35,7 @@ div
|
||||
computed: {
|
||||
...mapState({
|
||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||
user: 'user.data',
|
||||
}),
|
||||
itemText () {
|
||||
if (this.item.text instanceof Function) {
|
||||
|
||||
@@ -23,7 +23,11 @@
|
||||
h4.title {{ itemText }}
|
||||
div.text(v-html="itemNotes")
|
||||
|
||||
equipmentAttributesGrid.bordered(
|
||||
span.classTag(v-if="showClassTag")
|
||||
span.svg-icon.inline.icon-24(v-html="icons[itemClass]")
|
||||
span.className.textCondensed(:class="itemClass") {{ getClassName(itemClass) }}
|
||||
|
||||
attributesGrid.attributesGrid(
|
||||
:item="item",
|
||||
v-if="attributesGridVisible"
|
||||
)
|
||||
@@ -58,11 +62,41 @@
|
||||
width: 282px;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-radius: 2px;
|
||||
background-color: #f9f9f9;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px 24px 10px;
|
||||
.classTag {
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.className {
|
||||
height: 24px;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.healer {
|
||||
color: $healer-color;
|
||||
}
|
||||
|
||||
.rogue {
|
||||
color: $rogue-color;
|
||||
}
|
||||
|
||||
.warrior {
|
||||
color: $warrior-color;
|
||||
}
|
||||
|
||||
.wizard {
|
||||
color: $wizard-color;
|
||||
}
|
||||
|
||||
.attributesGrid {
|
||||
background-color: $gray-500;
|
||||
|
||||
margin: 10px 0 24px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
@@ -74,15 +108,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.43;
|
||||
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
button.btn.btn-primary {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
@@ -94,19 +119,27 @@
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import svgClose from 'assets/svg/close.svg';
|
||||
import svgWarrior from 'assets/svg/warrior.svg';
|
||||
import svgWizard from 'assets/svg/wizard.svg';
|
||||
import svgRogue from 'assets/svg/rogue.svg';
|
||||
import svgHealer from 'assets/svg/healer.svg';
|
||||
|
||||
import Avatar from 'client/components/avatar';
|
||||
import EquipmentAttributesGrid from 'client/components/shops/market/equipmentAttributesGrid.vue';
|
||||
import attributesGrid from 'client/components/inventory/equipment/attributesGrid.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Avatar,
|
||||
EquipmentAttributesGrid,
|
||||
attributesGrid,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: svgClose,
|
||||
warrior: svgWarrior,
|
||||
wizard: svgWizard,
|
||||
rogue: svgRogue,
|
||||
healer: svgHealer,
|
||||
}),
|
||||
};
|
||||
},
|
||||
@@ -115,6 +148,9 @@
|
||||
content: 'content',
|
||||
user: 'user.data',
|
||||
}),
|
||||
showClassTag () {
|
||||
return this.content.classes.includes(this.itemClass);
|
||||
},
|
||||
itemText () {
|
||||
if (this.item.text instanceof Function) {
|
||||
return this.item.text();
|
||||
@@ -129,6 +165,9 @@
|
||||
return this.item.notes;
|
||||
}
|
||||
},
|
||||
itemClass () {
|
||||
return this.item.klass || this.item.specialClass;
|
||||
},
|
||||
attributesGridVisible () {
|
||||
if (this.costumeMode) {
|
||||
return false;
|
||||
@@ -153,6 +192,13 @@
|
||||
[gear.type]: gear.key,
|
||||
};
|
||||
},
|
||||
getClassName (classType) {
|
||||
if (classType === 'wizard') {
|
||||
return this.$t('mage');
|
||||
} else {
|
||||
return this.$t(classType);
|
||||
}
|
||||
},
|
||||
},
|
||||
props: {
|
||||
item: {
|
||||
|
||||
@@ -242,7 +242,19 @@ export default {
|
||||
});
|
||||
},
|
||||
sortItems (items, sortBy) {
|
||||
return sortBy === 'sortByName' ? _sortBy(items, sortGearTypeMap[sortBy]) : _reverse(_sortBy(items, sortGearTypeMap[sortBy]));
|
||||
let userClass = this.user.stats.class;
|
||||
|
||||
return sortBy === 'sortByName' ?
|
||||
_sortBy(items, sortGearTypeMap[sortBy]) :
|
||||
_reverse(_sortBy(items, (item) => {
|
||||
let attrToSort = sortGearTypeMap[sortBy];
|
||||
let attrValue = item[attrToSort];
|
||||
if (item.klass === userClass || item.specialClass === userClass) {
|
||||
attrValue *= 1.5;
|
||||
}
|
||||
|
||||
return attrValue;
|
||||
}));
|
||||
},
|
||||
drawerToggled (newState) {
|
||||
this.$store.state.equipmentDrawerOpen = newState;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
div.text(v-html="itemNotes")
|
||||
|
||||
slot(name="additionalInfo", :item="item")
|
||||
equipmentAttributesGrid.bordered(
|
||||
equipmentAttributesGrid.attributesGrid(
|
||||
v-if="showAttributesGrid",
|
||||
:item="item"
|
||||
)
|
||||
@@ -50,7 +50,7 @@
|
||||
input(type='number', min='0', v-model='selectedAmountToBuy')
|
||||
span(:class="{'notEnough': notEnoughCurrency}")
|
||||
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
|
||||
span.value(:class="getPriceClass()") {{ item.value }}
|
||||
span.cost(:class="getPriceClass()") {{ item.value }}
|
||||
|
||||
.gems-left(v-if='item.key === "gem"')
|
||||
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
|
||||
@@ -125,7 +125,7 @@
|
||||
width: 74px;
|
||||
height: 40px;
|
||||
border-radius: 2px;
|
||||
background-color: #ffffff;
|
||||
background-color: $white;
|
||||
box-shadow: 0 2px 2px 0 rgba(26, 24, 29, 0.16), 0 1px 4px 0 rgba(26, 24, 29, 0.12);
|
||||
margin-right: 24px;
|
||||
|
||||
@@ -144,15 +144,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.43;
|
||||
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
span.svg-icon.inline.icon-32 {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
@@ -162,10 +153,9 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.value {
|
||||
.cost {
|
||||
width: 28px;
|
||||
height: 32px;
|
||||
font-family: Roboto;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
line-height: 1.33;
|
||||
@@ -173,15 +163,15 @@
|
||||
vertical-align: middle;
|
||||
|
||||
&.gems {
|
||||
color: $green-10;
|
||||
color: $gems-color;
|
||||
}
|
||||
|
||||
&.gold {
|
||||
color: $yellow-10
|
||||
color: $gold-color;
|
||||
}
|
||||
|
||||
&.hourglasses {
|
||||
color: $blue-10;
|
||||
color: $hourglass-color;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +219,7 @@
|
||||
|
||||
.limitedTime {
|
||||
height: 32px;
|
||||
background-color: #6133b4;
|
||||
background-color: $purple-300;
|
||||
width: calc(100% + 30px);
|
||||
margin: 0 -15px; // the modal content has its own padding
|
||||
|
||||
@@ -248,8 +238,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bordered {
|
||||
.attributesGrid {
|
||||
margin-top: 8px;
|
||||
border-radius: 2px;
|
||||
background-color: $gray-500;
|
||||
|
||||
margin: 10px 0 24px;
|
||||
}
|
||||
|
||||
.gems-left {
|
||||
@@ -277,7 +271,7 @@
|
||||
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
import EquipmentAttributesGrid from './market/equipmentAttributesGrid.vue';
|
||||
import EquipmentAttributesGrid from '../inventory/equipment/attributesGrid.vue';
|
||||
|
||||
import Item from 'client/components/inventory/item';
|
||||
import Avatar from 'client/components/avatar';
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
.attribute-entry(v-for="attr in ATTRIBUTES", :key="attr")
|
||||
span.key(:class="{'no-value': item[attr] == '0'}") {{ `${$t(attr)}: ` }}
|
||||
span.val(:class="{'no-value': item[attr] == '0'}") {{ `+${item[attr]}` }}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.attribute-entry {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
span{
|
||||
width: 38px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
line-height: 1.0;
|
||||
}
|
||||
|
||||
.no-value {
|
||||
color: $gray-400 !important;
|
||||
}
|
||||
|
||||
.key {
|
||||
color: $gray-100;
|
||||
}
|
||||
|
||||
.val {
|
||||
color: $green-10;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'client/libs/store';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
ATTRIBUTES: 'constants.ATTRIBUTES',
|
||||
}),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -255,13 +255,6 @@
|
||||
margin: 24px auto;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-radius: 2px;
|
||||
background-color: #f9f9f9;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px 24px 10px;
|
||||
}
|
||||
|
||||
.item-wrapper.bordered-item .item {
|
||||
width: 112px;
|
||||
height: 112px;
|
||||
@@ -359,7 +352,7 @@
|
||||
import Avatar from 'client/components/avatar';
|
||||
|
||||
import SellModal from './sellModal.vue';
|
||||
import EquipmentAttributesGrid from './equipmentAttributesGrid.vue';
|
||||
import EquipmentAttributesGrid from '../../inventory/equipment/attributesGrid.vue';
|
||||
import SelectMembersModal from 'client/components/selectMembersModal.vue';
|
||||
|
||||
import svgPin from 'assets/svg/pin.svg';
|
||||
|
||||
@@ -63,15 +63,6 @@
|
||||
width: 282px;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.43;
|
||||
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
span.svg-icon.inline.icon-32 {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
|
||||
@@ -81,14 +81,6 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
line-height: 1.43;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.right-sidebar {
|
||||
position: absolute;
|
||||
right: -350px;
|
||||
|
||||
@@ -241,13 +241,6 @@
|
||||
margin: 24px auto;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-radius: 2px;
|
||||
background-color: #f9f9f9;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px 24px 10px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: inline-block;
|
||||
width: 33%;
|
||||
|
||||
@@ -149,13 +149,6 @@
|
||||
margin: 24px auto;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-radius: 2px;
|
||||
background-color: #f9f9f9;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px 24px 10px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
|
||||
@@ -129,13 +129,6 @@
|
||||
margin: 24px auto;
|
||||
}
|
||||
|
||||
.bordered {
|
||||
border-radius: 2px;
|
||||
background-color: #f9f9f9;
|
||||
margin-bottom: 24px;
|
||||
padding: 24px 24px 10px;
|
||||
}
|
||||
|
||||
.group {
|
||||
display: inline-block;
|
||||
width: 33%;
|
||||
|
||||
@@ -123,39 +123,26 @@ div
|
||||
.col-12.col-md-6
|
||||
h2.text-center {{$t('equipment')}}
|
||||
.well
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.eyewear && equippedItems.eyewear.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.eyewear}`")
|
||||
h3 {{$t('eyewear')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.head && equippedItems.head.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.head}`")
|
||||
h3 {{$t('headgearCapitalized')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.headAccessory && equippedItems.headAccessory.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.headAccessory}`")
|
||||
h3 {{$t('headAccess')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.back && equippedItems.back.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.back}`")
|
||||
h3 {{$t('backAccess')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.armor && equippedItems.armor.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.armor}`")
|
||||
h3 {{$t('armorCapitalized')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.body && equippedItems.body.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.body}`")
|
||||
h3 {{$t('bodyAccess')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.weapon && equippedItems.weapon.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.weapon}`")
|
||||
h3 {{$t('mainHand')}}
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.col-12.col-md-4.item-wrapper
|
||||
.box(:class='{white: equippedItems.shield && equippedItems.shield.indexOf("base_0") === -1}')
|
||||
div(:class="`shop_${equippedItems.shield}`")
|
||||
h3 {{$t('offHand')}}
|
||||
.col-12.col-md-4.item-wrapper(v-for="(label, key) in equipTypes")
|
||||
.box(
|
||||
:id="key",
|
||||
v-if="label !== 'skip'",
|
||||
:class='{white: equippedItems[key] && equippedItems[key].indexOf("base_0") === -1}'
|
||||
)
|
||||
div(:class="`shop_${equippedItems[key]}`")
|
||||
b-popover(
|
||||
v-if="label !== 'skip' && equippedItems[key] && equippedItems[key].indexOf(\"base_0\") === -1",
|
||||
:target="key",
|
||||
triggers="hover",
|
||||
:placement="'right'",
|
||||
:preventOverflow="false",
|
||||
)
|
||||
h4.gearTitle {{ getGearTitle(equippedItems[key]) }}
|
||||
attributesGrid.attributesGrid(
|
||||
:item="content.gear.flat[equippedItems[key]]",
|
||||
)
|
||||
|
||||
h3(v-if="label !== 'skip'") {{ label }}
|
||||
.col-12.col-md-6
|
||||
h2.text-center {{$t('costume')}}
|
||||
.well
|
||||
@@ -292,6 +279,11 @@ div
|
||||
.modal-content {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.gearTitle {
|
||||
color: white;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-icon svg {
|
||||
@@ -595,6 +587,7 @@ import achievementsLib from '../../../common/script/libs/achievements';
|
||||
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
|
||||
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
|
||||
import Content from '../../../common/script/content';
|
||||
import attributesGrid from 'client/components/inventory/equipment/attributesGrid';
|
||||
const DROP_ANIMALS = keys(Content.pets);
|
||||
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
|
||||
|
||||
@@ -612,6 +605,7 @@ export default {
|
||||
sendGemsModal,
|
||||
MemberDetails,
|
||||
toggleSwitch,
|
||||
attributesGrid,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -636,6 +630,17 @@ export default {
|
||||
selectedPage: 'profile',
|
||||
achievements: {},
|
||||
content: Content,
|
||||
equipTypes: {
|
||||
eyewear: this.$t('eyewear'),
|
||||
head: this.$t('headgearCapitalized'),
|
||||
headAccessory: this.$t('headAccess'),
|
||||
back: this.$t('backAccess'),
|
||||
armor: this.$t('armorCapitalized'),
|
||||
body: this.$t('bodyAccess'),
|
||||
weapon: this.$t('mainHand'),
|
||||
_skip: 'skip',
|
||||
shield: this.$t('offHand'),
|
||||
},
|
||||
stats: {
|
||||
str: {
|
||||
title: 'strength',
|
||||
@@ -755,6 +760,9 @@ export default {
|
||||
userName: this.user.profile.name,
|
||||
});
|
||||
},
|
||||
getGearTitle (key) {
|
||||
return this.flatGear[key].text();
|
||||
},
|
||||
getProgressDisplay () {
|
||||
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
|
||||
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');
|
||||
|
||||
69
website/client/mixins/stats.js
Normal file
69
website/client/mixins/stats.js
Normal file
@@ -0,0 +1,69 @@
|
||||
let emptyStats = () => {
|
||||
return {
|
||||
str: 0,
|
||||
int: 0,
|
||||
per: 0,
|
||||
con: 0,
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
stats () {
|
||||
let gearStats = this.getItemStats(this.item);
|
||||
let classBonus = this.getClassBonus(this.item);
|
||||
|
||||
let sumNewGearStats = this.calculateStats(gearStats, classBonus, (a1, a2) => {
|
||||
return a1 + a2;
|
||||
});
|
||||
|
||||
return {
|
||||
gear: gearStats,
|
||||
classBonus,
|
||||
sum: sumNewGearStats,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getItemStats (item) {
|
||||
let result = emptyStats();
|
||||
|
||||
if (!item) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (let attr of this.ATTRIBUTES) {
|
||||
result[attr] = Number(item[attr]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
getClassBonus (item) {
|
||||
let result = emptyStats();
|
||||
|
||||
if (!item) {
|
||||
return result;
|
||||
}
|
||||
|
||||
let itemStats = this.getItemStats(item);
|
||||
|
||||
let userClass = this.user.stats.class;
|
||||
if (userClass === item.klass || userClass === item.specialClass) {
|
||||
for (let attr of this.ATTRIBUTES) {
|
||||
result[attr] = itemStats[attr] * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
calculateStats (srcStats, otherStats, func) {
|
||||
let result = emptyStats();
|
||||
|
||||
for (let attr of this.ATTRIBUTES) {
|
||||
result[attr] = func(srcStats[attr], otherStats[attr]);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -64,6 +64,7 @@
|
||||
"classBonusText": "Your class (Warrior, if you haven't unlocked or selected another class) uses its own equipment more effectively than gear from other classes. Equipped gear from your current class gets a 50% boost to the Stat bonus it grants.",
|
||||
"classEquipBonus": "Class Bonus",
|
||||
"battleGear": "Battle Gear",
|
||||
"gear": "Gear",
|
||||
"battleGearText": "This is the gear you wear into battle; it affects numbers when interacting with your tasks.",
|
||||
"autoEquipBattleGear": "Auto-equip new gear",
|
||||
"costume": "Costume",
|
||||
|
||||
Reference in New Issue
Block a user