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:
negue
2018-02-09 23:47:38 +01:00
committed by Sabe Jones
parent 6c677be9d2
commit 723b4f3451
19 changed files with 405 additions and 189 deletions

View File

@@ -61,3 +61,12 @@ $green-100: #5AEAB2;
$green-500: #A6FFDF; $green-500: #A6FFDF;
$suggested-item-color: #D5C8FF; $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;

View File

@@ -28,6 +28,12 @@
height: 12px; height: 12px;
} }
.icon-24 {
width: 24px;
height: 24px;
}
.icon-10 { .icon-10 {
width: 10px; width: 10px;
height: 10px; height: 10px;

View File

@@ -36,17 +36,3 @@
margin-bottom: 0px; margin-bottom: 0px;
} }
.popover-content-attr {
width: 50%;
display: inline-block;
font-weight: bold;
margin-bottom: 4px;
&-key {
color: $white;
}
&-val {
color: $green-10;
}
}

View File

@@ -68,3 +68,8 @@ h4 {
font-size: 14px; font-size: 14px;
line-height: 1.43; line-height: 1.43;
} }
.textCondensed {
font-family: 'Roboto Condensed', sans-serif;
font-weight: bold;
}

View 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>

View File

@@ -8,15 +8,25 @@ div
div(v-else) div(v-else)
h4.popover-content-title {{ itemText }} h4.popover-content-title {{ itemText }}
.popover-content-text {{ itemNotes }} .popover-content-text {{ itemNotes }}
.popover-content-attr(v-for="attr in ATTRIBUTES", :key="attr") attributesGrid(:item="item")
span.popover-content-attr-key {{ `${$t(attr)}: ` }}
span.popover-content-attr-val {{ `+${item[attr]}` }}
</template> </template>
<style scoped>
.popover-content-text {
margin-bottom: 25px;
}
</style>
<script> <script>
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import attributesGrid from './attributesGrid';
import statsMixin from 'client/mixins/stats';
export default { export default {
mixins: [statsMixin],
components: {
attributesGrid,
},
props: { props: {
item: { item: {
type: Object, type: Object,
@@ -25,6 +35,7 @@ div
computed: { computed: {
...mapState({ ...mapState({
ATTRIBUTES: 'constants.ATTRIBUTES', ATTRIBUTES: 'constants.ATTRIBUTES',
user: 'user.data',
}), }),
itemText () { itemText () {
if (this.item.text instanceof Function) { if (this.item.text instanceof Function) {

View File

@@ -23,7 +23,11 @@
h4.title {{ itemText }} h4.title {{ itemText }}
div.text(v-html="itemNotes") 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", :item="item",
v-if="attributesGridVisible" v-if="attributesGridVisible"
) )
@@ -58,11 +62,41 @@
width: 282px; width: 282px;
} }
.bordered { .classTag {
border-radius: 2px; height: 24px;
background-color: #f9f9f9; display: flex;
margin-bottom: 24px; align-items: center;
padding: 24px 24px 10px; 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 { .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 { button.btn.btn-primary {
margin-top: 24px; margin-top: 24px;
margin-bottom: 24px; margin-bottom: 24px;
@@ -94,19 +119,27 @@
import { mapState } from 'client/libs/store'; import { mapState } from 'client/libs/store';
import svgClose from 'assets/svg/close.svg'; 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 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 { export default {
components: { components: {
Avatar, Avatar,
EquipmentAttributesGrid, attributesGrid,
}, },
data () { data () {
return { return {
icons: Object.freeze({ icons: Object.freeze({
close: svgClose, close: svgClose,
warrior: svgWarrior,
wizard: svgWizard,
rogue: svgRogue,
healer: svgHealer,
}), }),
}; };
}, },
@@ -115,6 +148,9 @@
content: 'content', content: 'content',
user: 'user.data', user: 'user.data',
}), }),
showClassTag () {
return this.content.classes.includes(this.itemClass);
},
itemText () { itemText () {
if (this.item.text instanceof Function) { if (this.item.text instanceof Function) {
return this.item.text(); return this.item.text();
@@ -129,6 +165,9 @@
return this.item.notes; return this.item.notes;
} }
}, },
itemClass () {
return this.item.klass || this.item.specialClass;
},
attributesGridVisible () { attributesGridVisible () {
if (this.costumeMode) { if (this.costumeMode) {
return false; return false;
@@ -153,6 +192,13 @@
[gear.type]: gear.key, [gear.type]: gear.key,
}; };
}, },
getClassName (classType) {
if (classType === 'wizard') {
return this.$t('mage');
} else {
return this.$t(classType);
}
},
}, },
props: { props: {
item: { item: {

View File

@@ -242,7 +242,19 @@ export default {
}); });
}, },
sortItems (items, sortBy) { 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) { drawerToggled (newState) {
this.$store.state.equipmentDrawerOpen = newState; this.$store.state.equipmentDrawerOpen = newState;

View File

@@ -37,7 +37,7 @@
div.text(v-html="itemNotes") div.text(v-html="itemNotes")
slot(name="additionalInfo", :item="item") slot(name="additionalInfo", :item="item")
equipmentAttributesGrid.bordered( equipmentAttributesGrid.attributesGrid(
v-if="showAttributesGrid", v-if="showAttributesGrid",
:item="item" :item="item"
) )
@@ -50,7 +50,7 @@
input(type='number', min='0', v-model='selectedAmountToBuy') input(type='number', min='0', v-model='selectedAmountToBuy')
span(:class="{'notEnough': notEnoughCurrency}") span(:class="{'notEnough': notEnoughCurrency}")
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]") 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"') .gems-left(v-if='item.key === "gem"')
strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }} strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }}
@@ -125,7 +125,7 @@
width: 74px; width: 74px;
height: 40px; height: 40px;
border-radius: 2px; 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); 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; 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 { span.svg-icon.inline.icon-32 {
height: 32px; height: 32px;
width: 32px; width: 32px;
@@ -162,10 +153,9 @@
vertical-align: middle; vertical-align: middle;
} }
.value { .cost {
width: 28px; width: 28px;
height: 32px; height: 32px;
font-family: Roboto;
font-size: 24px; font-size: 24px;
font-weight: bold; font-weight: bold;
line-height: 1.33; line-height: 1.33;
@@ -173,15 +163,15 @@
vertical-align: middle; vertical-align: middle;
&.gems { &.gems {
color: $green-10; color: $gems-color;
} }
&.gold { &.gold {
color: $yellow-10 color: $gold-color;
} }
&.hourglasses { &.hourglasses {
color: $blue-10; color: $hourglass-color;
} }
} }
@@ -229,7 +219,7 @@
.limitedTime { .limitedTime {
height: 32px; height: 32px;
background-color: #6133b4; background-color: $purple-300;
width: calc(100% + 30px); width: calc(100% + 30px);
margin: 0 -15px; // the modal content has its own padding margin: 0 -15px; // the modal content has its own padding
@@ -248,8 +238,12 @@
} }
} }
.bordered { .attributesGrid {
margin-top: 8px; margin-top: 8px;
border-radius: 2px;
background-color: $gray-500;
margin: 10px 0 24px;
} }
.gems-left { .gems-left {
@@ -277,7 +271,7 @@
import { mapState } from 'client/libs/store'; 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 Item from 'client/components/inventory/item';
import Avatar from 'client/components/avatar'; import Avatar from 'client/components/avatar';

View File

@@ -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>

View File

@@ -255,13 +255,6 @@
margin: 24px auto; margin: 24px auto;
} }
.bordered {
border-radius: 2px;
background-color: #f9f9f9;
margin-bottom: 24px;
padding: 24px 24px 10px;
}
.item-wrapper.bordered-item .item { .item-wrapper.bordered-item .item {
width: 112px; width: 112px;
height: 112px; height: 112px;
@@ -359,7 +352,7 @@
import Avatar from 'client/components/avatar'; import Avatar from 'client/components/avatar';
import SellModal from './sellModal.vue'; 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 SelectMembersModal from 'client/components/selectMembersModal.vue';
import svgPin from 'assets/svg/pin.svg'; import svgPin from 'assets/svg/pin.svg';

View File

@@ -63,15 +63,6 @@
width: 282px; 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 { span.svg-icon.inline.icon-32 {
height: 32px; height: 32px;
width: 32px; width: 32px;

View File

@@ -81,14 +81,6 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.content-text {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-weight: normal;
line-height: 1.43;
width: 400px;
}
.right-sidebar { .right-sidebar {
position: absolute; position: absolute;
right: -350px; right: -350px;

View File

@@ -241,13 +241,6 @@
margin: 24px auto; margin: 24px auto;
} }
.bordered {
border-radius: 2px;
background-color: #f9f9f9;
margin-bottom: 24px;
padding: 24px 24px 10px;
}
.group { .group {
display: inline-block; display: inline-block;
width: 33%; width: 33%;

View File

@@ -149,13 +149,6 @@
margin: 24px auto; margin: 24px auto;
} }
.bordered {
border-radius: 2px;
background-color: #f9f9f9;
margin-bottom: 24px;
padding: 24px 24px 10px;
}
.group { .group {
display: inline-block; display: inline-block;
width: 50%; width: 50%;

View File

@@ -129,13 +129,6 @@
margin: 24px auto; margin: 24px auto;
} }
.bordered {
border-radius: 2px;
background-color: #f9f9f9;
margin-bottom: 24px;
padding: 24px 24px 10px;
}
.group { .group {
display: inline-block; display: inline-block;
width: 33%; width: 33%;

View File

@@ -123,39 +123,26 @@ div
.col-12.col-md-6 .col-12.col-md-6
h2.text-center {{$t('equipment')}} h2.text-center {{$t('equipment')}}
.well .well
.col-12.col-md-4.item-wrapper .col-12.col-md-4.item-wrapper(v-for="(label, key) in equipTypes")
.box(:class='{white: equippedItems.eyewear && equippedItems.eyewear.indexOf("base_0") === -1}') .box(
div(:class="`shop_${equippedItems.eyewear}`") :id="key",
h3 {{$t('eyewear')}} v-if="label !== 'skip'",
.col-12.col-md-4.item-wrapper :class='{white: equippedItems[key] && equippedItems[key].indexOf("base_0") === -1}'
.box(:class='{white: equippedItems.head && equippedItems.head.indexOf("base_0") === -1}') )
div(:class="`shop_${equippedItems.head}`") div(:class="`shop_${equippedItems[key]}`")
h3 {{$t('headgearCapitalized')}} b-popover(
.col-12.col-md-4.item-wrapper v-if="label !== 'skip' && equippedItems[key] && equippedItems[key].indexOf(\"base_0\") === -1",
.box(:class='{white: equippedItems.headAccessory && equippedItems.headAccessory.indexOf("base_0") === -1}') :target="key",
div(:class="`shop_${equippedItems.headAccessory}`") triggers="hover",
h3 {{$t('headAccess')}} :placement="'right'",
.col-12.col-md-4.item-wrapper :preventOverflow="false",
.box(:class='{white: equippedItems.back && equippedItems.back.indexOf("base_0") === -1}') )
div(:class="`shop_${equippedItems.back}`") h4.gearTitle {{ getGearTitle(equippedItems[key]) }}
h3 {{$t('backAccess')}} attributesGrid.attributesGrid(
.col-12.col-md-4.item-wrapper :item="content.gear.flat[equippedItems[key]]",
.box(:class='{white: equippedItems.armor && equippedItems.armor.indexOf("base_0") === -1}') )
div(:class="`shop_${equippedItems.armor}`")
h3 {{$t('armorCapitalized')}} h3(v-if="label !== 'skip'") {{ label }}
.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-6 .col-12.col-md-6
h2.text-center {{$t('costume')}} h2.text-center {{$t('costume')}}
.well .well
@@ -292,6 +279,11 @@ div
.modal-content { .modal-content {
background: #f9f9f9; background: #f9f9f9;
} }
.gearTitle {
color: white;
margin-bottom: 20px;
}
} }
.message-icon svg { .message-icon svg {
@@ -595,6 +587,7 @@ import achievementsLib from '../../../common/script/libs/achievements';
// @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL // @TODO: EMAILS.COMMUNITY_MANAGER_EMAIL
const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com'; const COMMUNITY_MANAGER_EMAIL = 'admin@habitica.com';
import Content from '../../../common/script/content'; import Content from '../../../common/script/content';
import attributesGrid from 'client/components/inventory/equipment/attributesGrid';
const DROP_ANIMALS = keys(Content.pets); const DROP_ANIMALS = keys(Content.pets);
const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length; const TOTAL_NUMBER_OF_DROP_ANIMALS = DROP_ANIMALS.length;
@@ -612,6 +605,7 @@ export default {
sendGemsModal, sendGemsModal,
MemberDetails, MemberDetails,
toggleSwitch, toggleSwitch,
attributesGrid,
}, },
data () { data () {
return { return {
@@ -636,6 +630,17 @@ export default {
selectedPage: 'profile', selectedPage: 'profile',
achievements: {}, achievements: {},
content: Content, 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: { stats: {
str: { str: {
title: 'strength', title: 'strength',
@@ -755,6 +760,9 @@ export default {
userName: this.user.profile.name, userName: this.user.profile.name,
}); });
}, },
getGearTitle (key) {
return this.flatGear[key].text();
},
getProgressDisplay () { getProgressDisplay () {
// let currentLoginDay = Content.loginIncentives[this.user.loginIncentives]; // let currentLoginDay = Content.loginIncentives[this.user.loginIncentives];
// if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage'); // if (!currentLoginDay) return this.$t('checkinReceivedAllRewardsMessage');

View 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;
},
},
};

View File

@@ -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.", "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", "classEquipBonus": "Class Bonus",
"battleGear": "Battle Gear", "battleGear": "Battle Gear",
"gear": "Gear",
"battleGearText": "This is the gear you wear into battle; it affects numbers when interacting with your tasks.", "battleGearText": "This is the gear you wear into battle; it affects numbers when interacting with your tasks.",
"autoEquipBattleGear": "Auto-equip new gear", "autoEquipBattleGear": "Auto-equip new gear",
"costume": "Costume", "costume": "Costume",