diff --git a/website/client/assets/scss/colors.scss b/website/client/assets/scss/colors.scss
index cc653f33d9..6e54888ef7 100644
--- a/website/client/assets/scss/colors.scss
+++ b/website/client/assets/scss/colors.scss
@@ -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;
diff --git a/website/client/assets/scss/icon.scss b/website/client/assets/scss/icon.scss
index 78a9b8b8a2..bdb2fa4345 100644
--- a/website/client/assets/scss/icon.scss
+++ b/website/client/assets/scss/icon.scss
@@ -28,6 +28,12 @@
height: 12px;
}
+.icon-24 {
+ width: 24px;
+ height: 24px;
+}
+
+
.icon-10 {
width: 10px;
height: 10px;
diff --git a/website/client/assets/scss/popover.scss b/website/client/assets/scss/popover.scss
index b15566fdab..8e165ec10b 100644
--- a/website/client/assets/scss/popover.scss
+++ b/website/client/assets/scss/popover.scss
@@ -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;
- }
-}
diff --git a/website/client/assets/scss/typography.scss b/website/client/assets/scss/typography.scss
index 4076d98594..9dceb3ed35 100644
--- a/website/client/assets/scss/typography.scss
+++ b/website/client/assets/scss/typography.scss
@@ -68,3 +68,8 @@ h4 {
font-size: 14px;
line-height: 1.43;
}
+
+.textCondensed {
+ font-family: 'Roboto Condensed', sans-serif;
+ font-weight: bold;
+}
diff --git a/website/client/components/inventory/equipment/attributesGrid.vue b/website/client/components/inventory/equipment/attributesGrid.vue
new file mode 100644
index 0000000000..6cbacdd3a0
--- /dev/null
+++ b/website/client/components/inventory/equipment/attributesGrid.vue
@@ -0,0 +1,169 @@
+
+ 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]}` }}
+
+
+
+
+
diff --git a/website/client/components/inventory/equipment/attributesPopover.vue b/website/client/components/inventory/equipment/attributesPopover.vue
index 2b3adc8ba2..731d649f34 100644
--- a/website/client/components/inventory/equipment/attributesPopover.vue
+++ b/website/client/components/inventory/equipment/attributesPopover.vue
@@ -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")
+
+
diff --git a/website/client/components/shops/market/index.vue b/website/client/components/shops/market/index.vue
index 6fb5b45190..8249f0ee73 100644
--- a/website/client/components/shops/market/index.vue
+++ b/website/client/components/shops/market/index.vue
@@ -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';
diff --git a/website/client/components/shops/market/sellModal.vue b/website/client/components/shops/market/sellModal.vue
index 8eee123848..cfeaef7b48 100644
--- a/website/client/components/shops/market/sellModal.vue
+++ b/website/client/components/shops/market/sellModal.vue
@@ -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;
diff --git a/website/client/components/shops/quests/buyQuestModal.vue b/website/client/components/shops/quests/buyQuestModal.vue
index 42dd2905e7..483ee77717 100644
--- a/website/client/components/shops/quests/buyQuestModal.vue
+++ b/website/client/components/shops/quests/buyQuestModal.vue
@@ -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;
diff --git a/website/client/components/shops/quests/index.vue b/website/client/components/shops/quests/index.vue
index 0f499129d0..9f52c66a44 100644
--- a/website/client/components/shops/quests/index.vue
+++ b/website/client/components/shops/quests/index.vue
@@ -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%;
diff --git a/website/client/components/shops/seasonal/index.vue b/website/client/components/shops/seasonal/index.vue
index 1c30460371..80a95dcd53 100644
--- a/website/client/components/shops/seasonal/index.vue
+++ b/website/client/components/shops/seasonal/index.vue
@@ -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%;
diff --git a/website/client/components/shops/timeTravelers/index.vue b/website/client/components/shops/timeTravelers/index.vue
index e3ff050662..f0b24f7c78 100644
--- a/website/client/components/shops/timeTravelers/index.vue
+++ b/website/client/components/shops/timeTravelers/index.vue
@@ -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%;
diff --git a/website/client/components/userMenu/profile.vue b/website/client/components/userMenu/profile.vue
index 204d7e511e..1e7a2c9a39 100644
--- a/website/client/components/userMenu/profile.vue
+++ b/website/client/components/userMenu/profile.vue
@@ -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');
diff --git a/website/client/mixins/stats.js b/website/client/mixins/stats.js
new file mode 100644
index 0000000000..89131e712a
--- /dev/null
+++ b/website/client/mixins/stats.js
@@ -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;
+ },
+ },
+};
diff --git a/website/common/locales/en/character.json b/website/common/locales/en/character.json
index 51a7013b30..162fa6e701 100644
--- a/website/common/locales/en/character.json
+++ b/website/common/locales/en/character.json
@@ -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",