Backgrounds / BuyModal / misc (#9009)

* remove buyModal from all views / move to app.vue - show party member selection if card was bought

* add .btn style to 'show more' / 'show less'

* prevent adding duplicate pinned items - reset pinned gear on rebirth

* remove hourglass in items

* fix unequip gear from drawer

* fix bought item notification

* fix background in avatar - avatar position in buyModal
This commit is contained in:
negue 2017-08-31 22:56:53 +02:00 committed by GitHub
parent 19789eb7ab
commit 87d57dab13
18 changed files with 187 additions and 338 deletions

View file

@ -10,6 +10,21 @@
app-menu
.container-fluid
app-header
buyModal(
:item="selectedItemToBuy",
:withPin="false",
@change="resetItemToBuy($event)",
@buyPressed="customPurchase($event)",
:genericPurchase="genericPurchase(selectedItemToBuy)",
)
selectMembersModal(
:item="selectedCardToBuy",
:group="user.party",
@change="resetCardToBuy($event)",
@memberSelected="memberSelected($event)",
)
div(:class='{sticky: user.preferences.stickyHeader}')
router-view
app-footer
@ -50,8 +65,12 @@ import AppFooter from './components/appFooter';
import notificationsDisplay from './components/notifications';
import snackbars from './components/snackbars/notifications';
import { mapState } from 'client/libs/store';
import BuyModal from './components/shops/buyModal.vue';
import SelectMembersModal from 'client/components/selectMembersModal.vue';
import notifications from 'client/mixins/notifications';
export default {
mixins: [notifications],
name: 'app',
components: {
AppMenu,
@ -59,10 +78,14 @@ export default {
AppFooter,
notificationsDisplay,
snackbars,
BuyModal,
SelectMembersModal,
},
data () {
return {
isUserLoaded: false,
selectedItemToBuy: null,
selectedCardToBuy: null,
};
},
computed: {
@ -76,6 +99,10 @@ export default {
},
},
created () {
this.$root.$on('buyModal::showItem', (item) => {
this.selectedItemToBuy = item;
});
// Set up Error interceptors
axios.interceptors.response.use((response) => {
if (this.user) {
@ -112,6 +139,44 @@ export default {
});
}
},
methods: {
resetItemToBuy ($event) {
if (!$event) {
this.selectedItemToBuy = null;
}
},
resetCardToBuy ($event) {
if (!$event) {
this.selectedCardToBuy = null;
}
},
itemSelected (item) {
this.selectedItemToBuy = item;
},
genericPurchase (item) {
if (!item)
return false;
if (item.purchaseType === 'card')
return false;
return true;
},
customPurchase (item) {
if (item.purchaseType === 'card') {
if (this.user.party._id) {
this.selectedCardToBuy = item;
} else {
this.error(this.$t('errorNotInParty'));
}
}
},
memberSelected (member) {
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
this.selectedCardToBuy = null;
},
},
};
</script>

View file

@ -166,6 +166,10 @@ export default {
let allowToShowBackground = !this.avatarOnly || this.withBackground;
if (this.overrideAvatarGear && this.overrideAvatarGear.background) {
return `background_${this.overrideAvatarGear.background}`;
}
if (background && allowToShowBackground) {
return `background_${this.member.preferences.background}`;
}

View file

@ -187,7 +187,7 @@ b-modal#avatar-modal(title="", size='lg', :hide-header='true', :hide-footer='tru
strong {{set.text}}
.col-12(v-if='showPlainBackgroundBlurb(set.identifier, set.items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
.col-4.text-center.customize-option.background-button(v-for='bg in set.items',
@click='unlock("background." + bg.key)',
@click='backgroundSelected(bg)',
:popover-title='bg.text',
:popover='bg.notes',
popover-trigger='mouseenter')
@ -664,7 +664,7 @@ 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 'common/script/ops/pinnedGearUtils';
import isPinned from 'common/script/libs/isPinned';
let tasksByCategory = {
work: [
@ -998,6 +998,9 @@ export default {
this.text(this.$t('unpinnedItem', {item: bg.text}));
}
},
backgroundSelected (bg) {
this.$root.$emit('buyModal::showItem', bg);
},
},
};
</script>

View file

@ -82,7 +82,7 @@
starBadge(
:selected="true",
:show="!costume || user.preferences.costume",
@click="equip(context.item)",
@click="equipItem(context.item)",
)
div(
v-for="group in itemsGroups",

View file

@ -300,15 +300,6 @@ export default {
});
}
if (this.user.purchased.plan.consecutive.trinkets) {
specialArray.push({
key: 'timeTravelers',
class: 'inventory_special_trinket',
text: this.$t('mysticHourglassPopover'),
quantity: this.user.purchased.plan.consecutive.trinkets,
});
}
return itemsByType;
},
},

View file

@ -19,11 +19,30 @@
div.inner-content
slot(name="item", :item="item")
div(v-if="showAvatar")
avatar(
:member="user",
:avatarOnly="true",
:hideClassBadge="true",
:withBackground="true",
:overrideAvatarGear="getAvatarOverrides(item)",
:spritesMargin="'0px auto 0px -24px'",
)
item.flat.bordered-item(
:item="item",
:itemContentClass="item.class",
:showPopover="false",
v-else-if="item.key != 'gem'"
)
h4.title {{ itemText }}
div.text(v-html="itemNotes")
slot(name="additionalInfo", :item="item")
equipmentAttributesGrid.bordered(
v-if="showAttributesGrid",
:item="item"
)
div(:class="{'notEnough': !this.enoughCurrency(getPriceClass(), item.value)}")
span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]")
@ -58,6 +77,11 @@
#buy-modal {
@include centeredModal();
.avatar {
cursor: default;
margin: 0 auto;
}
.content {
text-align: center;
}
@ -169,11 +193,21 @@
import currencyMixin from './_currencyMixin';
import notifications from 'client/mixins/notifications';
import { mapState } from 'client/libs/store';
import EquipmentAttributesGrid from './market/equipmentAttributesGrid.vue';
import Item from 'client/components/inventory/item';
import Avatar from 'client/components/avatar';
export default {
mixins: [currencyMixin, notifications],
components: {
bModal,
BalanceInfo,
EquipmentAttributesGrid,
Item,
Avatar,
},
data () {
return {
@ -187,6 +221,15 @@
};
},
computed: {
...mapState({user: 'user.data'}),
showAvatar () {
return ['backgrounds', 'gear', 'mystery_set'].includes(this.item.purchaseType);
},
showAttributesGrid () {
return this.item.purchaseType === 'gear';
},
itemText () {
if (this.item.text instanceof Function) {
return this.item.text();
@ -214,7 +257,9 @@
key: this.item.key,
currency: this.item.currency,
});
this.purchased(this.item.text);
this.$root.$emit('buyModal::boughtItem', this.item);
}
this.$emit('buyPressed', this.item);
@ -224,7 +269,9 @@
this.$root.$emit('show::modal', 'buy-gems');
},
togglePinned () {
this.$emit('togglePinned', this.item);
if (!this.$store.dispatch('user:togglePinnedItem', {type: this.item.pinType, path: this.item.path})) {
this.text(this.$t('unpinnedItem', {item: this.item.text}));
}
},
hideDialog () {
this.$root.$emit('hide::modal', 'buy-modal');
@ -232,10 +279,35 @@
getPriceClass () {
if (this.priceType && this.icons[this.priceType]) {
return this.priceType;
} else if (this.item.currency && this.icons[this.item.currency]) {
return this.item.currency;
} else {
return 'gold';
}
},
getAvatarOverrides (item) {
switch (item.purchaseType) {
case 'gear':
return {
[item.type]: item.key,
};
case 'backgrounds':
return {
background: item.key,
};
case 'mystery_set': {
let gear = {};
item.items.map((setItem) => {
gear[setItem.type] = setItem.key;
});
return gear;
}
}
return {};
},
},
props: {
item: {

View file

@ -227,49 +227,6 @@
:show="true",
:count="userItems[drawerTabs[selectedDrawerTab].contentType][ctx.item.key] || 0"
)
buyModal(
:item="selectedGearToBuy",
priceType="gold",
:withPin="true",
@change="resetGearToBuy($event)",
@togglePinned="togglePinned($event)",
)
template(slot="item", scope="ctx")
div
avatar(
:member="user",
:avatarOnly="true",
:withBackground="true",
:overrideAvatarGear="memberOverrideAvatarGear(selectedGearToBuy)",
:spritesMargin="'0px auto 0px'",
)
template(slot="additionalInfo", scope="ctx")
equipmentAttributesGrid.bordered(:item="ctx.item")
buyModal(
:item="selectedItemToBuy",
:priceType="selectedItemToBuy ? selectedItemToBuy.currency : ''",
@change="resetItemToBuy($event)",
@togglePinned="togglePinned($event)",
@buyPressed="purchaseCallback($event)",
:genericPurchase="selectedItemToBuy != null && selectedItemToBuy.key != 'rebirth_orb'"
)
template(slot="item", scope="ctx")
item.flat.bordered-item(
:item="ctx.item",
:itemContentClass="ctx.item.class",
:showPopover="false",
v-if="ctx.item.key != 'gem'"
)
selectMembersModal(
:item="selectedCardToBuy",
:group="user.party",
@change="resetCardToBuy($event)",
@memberSelected="memberSelected($event)",
)
</template>
<style lang="scss">
@ -504,10 +461,6 @@ export default {
selectedSortItemsBy: 'AZ',
selectedItemToSell: null,
selectedGearToBuy: null,
selectedItemToBuy: null,
selectedCardToBuy: null,
hideLocked: false,
hidePinned: false,
@ -756,21 +709,6 @@ export default {
this.selectedItemToSell = null;
}
},
resetGearToBuy ($event) {
if (!$event) {
this.selectedGearToBuy = null;
}
},
resetItemToBuy ($event) {
if (!$event) {
this.selectedItemToBuy = null;
}
},
resetCardToBuy ($event) {
if (!$event) {
this.selectedCardToBuy = null;
}
},
isGearLocked (gear) {
if (gear.klass !== this.userStats.class) {
return true;
@ -778,47 +716,24 @@ export default {
return false;
},
memberOverrideAvatarGear (gear) {
return {
[gear.type]: gear.key,
};
},
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
itemSelected (item) {
if (item.purchaseType === 'card') {
if (this.user.party._id) {
this.selectedCardToBuy = item;
} else {
this.error(this.$t('errorNotInParty'));
}
} else {
this.selectedItemToBuy = item;
}
this.$root.$emit('buyModal::showItem', item);
},
featuredItemSelected (item) {
if (item.purchaseType === 'gear' && !item.locked) {
this.selectedGearToBuy = item;
if (item.purchaseType === 'gear') {
this.gearSelected(item);
} else {
this.itemSelected(item);
}
},
gearSelected (item) {
if (!item.locked) {
this.selectedGearToBuy = item;
}
},
memberSelected (member) {
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
this.selectedCardToBuy = null;
},
async purchaseCallback (item) {
if (item.key === 'rebirth_orb') {
await this.$store.dispatch('user:rebirth');
window.location.reload(true);
this.$root.$emit('buyModal::showItem', item);
}
},
},

View file

@ -92,7 +92,7 @@
:price="ctx.item.value",
:priceType="ctx.item.currency",
:emptyItem="false",
@click="selectedItemToBuy = ctx.item"
@click="selectItemToBuy(ctx.item)"
)
span(slot="popoverContent", scope="ctx")
div
@ -106,23 +106,6 @@
)
span.svg-icon.inline.icon-12.color(v-html="icons.pin")
buyModal(
:item="selectedItemToBuy",
:priceType="selectedItemToBuy ? selectedItemToBuy.currency : ''",
:withPin="true",
@change="resetItemToBuy($event)",
@buyPressed="buyItem($event)"
)
template(slot="item", scope="scope")
div
avatar(
:member="user",
:avatarOnly="true",
:withBackground="true",
:overrideAvatarGear="memberOverrideAvatarGear(scope.item)",
:spritesMargin="'0px auto 0px'",
)
</template>
<style lang="scss">
@ -326,8 +309,6 @@
sortItemsBy: ['AZ', 'sortByNumber'],
selectedSortItemsBy: 'AZ',
selectedItemToBuy: null,
hidePinned: false,
backgroundUpdate: new Date(),
@ -436,37 +417,19 @@
getGrouped (entries) {
return _groupBy(entries, 'group');
},
resetItemToBuy ($event) {
if (!$event) {
this.selectedItemToBuy = null;
}
},
togglePinned (item) {
if (!this.$store.dispatch('user:togglePinnedItem', {type: item.pinType, path: item.path})) {
this.$parent.showUnpinNotification(item);
}
},
buyItem (item) {
if (item.purchaseType === 'set_mystery') {
this.$store.dispatch('shops:purchaseMysterySet', {key: item.key});
} else {
this.$store.dispatch('shops:purchaseHourglassItem', {type: item.purchaseType, key: item.key});
}
this.backgroundUpdate = new Date();
},
memberOverrideAvatarGear (set) {
let gear = {};
set.items.map((item) => {
gear[item.type] = item.key;
});
return gear;
selectItemToBuy (item) {
this.$root.$emit('buyModal::showItem', item);
},
},
created () {
this.$store.dispatch('shops:fetchTimeTravelers');
this.$root.$on('buyModal::boughtItem', () => {
this.backgroundUpdate = new Date();
});
},
};
</script>

