mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
multiplefixes quest (#8964)
* add hourglass to header * multiple quest fixes - show quest info in popover/modal * pin backgrounds * unpin gem-purchable items / change pinType of quest to quests * check if hatching potion is allowed on egg - wide hatching info * fix (perf): items - request text()/notes() only once * change items margin to 23px * list cards + open modal to choose a target + add space between market items * buy card from task-list * fix tests - unpin items on purchasing eggs / hatchingPotions
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
.items > div {
|
||||
display: inline-block;
|
||||
margin-right: 24px;
|
||||
margin-right: 23px;
|
||||
}
|
||||
|
||||
.items > div:last-of-type {
|
||||
@@ -87,3 +87,7 @@
|
||||
color: $gray-400;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.questPopover {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
.modal-dialog {
|
||||
.title {
|
||||
height: 24px;
|
||||
min-height: 24px;
|
||||
margin-top: 24px;
|
||||
font-family: 'Roboto Condensed';
|
||||
font-size: 20px;
|
||||
|
||||
3
website/client/assets/svg/difficulty-star-empty.svg
Normal file
3
website/client/assets/svg/difficulty-star-empty.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="#E1E0E3" fill-rule="evenodd" d="M10.667 10.667L16 8l-5.333-2.667L8 0 5.333 5.333 0 8l5.333 2.667L8 16z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 213 B |
6
website/client/assets/svg/difficulty-star-half.svg
Normal file
6
website/client/assets/svg/difficulty-star-half.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<path fill="#E1E0E3" d="M10.667 10.667L16 8l-5.333-2.667L8 0 5.333 5.333 0 8l5.333 2.667L8 16z"/>
|
||||
<path fill="#FFB445" d="M8 0L5.333 5.333 0 8l5.333 2.667L8 16z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 320 B |
@@ -52,6 +52,9 @@ div
|
||||
a.dropdown-item(href="https://trello.com/c/odmhIqyW/440-read-first-table-of-contents", target='_blank') {{ $t('requestAF') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Contributing_to_Habitica", target='_blank') {{ $t('contributing') }}
|
||||
a.dropdown-item(href="http://habitica.wikia.com/wiki/Habitica_Wiki", target='_blank') {{ $t('wiki') }}
|
||||
.item-with-icon(v-if="userHourglasses != 0")
|
||||
.svg-icon(v-html="icons.hourglasses")
|
||||
span {{ userHourglasses }}
|
||||
.item-with-icon
|
||||
.svg-icon(v-html="icons.gem")
|
||||
span {{userGems | roundBigNumber}}
|
||||
@@ -219,6 +222,7 @@ import { mapState, mapGetters } from 'client/libs/store';
|
||||
import gemIcon from 'assets/svg/gem.svg';
|
||||
import goldIcon from 'assets/svg/gold.svg';
|
||||
import userIcon from 'assets/svg/user.svg';
|
||||
import svgHourglasses from 'assets/svg/hourglass.svg';
|
||||
import logo from 'assets/svg/logo.svg';
|
||||
import InboxModal from './userMenu/inbox.vue';
|
||||
import notificationMenu from './notificationMenu';
|
||||
@@ -236,6 +240,7 @@ export default {
|
||||
gem: gemIcon,
|
||||
gold: goldIcon,
|
||||
user: userIcon,
|
||||
hourglasses: svgHourglasses,
|
||||
logo,
|
||||
}),
|
||||
groupPlans: [],
|
||||
@@ -245,7 +250,10 @@ export default {
|
||||
...mapGetters({
|
||||
userGems: 'user:gems',
|
||||
}),
|
||||
...mapState({user: 'user.data'}),
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
userHourglasses: 'user.data.purchased.plan.consecutive.trinkets',
|
||||
}),
|
||||
},
|
||||
mounted () {
|
||||
this.getUserGroupPlans();
|
||||
|
||||
@@ -157,7 +157,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
|
||||
.col-6.offset-3.text-center.set-title
|
||||
strong {{set.text}}
|
||||
.col-12(v-if='showPlainBackgroundBlurb(set.identifier, set.items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
|
||||
.col-4.text-center.customize-option(v-for='bg in set.items',
|
||||
.col-4.text-center.customize-option.background-button(v-for='bg in set.items',
|
||||
@click='unlock("background." + bg.key)',
|
||||
:popover-title='bg.text',
|
||||
:popover='bg.notes',
|
||||
@@ -167,6 +167,11 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
|
||||
.purchase-single(v-if='!user.purchased.background[bg.key]')
|
||||
.svg-icon.gem(v-html='icons.gem')
|
||||
span 7
|
||||
span.badge.badge-pill.badge-item.badge-svg(
|
||||
:class="{'item-selected-badge': isBackgroundPinned(bg), 'hide': !isBackgroundPinned(bg)}",
|
||||
@click.prevent.stop="togglePinned(bg)"
|
||||
)
|
||||
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
|
||||
.col-12.text-center(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
|
||||
.gem-amount
|
||||
.svg-icon.gem(v-html='icons.gem')
|
||||
@@ -488,6 +493,39 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
|
||||
vertical-align: bottom;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.badge-svg {
|
||||
left: calc((100% - 18px) / 2);
|
||||
cursor: pointer;
|
||||
color: $gray-400;
|
||||
background: $white;
|
||||
padding: 4.5px 6px;
|
||||
|
||||
&.item-selected-badge {
|
||||
background: $purple-300;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-12 {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
span.badge.badge-pill.badge-item.badge-svg:not(.item-selected-badge) {
|
||||
color: #a5a1ac;
|
||||
}
|
||||
|
||||
span.badge.badge-pill.badge-item.badge-svg.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.background-button:hover {
|
||||
span.badge.badge-pill.badge-item.badge-svg.hide {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
@@ -499,6 +537,7 @@ import avatar from './avatar';
|
||||
import { getBackgroundShopSets } from '../../common/script/libs/shops';
|
||||
import unlock from '../../common/script/ops/unlock';
|
||||
import guide from 'client/mixins/guide';
|
||||
import notifications from 'client/mixins/notifications';
|
||||
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
|
||||
@@ -509,9 +548,12 @@ import skinIcon from 'assets/svg/skin.svg';
|
||||
import hairIcon from 'assets/svg/hair.svg';
|
||||
import backgroundsIcon from 'assets/svg/backgrounds.svg';
|
||||
import gem from 'assets/svg/gem.svg';
|
||||
import pin from 'assets/svg/pin.svg';
|
||||
import _isPinned from './shops/_isPinned';
|
||||
|
||||
|
||||
export default {
|
||||
mixins: [guide],
|
||||
mixins: [guide, notifications],
|
||||
components: {
|
||||
avatar,
|
||||
bModal,
|
||||
@@ -546,6 +588,7 @@ export default {
|
||||
hairIcon,
|
||||
backgroundsIcon,
|
||||
gem,
|
||||
pin,
|
||||
}),
|
||||
modalPage: 1,
|
||||
activeTopPage: 'body',
|
||||
@@ -674,6 +717,14 @@ export default {
|
||||
if (this.user.purchased.background[bgKey]) backgroundClass = 'background-unlocked';
|
||||
return backgroundClass;
|
||||
},
|
||||
isBackgroundPinned (bg) {
|
||||
return _isPinned(this.user, bg);
|
||||
},
|
||||
togglePinned (bg) {
|
||||
if (!this.$store.dispatch('user:togglePinnedItem', {type: bg.pinType, path: bg.path})) {
|
||||
this.text(this.$t('unpinnedItem', {item: bg.text}));
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
:item="context.item",
|
||||
:key="context.item.key",
|
||||
:itemContentClass="`${group.classPrefix}${context.item.key}`",
|
||||
:highlightBorder="currentDraggingPotion != null",
|
||||
:highlightBorder="isHatchable(currentDraggingPotion, context.item.key)",
|
||||
v-drag.drop.hatch="context.item.key",
|
||||
|
||||
@itemDragOver="onDragOver($event, context.item)",
|
||||
@@ -58,8 +58,8 @@
|
||||
@click="onEggClicked($event, context.item)",
|
||||
)
|
||||
template(slot="popoverContent", scope="context")
|
||||
h4.popover-content-title {{ context.item.text() }}
|
||||
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes() }}
|
||||
h4.popover-content-title {{ context.item.text }}
|
||||
.popover-content-text(v-if="currentDraggingPotion == null") {{ context.item.notes }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
@click="onPotionClicked($event, context.item)"
|
||||
)
|
||||
template(slot="popoverContent", scope="context")
|
||||
h4.popover-content-title {{ context.item.text() }}
|
||||
.popover-content-text {{ context.item.notes() }}
|
||||
h4.popover-content-title {{ context.item.text }}
|
||||
.popover-content-text {{ context.item.notes }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
|
||||
|
||||
@@ -107,8 +107,8 @@
|
||||
:showPopover="currentDraggingPotion == null"
|
||||
)
|
||||
template(slot="popoverContent", scope="context")
|
||||
h4.popover-content-title {{ context.item.text() }}
|
||||
.popover-content-text {{ context.item.notes() }}
|
||||
h4.popover-content-title {{ context.item.text }}
|
||||
.popover-content-text {{ context.item.notes }}
|
||||
template(slot="itemBadge", scope="context")
|
||||
span.badge.badge-pill.badge-item.badge-quantity {{ context.item.quantity }}
|
||||
|
||||
@@ -121,13 +121,13 @@
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('dragThisPotion', {potionName: currentDraggingPotion.text() }) }}
|
||||
div.popover-content {{ $t('dragThisPotion', {potionName: currentDraggingPotion.text }) }}
|
||||
|
||||
div.hatchingPotionInfo.mouse(ref="clickPotionInfo", v-if="potionClickMode")
|
||||
div(v-if="currentDraggingPotion != null")
|
||||
div.potion-icon(:class="'Pet_HatchingPotion_'+currentDraggingPotion.key")
|
||||
div.popover
|
||||
div.popover-content {{ $t('clickOnEggToHatch', {potionName: currentDraggingPotion.text() }) }}
|
||||
div.popover-content {{ $t('clickOnEggToHatch', {potionName: currentDraggingPotion.text }) }}
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -143,12 +143,12 @@
|
||||
}
|
||||
|
||||
.potion-icon {
|
||||
margin: 0 auto;
|
||||
margin: 0 auto 8px;
|
||||
}
|
||||
|
||||
.popover {
|
||||
position: inherit;
|
||||
width: 100px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -243,6 +243,8 @@ export default {
|
||||
if (isSearched) {
|
||||
itemsArray.push({
|
||||
...item,
|
||||
text: item.text(),
|
||||
notes: item.notes(),
|
||||
quantity: itemQuantity,
|
||||
});
|
||||
|
||||
@@ -255,7 +257,7 @@ export default {
|
||||
if (this.sortBy === 'quantity') {
|
||||
return b.quantity - a.quantity;
|
||||
} else { // AZ
|
||||
return a.data.text().localeCompare(b.data.text());
|
||||
return a.text.localeCompare(b.text);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -264,7 +266,7 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
petExists (potionKey, eggKey) {
|
||||
userHasPet (potionKey, eggKey) {
|
||||
let animalKey = `${eggKey}-${potionKey}`;
|
||||
|
||||
let result = this.user.items.pets[animalKey] > 0;
|
||||
@@ -290,10 +292,20 @@ export default {
|
||||
dragEvent.dataTransfer.setDragImage(itemRef, -20, -20);
|
||||
},
|
||||
|
||||
onDragOver ($event, egg) {
|
||||
let potionKey = this.currentDraggingPotion.key;
|
||||
isHatchable (potion, eggKey) {
|
||||
if (potion === null)
|
||||
return false;
|
||||
|
||||
if (this.petExists(potionKey, egg.key)) {
|
||||
let petKey = `${eggKey}-${potion.key}`;
|
||||
|
||||
if (!this.content.petInfo[petKey])
|
||||
return false;
|
||||
|
||||
return !this.userHasPet(potion.key, eggKey);
|
||||
},
|
||||
|
||||
onDragOver ($event, egg) {
|
||||
if (this.isHatchable(this.currentDraggingPotion, egg.key)) {
|
||||
$event.dropable = false;
|
||||
}
|
||||
},
|
||||
@@ -309,7 +321,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.petExists(this.currentDraggingPotion.key, egg.key)) {
|
||||
if (this.isHatchable(this.currentDraggingPotion, egg.key)) {
|
||||
this.hatchPet(this.currentDraggingPotion, egg);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,13 +144,14 @@
|
||||
:item="item",
|
||||
:emptyItem="false",
|
||||
:popoverPosition="'top'",
|
||||
@click="selectedItemToBuy = item"
|
||||
@click="itemSelected(item)"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
h4.popover-content-title {{ item.text }}
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
countBadge(
|
||||
v-if="item.purchaseType !== 'card'",
|
||||
:show="userItems[item.purchaseType][item.key] != 0",
|
||||
:count="userItems[item.purchaseType][item.key] || 0"
|
||||
)
|
||||
@@ -161,6 +162,7 @@
|
||||
)
|
||||
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
|
||||
|
||||
div.fill-height
|
||||
|
||||
drawer(
|
||||
:title="$t('quickInventory')"
|
||||
@@ -255,11 +257,22 @@
|
||||
:itemContentClass="ctx.item.class",
|
||||
:showPopover="false"
|
||||
)
|
||||
|
||||
selectMembersModal(
|
||||
:card="selectedCardToBuy",
|
||||
:group="user.party",
|
||||
@change="resetCardToBuy($event)",
|
||||
@memberSelected="memberSelected($event)",
|
||||
)
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.fill-height {
|
||||
height: 38px; // button + margin + padding
|
||||
}
|
||||
|
||||
.badge-svg {
|
||||
left: calc((100% - 18px) / 2);
|
||||
cursor: pointer;
|
||||
@@ -391,6 +404,7 @@
|
||||
import SellModal from './sellModal.vue';
|
||||
import BuyModal from '../buyModal.vue';
|
||||
import EquipmentAttributesGrid from './equipmentAttributesGrid.vue';
|
||||
import SelectMembersModal from './selectMembersModal.vue';
|
||||
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
@@ -416,6 +430,8 @@
|
||||
|
||||
const sortGearTypes = ['sortByType', 'sortByPrice', 'sortByCon', 'sortByPer', 'sortByStr', 'sortByInt'];
|
||||
|
||||
import notifications from 'client/mixins/notifications';
|
||||
|
||||
const sortGearTypeMap = {
|
||||
sortByType: 'type',
|
||||
sortByPrice: 'value',
|
||||
@@ -425,6 +441,7 @@
|
||||
};
|
||||
|
||||
export default {
|
||||
mixins: [notifications],
|
||||
components: {
|
||||
ShopItem,
|
||||
Item,
|
||||
@@ -443,6 +460,8 @@ export default {
|
||||
BuyModal,
|
||||
EquipmentAttributesGrid,
|
||||
Avatar,
|
||||
|
||||
SelectMembersModal,
|
||||
},
|
||||
watch: {
|
||||
searchText: _throttle(function throttleSearch () {
|
||||
@@ -480,6 +499,8 @@ export default {
|
||||
selectedGearToBuy: null,
|
||||
selectedItemToBuy: null,
|
||||
|
||||
selectedCardToBuy: null,
|
||||
|
||||
hideLocked: false,
|
||||
hidePinned: false,
|
||||
};
|
||||
@@ -494,13 +515,26 @@ export default {
|
||||
}),
|
||||
categories () {
|
||||
if (this.market) {
|
||||
this.market.categories.map((category) => {
|
||||
let categories = [
|
||||
...this.market.categories,
|
||||
];
|
||||
|
||||
categories.push({
|
||||
identifier: 'cards',
|
||||
text: this.$t('cards'),
|
||||
items: _map(_filter(this.content.cardTypes, (value) => {
|
||||
return value.yearRound;
|
||||
}), (value) => {
|
||||
return getItemInfo(this.user, 'card', value);
|
||||
}),
|
||||
});
|
||||
categories.map((category) => {
|
||||
this.$set(this.viewOptions, category.identifier, {
|
||||
selected: true,
|
||||
});
|
||||
});
|
||||
|
||||
return this.market.categories;
|
||||
return categories;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -684,6 +718,11 @@ export default {
|
||||
this.selectedItemToBuy = null;
|
||||
}
|
||||
},
|
||||
resetCardToBuy ($event) {
|
||||
if (!$event) {
|
||||
this.selectedCardToBuy = null;
|
||||
}
|
||||
},
|
||||
isGearLocked (gear) {
|
||||
if (gear.value > this.userStats.gp) {
|
||||
return true;
|
||||
@@ -707,6 +746,21 @@ export default {
|
||||
buyItem (item) {
|
||||
this.$store.dispatch('shops:purchase', {type: item.purchaseType, key: item.key});
|
||||
},
|
||||
itemSelected (item) {
|
||||
if (item.purchaseType === 'card') {
|
||||
if (this.user.party._id) {
|
||||
this.selectedCardToBuy = item;
|
||||
} else {
|
||||
this.error(this.$t('errorNotInParty'));
|
||||
}
|
||||
} else {
|
||||
this.selectedItemToBuy = item;
|
||||
}
|
||||
},
|
||||
memberSelected (member) {
|
||||
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
|
||||
this.selectedCardToBuy = null;
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('shops:fetchMarket');
|
||||
|
||||
194
website/client/components/shops/market/selectMembersModal.vue
Normal file
194
website/client/components/shops/market/selectMembersModal.vue
Normal file
@@ -0,0 +1,194 @@
|
||||
<template lang="pug">
|
||||
div
|
||||
b-modal#select-member-modal(
|
||||
size='lg',
|
||||
:visible="true",
|
||||
v-if="card != null",
|
||||
:hideFooter="true",
|
||||
@change="onChange($event)"
|
||||
)
|
||||
.header-wrap(slot="modal-header")
|
||||
.row
|
||||
.col-6
|
||||
h1(v-once) {{$t('selectPartyMember')}}
|
||||
.col-6
|
||||
button(type="button" aria-label="Close" class="close", @click='close()')
|
||||
span(aria-hidden="true") ×
|
||||
.row
|
||||
.form-group.col-6
|
||||
input.form-control.search(type="text", :placeholder="$t('search')", v-model='searchTerm')
|
||||
.col-4.offset-2
|
||||
span.dropdown-label {{ $t('sortBy') }}
|
||||
b-dropdown(:text="$t('sort')", right=true)
|
||||
b-dropdown-item(v-for='sortOption in sortOptions', @click='sort(sortOption.value)', :key='sortOption.value') {{sortOption.text}}
|
||||
.row(v-for='member in sortedMembers')
|
||||
.col-10
|
||||
member-details(:member='member')
|
||||
.col-2.actions
|
||||
button.btn.btn-primary(@click="selectMember(member)") {{ $t('select') }}
|
||||
.row.gradient(v-if='members.length > 3')
|
||||
</template>
|
||||
|
||||
<style lang='scss'>
|
||||
#select-member-modal {
|
||||
header {
|
||||
background-color: #edecee;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.header-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #4f2a93;
|
||||
}
|
||||
|
||||
.actions {
|
||||
padding-top: 5em;
|
||||
|
||||
.action-icon {
|
||||
margin-right: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
#select-member-modal_modal_body {
|
||||
padding: 0;
|
||||
max-height: 450px;
|
||||
|
||||
.col-8 {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.member-details {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.member-stats {
|
||||
width: 382px;
|
||||
height: 147px;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0), #ffffff);
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
margin-left: -15px;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-icon-item .svg-icon {
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// @TODO: Move this under members directory
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import bModal from 'bootstrap-vue/lib/components/modal';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
|
||||
import MemberDetails from '../../memberDetails';
|
||||
import removeIcon from 'assets/members/remove.svg';
|
||||
import messageIcon from 'assets/members/message.svg';
|
||||
import starIcon from 'assets/members/star.svg';
|
||||
|
||||
export default {
|
||||
props: ['group', 'hideBadge', 'card'],
|
||||
components: {
|
||||
bModal,
|
||||
bDropdown,
|
||||
bDropdownItem,
|
||||
MemberDetails,
|
||||
},
|
||||
mounted () {
|
||||
this.$root.$on('shown::modal', () => {
|
||||
this.getMembers();
|
||||
});
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
sortOption: '',
|
||||
members: [],
|
||||
memberToRemove: '',
|
||||
sortOptions: [
|
||||
{
|
||||
value: 'level',
|
||||
text: this.$t('tier'),
|
||||
},
|
||||
{
|
||||
value: 'name',
|
||||
text: this.$t('name'),
|
||||
},
|
||||
{
|
||||
value: 'lvl',
|
||||
text: this.$t('level'),
|
||||
},
|
||||
{
|
||||
value: 'class',
|
||||
text: this.$t('class'),
|
||||
},
|
||||
],
|
||||
searchTerm: '',
|
||||
icons: Object.freeze({
|
||||
removeIcon,
|
||||
messageIcon,
|
||||
starIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
sortedMembers () {
|
||||
let sortedMembers = this.members;
|
||||
if (!this.sortOption) return sortedMembers;
|
||||
|
||||
sortedMembers = sortBy(this.members, [(member) => {
|
||||
if (this.sortOption === 'tier') {
|
||||
if (!member.contributor) return;
|
||||
return member.contributor.level;
|
||||
} else if (this.sortOption === 'name') {
|
||||
return member.profile.name;
|
||||
} else if (this.sortOption === 'lvl') {
|
||||
return member.stats.lvl;
|
||||
} else if (this.sortOption === 'class') {
|
||||
return member.stats.class;
|
||||
}
|
||||
}]);
|
||||
|
||||
return this.members;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async getMembers () {
|
||||
let groupId = this.$store.state.groupId || this.group._id;
|
||||
if (groupId && groupId !== 'challenge') {
|
||||
let members = await this.$store.dispatch('members:getGroupMembers', {
|
||||
groupId,
|
||||
includeAllPublicFields: true,
|
||||
});
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
if (this.$store.state.viewingMembers.length > 0) {
|
||||
this.members = this.$store.state.viewingMembers;
|
||||
}
|
||||
},
|
||||
close () {
|
||||
this.$root.$emit('hide::modal', 'select-member-modal');
|
||||
},
|
||||
sort (option) {
|
||||
this.sortOption = option;
|
||||
},
|
||||
selectMember (member) {
|
||||
this.$emit('memberSelected', member);
|
||||
},
|
||||
onChange ($event) {
|
||||
this.$emit('change', $event);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -23,7 +23,7 @@
|
||||
h4.title {{ itemText }}
|
||||
div.text(v-html="itemNotes")
|
||||
|
||||
slot(name="additionalInfo", :item="item")
|
||||
questInfo.questInfo(:quest="item")
|
||||
|
||||
div
|
||||
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="(priceType === 'gems') ? icons.gem : icons.gold")
|
||||
@@ -81,7 +81,19 @@
|
||||
|
||||
.inner-content {
|
||||
margin: 33px auto auto;
|
||||
width: 282px;
|
||||
width: 400px;
|
||||
}
|
||||
.text {
|
||||
max-height: 220px;
|
||||
margin-bottom: 8px;
|
||||
overflow-y: scroll;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.questInfo {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content-text {
|
||||
@@ -222,12 +234,14 @@
|
||||
|
||||
import BalanceInfo from '../balanceInfo.vue';
|
||||
import currencyMixin from '../_currencyMixin';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
|
||||
export default {
|
||||
mixins: [currencyMixin],
|
||||
components: {
|
||||
bModal,
|
||||
BalanceInfo,
|
||||
QuestInfo,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
@@ -55,9 +55,9 @@
|
||||
@click="selectedItemToBuy = item"
|
||||
)
|
||||
template(slot="popoverContent", scope="ctx")
|
||||
div
|
||||
div.questPopover
|
||||
h4.popover-content-title {{ item.text }}
|
||||
.popover-content-text(v-html="item.notes")
|
||||
questInfo(:quest="item")
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-svg(
|
||||
@@ -105,9 +105,9 @@
|
||||
@click="selectedItemToBuy = ctx.item"
|
||||
)
|
||||
span(slot="popoverContent", scope="ctx")
|
||||
div
|
||||
div.questPopover
|
||||
h4.popover-content-title {{ ctx.item.text }}
|
||||
.popover-content-text(v-html="ctx.item.notes")
|
||||
questInfo(:quest="ctx.item")
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-svg(
|
||||
@@ -135,9 +135,9 @@
|
||||
@click="selectedItemToBuy = item"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
div
|
||||
div.questPopover
|
||||
h4.popover-content-title {{ item.text }}
|
||||
.popover-content-text(v-html="item.notes")
|
||||
questInfo(:quest="item")
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-svg(
|
||||
@@ -162,9 +162,9 @@
|
||||
@click="selectedItemToBuy = item"
|
||||
)
|
||||
span(slot="popoverContent")
|
||||
div
|
||||
div.questPopover
|
||||
h4.popover-content-title {{ item.text }}
|
||||
.popover-content-text(v-html="item.notes")
|
||||
questInfo(:quest="item")
|
||||
|
||||
template(slot="itemBadge", scope="ctx")
|
||||
span.badge.badge-pill.badge-item.badge-svg(
|
||||
@@ -331,6 +331,7 @@
|
||||
import Avatar from 'client/components/avatar';
|
||||
|
||||
import BuyModal from './buyQuestModal.vue';
|
||||
import QuestInfo from './questInfo.vue';
|
||||
import bPopover from 'bootstrap-vue/lib/components/popover';
|
||||
import bDropdown from 'bootstrap-vue/lib/components/dropdown';
|
||||
import bDropdownItem from 'bootstrap-vue/lib/components/dropdown-item';
|
||||
@@ -363,6 +364,7 @@ export default {
|
||||
|
||||
Avatar,
|
||||
BuyModal,
|
||||
QuestInfo,
|
||||
},
|
||||
watch: {
|
||||
searchText: _throttle(function throttleSearch () {
|
||||
|
||||
88
website/client/components/shops/quests/questInfo.vue
Normal file
88
website/client/components/shops/quests/questInfo.vue
Normal file
@@ -0,0 +1,88 @@
|
||||
<template lang="pug">
|
||||
div.row
|
||||
span.col-4(v-if="quest.collect") {{ $t('collect') }}
|
||||
span.col-8(v-if="quest.collect")
|
||||
div(v-for="(collect, key) of quest.collect")
|
||||
span {{ collect.count }} {{ collect.text }}
|
||||
|
||||
span.col-4 {{ $t('difficulty') }}
|
||||
span.col-8
|
||||
span.svg-icon.inline.icon-16(v-for="star of stars()", v-html="icons[star]")
|
||||
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@import '~client/assets/scss/colors.scss';
|
||||
|
||||
.col-4{
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
height: 16px;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.col-8 {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.col-8:not(:last-child) {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
span.svg-icon.inline.icon-16 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import svgStar from 'assets/svg/difficulty-star.svg';
|
||||
import svgStarHalf from 'assets/svg/difficulty-star-half.svg';
|
||||
import svgStarEmpty from 'assets/svg/difficulty-star-empty.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
star: svgStar,
|
||||
starHalf: svgStarHalf,
|
||||
starEmpty: svgStarEmpty,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
difficulty () {
|
||||
if (this.quest.boss) {
|
||||
return this.quest.boss.str;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
stars () {
|
||||
let result = [];
|
||||
let difficulty = this.difficulty;
|
||||
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
let diff = difficulty - i;
|
||||
|
||||
if (diff >= 0) {
|
||||
result.push('star');
|
||||
} else if (diff <= -1) {
|
||||
result.push('starEmpty');
|
||||
} else {
|
||||
result.push('starHalf');
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
},
|
||||
props: {
|
||||
quest: {
|
||||
type: Object,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -105,6 +105,14 @@
|
||||
:item="ctx.item",
|
||||
v-if="ctx.item.purchaseType === 'gear'"
|
||||
)
|
||||
|
||||
selectMembersModal(
|
||||
:card="selectedCardToBuy",
|
||||
:group="user.party",
|
||||
@change="resetCardToBuy($event)",
|
||||
@memberSelected="memberSelectedToSendCard($event)",
|
||||
)
|
||||
|
||||
spells
|
||||
</template>
|
||||
|
||||
@@ -292,6 +300,7 @@ import BuyModal from 'client/components/shops/buyModal.vue';
|
||||
import Item from 'client/components/inventory/item.vue';
|
||||
import Avatar from 'client/components/avatar';
|
||||
import EquipmentAttributesGrid from 'client/components/shops/market/equipmentAttributesGrid.vue';
|
||||
import SelectMembersModal from 'client/components/shops/market/selectMembersModal.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -304,6 +313,7 @@ export default {
|
||||
Avatar,
|
||||
EquipmentAttributesGrid,
|
||||
spells,
|
||||
SelectMembersModal,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -329,6 +339,7 @@ export default {
|
||||
creatingTask: null,
|
||||
|
||||
selectedItemToBuy: null,
|
||||
selectedCardToBuy: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -452,13 +463,20 @@ export default {
|
||||
this.selectedItemToBuy = null;
|
||||
}
|
||||
},
|
||||
resetCardToBuy ($event) {
|
||||
if (!$event) {
|
||||
this.selectedCardToBuy = null;
|
||||
}
|
||||
},
|
||||
memberOverrideAvatarGear (gear) {
|
||||
return {
|
||||
[gear.type]: gear.key,
|
||||
};
|
||||
},
|
||||
buyItem (item) {
|
||||
if (item.currency === 'gold') {
|
||||
if (item.purchaseType === 'card') {
|
||||
this.selectedCardToBuy = item;
|
||||
} else if (item.currency === 'gold') {
|
||||
this.$store.dispatch('shops:buyItem', {key: item.key});
|
||||
} else {
|
||||
this.$store.dispatch('shops:purchase', {type: item.purchaseType, key: item.key});
|
||||
@@ -467,6 +485,10 @@ export default {
|
||||
openBuyDialog (rewardItem) {
|
||||
this.selectedItemToBuy = rewardItem;
|
||||
},
|
||||
memberSelectedToSendCard (member) {
|
||||
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
|
||||
this.selectedCardToBuy = null;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -10,7 +10,7 @@ span.badge.badge-pill.badge-item.badge-count(
|
||||
.badge-count {
|
||||
right: -9px;
|
||||
color: $white;
|
||||
background: $orange-100;
|
||||
background: $gray-200;
|
||||
padding: 4.5px 6px;
|
||||
min-width: 24px;
|
||||
height: 24px;
|
||||
|
||||
@@ -71,3 +71,10 @@ export function togglePinnedItem (store, params) {
|
||||
|
||||
return addedItem;
|
||||
}
|
||||
|
||||
export function castSpell (store, params) {
|
||||
let spellUrl = `/api/v3/user/class/cast/${params.key}`;
|
||||
if (params.targetId) spellUrl += `?targetId=${params.targetId}`;
|
||||
|
||||
return axios.post(spellUrl);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"dragThisFood": "Drag this <%= foodName %> to a Pet and watch it grow!",
|
||||
"clickOnPetToFeed": "Click on a Pet to feed <%= foodName %> and watch it grow!",
|
||||
"dragThisPotion": "Drag this <%= potionName %> to an Egg and hatch a new pet!",
|
||||
"clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> and hatch a new pet!",
|
||||
"clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> hatching potion and hatch a new pet!",
|
||||
"hatchDialogText": "Pour your <%= potionName %> hatching potion on your <%= eggName %> egg, and it will hatch into a <%= petName %> .",
|
||||
"editAvatar": "Edit Avatar",
|
||||
"sort": "Sort",
|
||||
@@ -285,5 +285,7 @@
|
||||
"sortByName": "Name",
|
||||
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch.",
|
||||
"welcomeBack": "Welcome back!",
|
||||
"checkOffYesterDailies": "Check off any Dailies you did yesterday:"
|
||||
"checkOffYesterDailies": "Check off any Dailies you did yesterday:",
|
||||
"selectPartyMember": "Select a Party Member",
|
||||
"errorNotInParty": "You are not in a Party"
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ api.bundles = {
|
||||
return moment().isBetween('2017-05-16', '2017-05-31');
|
||||
},
|
||||
type: 'quests',
|
||||
class: 'quest_bundle_featheredFriends',
|
||||
value: 7,
|
||||
},
|
||||
splashyPals: {
|
||||
@@ -86,6 +87,7 @@ api.bundles = {
|
||||
return moment().isBetween('2017-07-11', '2017-08-02');
|
||||
},
|
||||
type: 'quests',
|
||||
class: 'quest_bundle_splashyPals',
|
||||
value: 7,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -5,29 +5,33 @@ const featuredItems = {
|
||||
path: 'armoire',
|
||||
},
|
||||
{
|
||||
type: 'hatchingPotion',
|
||||
type: 'hatchingPotions',
|
||||
path: 'hatchingPotions.Golden',
|
||||
},
|
||||
{
|
||||
type: 'food',
|
||||
path: 'food.Saddle',
|
||||
},
|
||||
{
|
||||
type: 'card',
|
||||
path: 'cardTypes.greeting',
|
||||
},
|
||||
],
|
||||
quests: [
|
||||
{
|
||||
type: 'quest',
|
||||
type: 'quests',
|
||||
path: 'quests.gryphon',
|
||||
},
|
||||
{
|
||||
type: 'quest',
|
||||
type: 'quests',
|
||||
path: 'quests.dilatoryDistress1',
|
||||
},
|
||||
{
|
||||
type: 'quest',
|
||||
type: 'quests',
|
||||
path: 'quests.nudibranch',
|
||||
},
|
||||
{
|
||||
type: 'quest',
|
||||
type: 'quests',
|
||||
path: 'quests.taskwoodsTerror1',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -3,6 +3,8 @@ import content from '../content/index';
|
||||
import { BadRequest } from './errors';
|
||||
import count from '../count';
|
||||
|
||||
import _mapValues from 'lodash/mapValues';
|
||||
|
||||
function lockQuest (quest, user) {
|
||||
if (quest.lvl && user.stats.lvl < quest.lvl) return true;
|
||||
if (quest.unlockCondition && (quest.key === 'moon1' || quest.key === 'moon2' || quest.key === 'moon3')) {
|
||||
@@ -42,7 +44,7 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
let itemInfo;
|
||||
|
||||
switch (type) {
|
||||
case 'egg':
|
||||
case 'eggs':
|
||||
itemInfo = {
|
||||
key: item.key,
|
||||
text: i18n.t('egg', {eggType: item.text(language)}, language),
|
||||
@@ -53,10 +55,10 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
currency: 'gems',
|
||||
purchaseType: 'eggs',
|
||||
path: `eggs.${item.key}`,
|
||||
pinType: 'egg',
|
||||
pinType: 'eggs',
|
||||
};
|
||||
break;
|
||||
case 'hatchingPotion':
|
||||
case 'hatchingPotions':
|
||||
itemInfo = {
|
||||
key: item.key,
|
||||
text: i18n.t('potion', {potionType: item.text(language)}),
|
||||
@@ -67,7 +69,7 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
currency: 'gems',
|
||||
purchaseType: 'hatchingPotions',
|
||||
path: `hatchingPotions.${item.key}`,
|
||||
pinType: 'hatchingPotion',
|
||||
pinType: 'hatchingPotions',
|
||||
};
|
||||
break;
|
||||
case 'premiumHatchingPotion':
|
||||
@@ -98,20 +100,20 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
pinType: 'food',
|
||||
};
|
||||
break;
|
||||
case 'questBundle':
|
||||
case 'bundles':
|
||||
itemInfo = {
|
||||
key: item.key,
|
||||
text: item.text(language),
|
||||
notes: item.notes(language),
|
||||
value: item.value,
|
||||
currency: 'gems',
|
||||
class: `quest_bundle_${item.key}`,
|
||||
class: item.class,
|
||||
purchaseType: 'bundles',
|
||||
path: `bundles.${item.key}`,
|
||||
pinType: 'questBundle',
|
||||
pinType: 'bundles',
|
||||
};
|
||||
break;
|
||||
case 'quest': // eslint-disable-line no-case-declarations
|
||||
case 'quests': // eslint-disable-line no-case-declarations
|
||||
const locked = lockQuest(item, user);
|
||||
|
||||
itemInfo = {
|
||||
@@ -125,13 +127,19 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
unlockCondition: item.unlockCondition,
|
||||
drop: item.drop,
|
||||
boss: item.boss,
|
||||
collect: item.collect,
|
||||
collect: item.collect ? _mapValues(item.collect, (o) => {
|
||||
return {
|
||||
count: o.count,
|
||||
text: o.text(),
|
||||
};
|
||||
}) : undefined,
|
||||
lvl: item.lvl,
|
||||
class: locked ? `inventory_quest_scroll_locked inventory_quest_scroll_${item.key}_locked` : `inventory_quest_scroll inventory_quest_scroll_${item.key}`,
|
||||
purchaseType: 'quests',
|
||||
path: `quests.${item.key}`,
|
||||
pinType: 'quest',
|
||||
pinType: 'quests',
|
||||
};
|
||||
|
||||
break;
|
||||
case 'timeTravelers':
|
||||
// TODO
|
||||
@@ -235,6 +243,23 @@ module.exports = function getItemInfo (user, type, item, language = 'en') {
|
||||
path: 'armoire',
|
||||
pinType: 'armoire',
|
||||
};
|
||||
break;
|
||||
case 'card': {
|
||||
let spellInfo = content.spells.special[item.key];
|
||||
|
||||
itemInfo = {
|
||||
key: item.key,
|
||||
purchaseType: 'card',
|
||||
class: `inventory_special_${item.key}`,
|
||||
text: spellInfo.text(),
|
||||
notes: spellInfo.notes(),
|
||||
value: spellInfo.value,
|
||||
currency: 'gold',
|
||||
path: `cardTypes.${item.key}`,
|
||||
pinType: 'card',
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemInfo) {
|
||||
|
||||
@@ -24,7 +24,7 @@ shops.getMarketCategories = function getMarket (user, language) {
|
||||
.filter(egg => egg.canBuy(user))
|
||||
.concat(values(content.dropEggs))
|
||||
.map(egg => {
|
||||
return getItemInfo(user, 'egg', egg, language);
|
||||
return getItemInfo(user, 'eggs', egg, language);
|
||||
}), 'key');
|
||||
categories.push(eggsCategory);
|
||||
|
||||
@@ -36,7 +36,7 @@ shops.getMarketCategories = function getMarket (user, language) {
|
||||
hatchingPotionsCategory.items = sortBy(values(content.hatchingPotions)
|
||||
.filter(hp => !hp.limited)
|
||||
.map(hatchingPotion => {
|
||||
return getItemInfo(user, 'hatchingPotion', hatchingPotion, language);
|
||||
return getItemInfo(user, 'hatchingPotions', hatchingPotion, language);
|
||||
}), 'key');
|
||||
categories.push(hatchingPotionsCategory);
|
||||
|
||||
@@ -134,7 +134,7 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language)
|
||||
bundleCategory.items = sortBy(values(content.bundles)
|
||||
.filter(bundle => bundle.type === 'quests' && bundle.canBuy())
|
||||
.map(bundle => {
|
||||
return getItemInfo(user, 'questBundle', bundle, language);
|
||||
return getItemInfo(user, 'bundles', bundle, language);
|
||||
}));
|
||||
|
||||
if (bundleCategory.items.length > 0) {
|
||||
@@ -150,7 +150,7 @@ shops.getQuestShopCategories = function getQuestShopCategories (user, language)
|
||||
category.items = content.questsByLevel
|
||||
.filter(quest => quest.canBuy(user) && quest.category === type)
|
||||
.map(quest => {
|
||||
return getItemInfo(user, 'quest', quest, language);
|
||||
return getItemInfo(user, 'quests', quest, language);
|
||||
});
|
||||
|
||||
categories.push(category);
|
||||
|
||||
@@ -11,6 +11,9 @@ import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
|
||||
import { removeItemByPath } from './pinnedGearUtils';
|
||||
import getItemInfo from '../libs/getItemInfo';
|
||||
|
||||
module.exports = function purchase (user, req = {}, analytics) {
|
||||
let type = get(req.params, 'type');
|
||||
let key = get(req.params, 'key');
|
||||
@@ -103,6 +106,9 @@ module.exports = function purchase (user, req = {}, analytics) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughGems', req.language));
|
||||
}
|
||||
|
||||
let itemInfo = getItemInfo(user, type, item);
|
||||
removeItemByPath(user, itemInfo.path);
|
||||
|
||||
user.balance -= price;
|
||||
|
||||
if (type === 'gear') {
|
||||
|
||||
Reference in New Issue
Block a user