diff --git a/website/client/assets/scss/item.scss b/website/client/assets/scss/item.scss
index d9468d9f9d..17688fdeb0 100644
--- a/website/client/assets/scss/item.scss
+++ b/website/client/assets/scss/item.scss
@@ -54,6 +54,12 @@
.flat .item {
box-shadow: none;
+ border: none;
+}
+
+.bordered-item .item {
+ background: $gray-700 !important;
+ margin: 0 auto;
}
.drawer-content .item:hover {
diff --git a/website/client/assets/scss/popover.scss b/website/client/assets/scss/popover.scss
index 85a93232a6..b1758cea03 100644
--- a/website/client/assets/scss/popover.scss
+++ b/website/client/assets/scss/popover.scss
@@ -21,10 +21,17 @@
line-height: 1.14;
}
-.popover-content-text {
+.popover-content-text:not(:last-child) {
margin-bottom: 16px;
}
+.popover-content-title:last-child {
+ margin-bottom: 0px;
+}
+.popover-content-text:last-child {
+ margin-bottom: 0px;
+}
+
.popover-content-attr {
width: 50%;
display: inline-block;
diff --git a/website/client/components/inventory/equipment/index.vue b/website/client/components/inventory/equipment/index.vue
index a5b0aa12ee..b133c32246 100644
--- a/website/client/components/inventory/equipment/index.vue
+++ b/website/client/components/inventory/equipment/index.vue
@@ -99,6 +99,7 @@
:items="sortItems(items[group.key], selectedSortGearBy)",
:itemWidth=94,
:itemMargin=24,
+ :type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: group.label })"
)
template(slot="item", scope="context")
diff --git a/website/client/components/inventory/items/index.vue b/website/client/components/inventory/items/index.vue
index eac6f96bc5..d720c62fd9 100644
--- a/website/client/components/inventory/items/index.vue
+++ b/website/client/components/inventory/items/index.vue
@@ -40,6 +40,7 @@
:items="items[group.key]",
:itemWidth=94,
:itemMargin=24,
+ :type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
@@ -67,6 +68,7 @@
:items="items[group.key]",
:itemWidth=94,
:itemMargin=24,
+ :type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
@@ -94,6 +96,7 @@
:items="items[group.key]",
:itemWidth=94,
:itemMargin=24,
+ :type="group.key",
:noItemsLabel="$t('noGearItemsOfType', { type: $t(group.key) })"
)
template(slot="item", scope="context")
diff --git a/website/client/components/inventory/stable/index.vue b/website/client/components/inventory/stable/index.vue
index 94ce565936..49f14c5a12 100644
--- a/website/client/components/inventory/stable/index.vue
+++ b/website/client/components/inventory/stable/index.vue
@@ -84,6 +84,7 @@
:items="pets(petGroup, hideMissing, selectedSortBy, searchTextThrottled)",
:itemWidth=94,
:itemMargin=24,
+ :type="petGroup.key",
)
template(slot="item", scope="context")
div(
@@ -99,14 +100,12 @@
:popoverPosition="'top'",
:progress="context.item.progress",
:emptyItem="!context.item.isOwned()",
- :showPopover="context.item.isOwned() || context.item.isHatchable()",
+ :showPopover="true",
:highlightBorder="highlightPet == context.item.key",
@click="petClicked(context.item)"
)
span(slot="popoverContent")
- div(v-if="context.item.isOwned()")
- h4.popover-content-title {{ context.item.name }}
- div.hatchablePopover(v-else-if="context.item.isHatchable()")
+ div.hatchablePopover(v-if="context.item.isHatchable()")
h4.popover-content-title {{ context.item.name }}
div.popover-content-text(v-html="$t('haveHatchablePet', { potion: context.item.potionName, egg: context.item.eggName })")
div.potionEggGroup
@@ -115,6 +114,9 @@
div.potionEggBackground
div(:class="'Pet_Egg_'+context.item.eggKey")
+ div(v-else)
+ h4.popover-content-title {{ context.item.name }}
+
template(slot="itemBadge", scope="context")
starBadge(
:selected="context.item.key === currentPet",
@@ -138,6 +140,7 @@
:items="mounts(mountGroup, hideMissing, selectedSortBy, searchTextThrottled)",
:itemWidth=94,
:itemMargin=24,
+ :type="mountGroup.key",
)
template(slot="item", scope="context")
mountItem(
@@ -146,7 +149,7 @@
:key="context.item.key",
:popoverPosition="'top'",
:emptyItem="!context.item.isOwned()",
- :showPopover="context.item.isOwned()",
+ :showPopover="true",
@click="selectMount(context.item)"
)
span(slot="popoverContent")
@@ -273,11 +276,18 @@
display: inline-block;
}
- .stable .item .item-content.Pet {
- position: absolute;
+ .stable .item .item-content.Pet:not(.FlyingPig) {
top: -28px;
}
+ .stable .item .item-content.FlyingPig {
+ top: 7px;
+ }
+
+ .stable .item .item-content.Pet-Dragon-Hydra {
+ top: -16px !important;
+ }
+
.hatchablePopover {
width: 180px
}
@@ -833,7 +843,7 @@
getPetItemClass (pet) {
if (pet.isOwned()) {
- return `Pet Pet-${pet.key}`;
+ return `Pet Pet-${pet.key} ${pet.eggKey}`;
}
if (pet.mountOwned()) {
diff --git a/website/client/components/shops/_currencyMixin.js b/website/client/components/shops/_currencyMixin.js
new file mode 100644
index 0000000000..21f4190825
--- /dev/null
+++ b/website/client/components/shops/_currencyMixin.js
@@ -0,0 +1,25 @@
+import { mapState, mapGetters } from 'client/libs/store';
+
+export default {
+ computed: {
+ ...mapGetters({
+ userGems: 'user:gems',
+ }),
+ ...mapState({
+ userHourglasses: 'user.data.purchased.plan.consecutive.trinkets',
+ userGold: 'user.data.stats.gp',
+ }),
+ },
+ methods: {
+ enoughCurrency (currency, amount) {
+ switch (currency) {
+ case 'gold':
+ return this.userGold >= amount;
+ case 'gems':
+ return this.userGems >= amount;
+ case 'hourglasses':
+ return this.userHourglasses >= amount;
+ }
+ },
+ },
+};
diff --git a/website/client/components/shops/balanceInfo.vue b/website/client/components/shops/balanceInfo.vue
index a4955c2f7c..80ba7dbae3 100644
--- a/website/client/components/shops/balanceInfo.vue
+++ b/website/client/components/shops/balanceInfo.vue
@@ -1,12 +1,11 @@
div
- .svg-icon(v-if="withHourglass",v-html="icons.hourglasses")
- span(v-if="withHourglass") {{userHourglasses | roundBigNumber}}
-
- .svg-icon(v-html="icons.gem")
- span {{userGems | roundBigNumber}}
- .svg-icon(v-html="icons.gold")
- span {{userGold | roundBigNumber}}
+ span(
+ v-for="currency of currencies",
+ :key="currency.key"
+ )
+ .svg-icon(v-html="currency.icon")
+ span(:class="{'notEnough': currency.notEnough}") {{ currency.value | roundBigNumber}}
diff --git a/website/client/components/shops/buyModal.vue b/website/client/components/shops/buyModal.vue
index 40c96594fc..c61382914f 100644
--- a/website/client/components/shops/buyModal.vue
+++ b/website/client/components/shops/buyModal.vue
@@ -25,15 +25,28 @@
slot(name="additionalInfo", :item="item")
- div
- span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getSvgClass()]")
- span.value(:class="getSvgClass()") {{ item.value }}
+ div(:class="{'notEnough': !this.enoughCurrency(getPriceClass(), item.value)}")
+ span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
+ span.value(:class="getPriceClass()") {{ item.value }}
- button.btn.btn-primary(@click="buyItem()") {{ $t('buyNow') }}
+ button.btn.btn-primary(
+ @click="purchaseGems()",
+ v-if="getPriceClass() === 'gems' && !this.enoughCurrency(getPriceClass(), item.value)"
+ ) {{ $t('purchaseGems') }}
+
+ button.btn.btn-primary(
+ @click="buyItem()",
+ v-else,
+ :class="{'notEnough': !this.enoughCurrency(getPriceClass(), item.value)}"
+ ) {{ $t('buyNow') }}
div.clearfix(slot="modal-footer")
span.balance.float-left {{ $t('yourBalance') }}
- balanceInfo(:withHourglass="getSvgClass() === 'hourglasses'").float-right
+ balanceInfo(
+ :withHourglass="getPriceClass() === 'hourglasses'",
+ :currencyNeeded="getPriceClass()",
+ :amountNeeded="item.value"
+ ).float-right
@@ -136,7 +149,10 @@
}
}
-
+ .notEnough {
+ pointer-events: none;
+ opacity: 0.55;
+ }
}
@@ -150,8 +166,10 @@
import svgPin from 'assets/svg/pin.svg';
import BalanceInfo from './balanceInfo.vue';
+ import currencyMixin from './_currencyMixin';
export default {
+ mixins: [currencyMixin],
components: {
bModal,
BalanceInfo,
@@ -191,13 +209,16 @@
this.$emit('buyPressed', this.item);
this.hideDialog();
},
+ purchaseGems () {
+ this.$root.$emit('show::modal', 'buy-gems');
+ },
togglePinned () {
this.$emit('togglePinned', this.item);
},
hideDialog () {
this.$root.$emit('hide::modal', 'buy-modal');
},
- getSvgClass () {
+ getPriceClass () {
if (this.priceType && this.icons[this.priceType]) {
return this.priceType;
} else {
diff --git a/website/client/components/shops/market/index.vue b/website/client/components/shops/market/index.vue
index 0685ea8fc4..0e4d101ef7 100644
--- a/website/client/components/shops/market/index.vue
+++ b/website/client/components/shops/market/index.vue
@@ -53,6 +53,12 @@
:popoverPosition="'top'",
@click="selectedGearToBuy = item"
)
+ template(slot="itemBadge", scope="ctx")
+ span.badge.badge-pill.badge-item.badge-svg(
+ :class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
+ @click.prevent.stop="togglePinned(ctx.item)"
+ )
+ span.svg-icon.inline.icon-12.color(v-html="icons.pin")
h1.mb-0.page-header(v-once) {{ $t('market') }}
@@ -92,8 +98,7 @@
:items="filteredGear(selectedGroupGearByClass, searchTextThrottled, selectedSortGearBy, hideLocked, hidePinned)",
:itemWidth=94,
:itemMargin=24,
- :showAllLabel="$t('showAllGeneric', { type: getClassName(selectedGroupGearByClass) + ' '+$t('equipment') })",
- :showLessLabel="$t('showLessGeneric', { type: getClassName(selectedGroupGearByClass) + ' '+$t('equipment') })"
+ :type="'gear'",
)
template(slot="item", scope="ctx")
shopItem(
@@ -146,7 +151,7 @@
template(slot="itemBadge", scope="ctx")
countBadge(
- :show="true",
+ :show="userItems[item.purchaseType][item.key] != 0",
:count="userItems[item.purchaseType][item.key] || 0"
)
@@ -196,12 +201,13 @@
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0"
)
span(slot="popoverContent")
- h4.popover-content-title {{ ctx.item.text() }}
+ h4.popover-content-title {{ getItemName(selectedDrawerItemType, ctx.item) }}
sellModal(
:item="selectedItemToSell",
:itemType="selectedDrawerItemType",
:itemCount="selectedItemToSell != null ? userItems[drawerTabs[selectedDrawerTab].contentType][selectedItemToSell.key] : 0",
+ :text="selectedItemToSell != null ? getItemName(selectedDrawerItemType, selectedItemToSell) : ''",
@change="resetItemToSell($event)"
)
template(slot="item", scope="ctx")
@@ -244,7 +250,7 @@
@togglePinned="togglePinned($event)"
)
template(slot="item", scope="ctx")
- item.flat(
+ item.flat.bordered-item(
:item="ctx.item",
:itemContentClass="ctx.item.class",
:showPopover="false"
@@ -302,6 +308,11 @@
padding: 24px 24px 10px;
}
+ .item-wrapper.bordered-item .item {
+ width: 112px;
+ height: 112px;
+ }
+
.market {
.avatar {
cursor: default;
@@ -398,6 +409,7 @@
import _filter from 'lodash/filter';
import _sortBy from 'lodash/sortBy';
import _map from 'lodash/map';
+ import _get from 'lodash/get';
import _throttle from 'lodash/throttle';
import _isPinned from '../_isPinned';
@@ -520,7 +532,10 @@ export default {
featuredItems () {
return featuredItems.market.map(i => {
- return getItemInfo(this.user, 'marketGear', this.content.gear.flat[i]);
+ let newItem = getItemInfo(this.user, i.type, _get(this.content, i.path));
+ newItem.pinned = _isPinned(this.user, newItem);
+
+ return newItem;
});
},
},
@@ -571,6 +586,16 @@ export default {
return '';
}
},
+ getItemName (type, item) {
+ switch (type) {
+ case 'eggs':
+ return this.$t('egg', {eggType: item.text()});
+ case 'hatchingPotions':
+ return this.$t('potion', {potionType: item.text()});
+ default:
+ return item.text();
+ }
+ },
filteredGear (groupByClass, searchBy, sortBy, hideLocked, hidePinned) {
let result = _filter(this.content.gear.flat, ['klass', groupByClass]);
result = _map(result, (e) => {
diff --git a/website/client/components/shops/market/sellModal.vue b/website/client/components/shops/market/sellModal.vue
index dbb80e1c5c..a26ca32584 100644
--- a/website/client/components/shops/market/sellModal.vue
+++ b/website/client/components/shops/market/sellModal.vue
@@ -12,7 +12,7 @@
div.inner-content
slot(name="item", :item="item")
- h4.title {{ item.text() }}
+ h4.title {{ text ? text : item.text() }}
div.text {{ item.notes() }}
div
@@ -176,6 +176,9 @@
itemType: {
type: String,
},
+ text: {
+ type: String,
+ },
itemCount: {
type: Number,
},
diff --git a/website/client/components/shops/quests/buyQuestModal.vue b/website/client/components/shops/quests/buyQuestModal.vue
index 2b8d5671e8..ccc156a5e7 100644
--- a/website/client/components/shops/quests/buyQuestModal.vue
+++ b/website/client/components/shops/quests/buyQuestModal.vue
@@ -29,7 +29,17 @@
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="(priceType === 'gems') ? icons.gem : icons.gold")
span.value(:class="priceType") {{ item.value }}
- button.btn.btn-primary(@click="buyItem()") {{ $t('buyNow') }}
+ button.btn.btn-primary(
+ @click="purchaseGems()",
+ v-if="priceType === 'gems' && !this.enoughCurrency(priceType, item.value)"
+ ) {{ $t('purchaseGems') }}
+
+
+ button.btn.btn-primary(
+ @click="buyItem()",
+ v-else,
+ :class="{'notEnough': !this.enoughCurrency(priceType, item.value)}"
+ ) {{ $t('buyNow') }}
div.right-sidebar(v-if="item.drop")
h3(v-once) {{ $t('rewards') }}
@@ -46,7 +56,10 @@
div.clearfix(slot="modal-footer")
span.balance.float-left {{ $t('yourBalance') }}
- balanceInfo.float-right
+ balanceInfo(
+ :currencyNeeded="priceType",
+ :amountNeeded="item.value"
+ ).float-right
@@ -186,6 +199,12 @@
color: $white;
}
}
+
+
+ .notEnough {
+ pointer-events: none;
+ opacity: 0.55;
+ }
}
@@ -202,8 +221,10 @@
import svgExperience from 'assets/svg/experience.svg';
import BalanceInfo from '../balanceInfo.vue';
+ import currencyMixin from '../_currencyMixin';
export default {
+ mixins: [currencyMixin],
components: {
bModal,
BalanceInfo,
@@ -284,6 +305,10 @@
return `Unknown type: ${drop.type}`;
}
},
+
+ purchaseGems () {
+ this.$root.$emit('show::modal', 'buy-gems');
+ },
},
props: {
item: {
diff --git a/website/client/components/shops/quests/index.vue b/website/client/components/shops/quests/index.vue
index 205d8a7fb8..4d3cb9b544 100644
--- a/website/client/components/shops/quests/index.vue
+++ b/website/client/components/shops/quests/index.vue
@@ -59,6 +59,14 @@
h4.popover-content-title {{ item.text }}
.popover-content-text(v-html="item.notes")
+ template(slot="itemBadge", scope="ctx")
+ span.badge.badge-pill.badge-item.badge-svg(
+ :class="{'item-selected-badge': ctx.item.pinned, 'hide': !ctx.item.pinned}",
+ @click.prevent.stop="togglePinned(ctx.item)"
+ )
+ span.svg-icon.inline.icon-12.color(v-html="icons.pin")
+
+
h1.mb-0.page-header(v-once) {{ $t('quests') }}
.clearfix
@@ -84,6 +92,7 @@
:items="questItems(category, selectedSortItemsBy, searchTextThrottled, hideLocked, hidePinned)",
:itemWidth=94,
:itemMargin=24,
+ :type="'pet_quests'",
)
template(slot="item", scope="ctx")
shopItem(
@@ -338,6 +347,7 @@
import _throttle from 'lodash/throttle';
import _groupBy from 'lodash/groupBy';
import _map from 'lodash/map';
+ import _get from 'lodash/get';
export default {
components: {
@@ -403,7 +413,10 @@ export default {
featuredItems () {
return featuredItems.quests.map(i => {
- return getItemInfo(this.user, 'quest', this.content.quests[i]);
+ let newItem = getItemInfo(this.user, i.type, _get(this.content, i.path));
+ newItem.pinned = _isPinned(this.user, newItem);
+
+ return newItem;
});
},
},
diff --git a/website/client/components/shops/timeTravelers/index.vue b/website/client/components/shops/timeTravelers/index.vue
index 9e190da844..32dfcc375a 100644
--- a/website/client/components/shops/timeTravelers/index.vue
+++ b/website/client/components/shops/timeTravelers/index.vue
@@ -78,6 +78,7 @@
:items="travelersItems(category, selectedSortItemsBy, searchTextThrottled, hidePinned)",
:itemWidth=94,
:itemMargin=24,
+ :type="category.identifier",
)
template(slot="item", scope="ctx")
shopItem(
diff --git a/website/client/components/tasks/column.vue b/website/client/components/tasks/column.vue
index 931bf8bb05..f87c7ce4bb 100644
--- a/website/client/components/tasks/column.vue
+++ b/website/client/components/tasks/column.vue
@@ -25,7 +25,8 @@
:item="reward",
:key="reward.key",
:highlightBorder="reward.isSuggested",
- @click="openBuyDialog(reward)"
+ @click="openBuyDialog(reward)",
+ :popoverPosition="'left'"
)
.column-background(
diff --git a/website/client/components/ui/itemRows.vue b/website/client/components/ui/itemRows.vue
index c05b24a683..034f21225e 100644
--- a/website/client/components/ui/itemRows.vue
+++ b/website/client/components/ui/itemRows.vue
@@ -1,18 +1,18 @@
.item-rows
- div.items(v-resize="500", @resized="currentWidth = $event.width")
+ div.items(v-resize="500", @resized="setCurrentWidth($event)")
template(v-for="item in itemsToShow(showAll)")
slot(
name="item",
- :item="item",
+ :item="item"
)
div(v-if="items.length === 0")
p(v-once) {{ noItemsLabel }}
.btn-flat.btn-show-more(
- @click="showAll = !showAll",
- v-if="items.length > itemsPerRow()"
+ @click="toggleItemsToShow()",
+ v-if="items.length > itemsPerRow"
) {{ showAll ? $t('showLess') : $t('showMore') }}
div.fill-height(v-else)
@@ -27,14 +27,22 @@
diff --git a/website/client/libs/createAnimal.js b/website/client/libs/createAnimal.js
index 07bc69d576..0f5e60ebcc 100644
--- a/website/client/libs/createAnimal.js
+++ b/website/client/libs/createAnimal.js
@@ -19,7 +19,7 @@ export default function createAnimal (egg, potion, type, content, userItems) {
return type === 'pet' && this.isOwned() && !this.mountOwned();
},
isHatchable () {
- return userItems.eggs[egg.key] > 0 && userItems.hatchingPotions[potion.key] > 0;
+ return !this.isOwned() & userItems.eggs[egg.key] > 0 && userItems.hatchingPotions[potion.key] > 0;
},
};
}
diff --git a/website/client/store/index.js b/website/client/store/index.js
index 1c4528a17a..d97cd99d55 100644
--- a/website/client/store/index.js
+++ b/website/client/store/index.js
@@ -75,6 +75,7 @@ export default function () {
constants: deepFreeze({...commonConstants, DAY_MAPPING}),
hideHeader: false,
viewingMembers: [],
+ openedItemRows: [],
castingSpell: false,
},
});
diff --git a/website/common/script/content/shop-featuredItems.js b/website/common/script/content/shop-featuredItems.js
index fb6bdd7636..09c6b82e60 100644
--- a/website/common/script/content/shop-featuredItems.js
+++ b/website/common/script/content/shop-featuredItems.js
@@ -1,19 +1,40 @@
const featuredItems = {
- market: [
- 'head_armoire_vikingHelm',
- 'weapon_special_1',
- 'shield_special_0',
- 'armor_warrior_5',
- ],
- quests: [
- 'dilatoryDistress1',
- 'dilatoryDistress2',
- 'dilatoryDistress3',
- ],
- seasonal: 'summerMage',
- timeTravelers: [
+ market: [
+ {
+ type: 'armoire',
+ path: 'armoire',
+ },
+ {
+ type: 'hatchingPotion',
+ path: 'hatchingPotions.Golden',
+ },
+ {
+ type: 'food',
+ path: 'food.Saddle',
+ },
+ ],
+ quests: [
+ {
+ type: 'quest',
+ path: 'quests.gryphon',
+ },
+ {
+ type: 'quest',
+ path: 'quests.dilatoryDistress1',
+ },
+ {
+ type: 'quest',
+ path: 'quests.nudibranch',
+ },
+ {
+ type: 'quest',
+ path: 'quests.taskwoodsTerror1',
+ },
+ ],
+ seasonal: 'summerMage',
+ timeTravelers: [
// TODO
- ],
+ ],
};
export default featuredItems;