View file

@ -77,42 +77,6 @@
@openBuyDialog="openBuyDialog($event)"
)
buyModal(
:item="selectedItemToBuy",
:priceType="selectedItemToBuy ? selectedItemToBuy.currency : ''",
@change="resetItemToBuy($event)",
@buyPressed="buyItem($event)",
@togglePinned="togglePinned($event)"
)
template(slot="item", scope="ctx")
div(v-if="ctx.item.purchaseType === 'gear'")
avatar.inline(
:member="user",
:avatarOnly="true",
:withBackground="true",
:overrideAvatarGear="memberOverrideAvatarGear(ctx.item)"
)
item.flat(
:item="ctx.item",
:itemContentClass="ctx.item.class",
:showPopover="false",
v-else
)
template(slot="additionalInfo", scope="ctx")
equipmentAttributesGrid.bordered(
:item="ctx.item",
v-if="ctx.item.purchaseType === 'gear'"
)
selectMembersModal(
:item="selectedCardToBuy",
:group="user.party",
@change="resetCardToBuy($event)",
@memberSelected="memberSelectedToSendCard($event)",
)
spells
</template>
@ -323,11 +287,7 @@ import cloneDeep from 'lodash/cloneDeep';
import { mapState, mapActions } from 'client/libs/store';
import taskDefaults from 'common/script/libs/taskDefaults';
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/selectMembersModal.vue';
export default {
components: {
@ -335,12 +295,8 @@ export default {
TaskModal,
bDropdown,
bDropdownItem,
BuyModal,
Item,
Avatar,
EquipmentAttributesGrid,
spells,
SelectMembersModal,
},
directives: {
markdown,
@ -367,9 +323,6 @@ export default {
newTag: null,
editingTask: null,
creatingTask: null,
selectedItemToBuy: null,
selectedCardToBuy: null,
};
},
computed: {
@ -488,36 +441,8 @@ export default {
if (this.temporarilySelectedTags.indexOf(tagId) !== -1) return true;
return false;
},
resetItemToBuy ($event) {
if (!$event) {
this.selectedItemToBuy = null;
}
},
resetCardToBuy ($event) {
if (!$event) {
this.selectedCardToBuy = null;
}
},
memberOverrideAvatarGear (gear) {
return {
[gear.type]: gear.key,
};
},
buyItem (item) {
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});
}
},
openBuyDialog (rewardItem) {
this.selectedItemToBuy = rewardItem;
},
memberSelectedToSendCard (member) {
this.$store.dispatch('user:castSpell', {key: this.selectedCardToBuy.key, targetId: member.id});
this.selectedCardToBuy = null;
openBuyDialog (item) {
this.$root.$emit('buyModal::showItem', item);
},
},
};

View file

@ -10,7 +10,7 @@
div(v-if="items.length === 0")
p(v-once) {{ noItemsLabel }}
.btn-flat.btn-show-more(
.btn.btn-flat.btn-show-more(
@click="toggleItemsToShow()",
v-if="items.length > itemsPerRow"
) {{ showAll ? $t('showLess') : $t('showMore') }}

View file

@ -1,105 +0,0 @@
<template lang="pug">
.standard-page
h1 {{ $t('backgrounds') }}
div.customize-menu(v-for='set in backgroundShopSets')
h2 {{set.text}}
div(v-if='showPlainBackgroundBlurb(set.identifier, set.items)') {{ $t('incentiveBackgroundsUnlockedWithCheckins') }}
div(v-if='!ownsSet("background", set.items) && set.identifier !== "incentiveBackgrounds"')
//+gemCost(7)
button.btn.btn-primary(@click='unlock(setKeys("background", set.items))') {{ $t('unlockSet', {cost: 15}) }}
span.Pet_Currency_Gem1x.inline-gems
button.customize-option(v-for='bg in set.items',
type='button',
:class='[`background_${bg.key}`, backgroundLockedStatus(bg.key)]',
ng-click='unlock("background." + bg.key)',
:popover-title='bg.text',
:popover='bg.notes',
popover-trigger='mouseenter')
i.glyphicon.glyphicon-lock(ng-if='!user.purchased.background[bg.key]')
</template>
<style lang="scss" scoped>
@import '~client/assets/scss/colors.scss';
.customize-menu {
margin-top: 4em;
margin-bottom: 4em;
}
</style>
<script>
import map from 'lodash/map';
import get from 'lodash/get';
import { mapState } from 'client/libs/store';
import { getBackgroundShopSets } from '../../../common/script/libs/shops';
export default {
data () {
let backgroundShopSets = getBackgroundShopSets();
return {
backgroundShopSets,
};
},
computed: {
...mapState({user: 'user.data'}),
},
methods: {
showPlainBackgroundBlurb (identifier, set) {
return identifier === 'incentiveBackgrounds' && !this.ownsSet('background', set);
},
ownsSet (type, set) {
let setOwnedByUser = find(set, (value, key) => {
if (type === 'background') key = value.key;
return this.user.purchased[type][key];
});
return Boolean(setOwnedByUser);
},
/**
* For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase
* @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc.
* Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets
*/
unlock (path) {
let fullSet = path.indexOf(',') !== -1;
let isBackground = Boolean(path.indexOf('background.'));
let cost;
if (isBackground) {
cost = fullSet ? 3.75 : 1.75; // (Backgrounds) 15G per set, 7G per individual
} else {
cost = fullSet ? 1.25 : 0.5; // (Hair, skin, etc) 5G per set, 2G per individual
}
let loginIncentives = ['background.blue', 'background.green', 'background.red', 'background.purple', 'background.yellow', 'background.violet'];
if (!loginIncentives.contains(loginIncentives)) {
if (fullSet) {
if (confirm(window.env.t('purchaseFor', {cost: cost * 4})) !== true) return;
// @TODO: implement gem modal
// if (this.user.balance < cost) return $rootScope.openModal('buyGems');
} else if (!get(this.user, `purchased.${path}`)) {
if (confirm(window.env.t('purchaseFor', {cost: cost * 4})) !== true) return;
// @TODO: implement gem modal
// if (this.user.balance < cost) return $rootScope.openModal('buyGems');
}
}
// @TODO: Add when we implment the user calls
// User.unlock({ query:{ path: path } });
},
setKeys (type, _set) {
return map(_set, (v, k) => {
if (type === 'background') k = v.key;
return `${type}.${k}`;
}).join(',');
},
backgroundLockedStatus (bgKey) {
let backgroundClass = 'background-locked';
if (this.user.purchased.background[bgKey]) backgroundClass = 'background-unlocked';
return backgroundClass;
},
},
};
</script>

View file

@ -87,7 +87,7 @@ export default {
this.notify(`${this.sign(val)} ${this.round(val)}`, 'mp', 'glyphicon glyphicon-fire', this.sign(val));
},
purchased (itemName) {
this.notify(this.$t('purchasedItem', {
this.text(this.$t('purchasedItem', {
itemName,
}));
},

View file

@ -28,7 +28,6 @@ const VideosPage = () => import(/* webpackChunkName: "static" */'./components/st
const RegisterLogin = () => import(/* webpackChunkName: "auth" */'./components/auth/registerLogin');
// User Pages
const BackgroundsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/backgrounds');
// const StatsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/stats');
// const AchievementsPage = () => import(/* webpackChunkName: "user" */'./components/userMenu/achievements');
const ProfilePage = () => import(/* webpackChunkName: "user" */'./components/userMenu/profilePage');
@ -195,7 +194,6 @@ const router = new VueRouter({
path: '/user',
component: ParentPage,
children: [
{ name: 'backgrounds', path: 'backgrounds', component: BackgroundsPage },
{ name: 'stats', path: 'stats', component: ProfilePage },
{ name: 'achievements', path: 'achievements', component: ProfilePage },
{ name: 'profile', path: 'profile', component: ProfilePage },

View file

@ -89,7 +89,7 @@ export function purchase (store, params) {
export function purchaseMysterySet (store, params) {
const user = store.state.user.data;
let opResult = buyMysterySetOp(user, {params});
let opResult = buyMysterySetOp(user, {params, noConfirm: true});
return {
result: opResult,
@ -134,6 +134,10 @@ export function genericPurchase (store, params) {
default:
if (params.pinType === 'quests' && params.currency === 'gold') {
return buyQuestItem(store, params);
} else if (params.key === 'rebirth_orb') {
return store.dispatch('user:rebirth');
} else if (params.currency === 'hourglasses') {
return purchaseHourglassItem(store, params);
} else {
return purchase(store, params);
}

View file

@ -101,6 +101,10 @@ export function openMysteryItem () {
return axios.post('/api/v3/user/open-mystery-item');
}
export function rebirth () {
return axios.post('/api/v3/user/rebirth');
export async function rebirth () {
let result = await axios.post('/api/v3/user/rebirth');
window.location.reload(true);
return result;
}

View file

@ -23,7 +23,7 @@ module.exports = function buyMysterySet (user, req = {}, analytics) {
throw new NotFound(i18n.t('mysterySetNotFound', req.language));
}
if (typeof window !== 'undefined' && window.confirm) { // TODO move to client
if (typeof window !== 'undefined' && !req.noConfirm && window.confirm) { // TODO move to client
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) return;
}

View file

@ -17,10 +17,16 @@ function addPinnedGearByClass (user) {
for (let item of newPinnedItems) {
let itemInfo = getItemInfo(user, 'marketGear', item);
user.pinnedItems.push({
type: 'marketGear',
path: itemInfo.path,
const foundIndex = user.pinnedItems.findIndex(pinnedItem => {
return pinnedItem.path === itemInfo.path;
});
if (foundIndex === -1) {
user.pinnedItems.push({
type: 'marketGear',
path: itemInfo.path,
});
}
}
}
}

View file

@ -6,6 +6,8 @@ import {
NotAuthorized,
} from '../libs/errors';
import equip from './equip';
import { removePinnedGearByClass } from './pinnedGearUtils';
const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp'];
@ -46,6 +48,8 @@ module.exports = function rebirth (user, tasks = [], req = {}, analytics) {
}
});
removePinnedGearByClass(user);
let stats = user.stats;
stats.buffs = {};
stats.hp = 50;