Merge branch 'release' into develop

This commit is contained in:
Sabe Jones 2021-04-16 11:13:19 -05:00
commit 7b4b4240cb
20 changed files with 405 additions and 121 deletions

2
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "habitica",
"version": "4.189.1",
"version": "4.189.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View file

@ -1,7 +1,7 @@
{
"name": "habitica",
"description": "A habit tracker app which treats your goals like a Role Playing Game.",
"version": "4.189.1",
"version": "4.189.2",
"main": "./website/server/index.js",
"dependencies": {
"@babel/core": "^7.12.10",

View file

@ -70,6 +70,7 @@ describe('POST /user/buy/:key', () => {
it('buys a special spell', async () => {
const key = 'spookySparkles';
const item = content.special[key];
const stub = sinon.stub(item, 'canOwn').returns(true);
await user.update({ 'stats.gp': 250 });
const res = await user.post(`/user/buy/${key}`);
@ -82,6 +83,8 @@ describe('POST /user/buy/:key', () => {
expect(res.message).to.equal(t('messageBought', {
itemText: item.text(),
}));
stub.restore();
});
it('allows for bulk purchases', async () => {

View file

@ -161,16 +161,10 @@
</button>
</div>
</div>
<div
<countdown-banner
v-if="item.event && item.owned == null"
class="limitedTime"
>
<span
class="svg-icon inline icon-16 clock-icon"
v-html="icons.clock"
></span>
<span class="limitedString">{{ limitedString }}</span>
</div>
:endDate = "endDate"
/>
<div
v-if="item.key === 'rebirth_orb' && item.value > 0 && user.stats.lvl >= 100"
class="free-rebirth d-flex align-items-center"
@ -324,27 +318,6 @@
opacity: 0.55;
}
.limitedTime {
height: 32px;
background-color: $purple-300;
width: calc(100% + 30px);
margin: 0 -15px; // the modal content has its own padding
font-size: 12px;
line-height: 1.33;
text-align: center;
color: $white;
display: flex;
align-items: center;
justify-content: center;
.limitedString {
height: 16px;
margin-left: 8px;
}
}
.attributesGrid {
margin-top: 8px;
border-radius: 2px;
@ -399,6 +372,7 @@ import svgWhiteClock from '@/assets/svg/clock-white.svg';
import BalanceInfo from './balanceInfo.vue';
import PinBadge from '@/components/ui/pinBadge';
import CountdownBanner from './countdownBanner';
import currencyMixin from './_currencyMixin';
import notifications from '@/mixins/notifications';
import buyMixin from '@/mixins/buy';
@ -432,6 +406,7 @@ export default {
Item,
Avatar,
PinBadge,
CountdownBanner,
},
mixins: [buyMixin, currencyMixin, notifications, numberInvalid, spellsMixin],
props: {
@ -462,6 +437,7 @@ export default {
selectedAmountToBuy: 1,
isPinned: false,
endDate: seasonalShopConfig.dateRange.end,
};
},
computed: {
@ -494,9 +470,6 @@ export default {
}
return this.item.notes;
},
limitedString () {
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
},
gemsLeft () {
if (!this.user.purchased.plan) return 0;
return planGemLimits.convCap

View file

@ -0,0 +1,105 @@
<template>
<div
class="limitedTime"
:class="availabilityClass"
>
<span
class="svg-icon inline icon-16"
v-html="availabilityClass === 'expired' ? icons.clockWhite : icons.clock"
></span>
<span class="limitedString"> {{ limitedString }} </span>
</div>
</template>
<style lang="scss" scoped>
@import '~@/assets/scss/colors.scss';
.limitedTime {
height: 32px;
width: calc(100% + 30px);
margin: 0 -15px; // the modal content has its own padding
font-size: 12px;
line-height: 1.33;
text-align: center;
color: $white;
display: flex;
align-items: center;
justify-content: center;
.limitedString {
height: 16px;
margin-left: 8px;
}
}
.available {
background-color: $purple-300;
}
.expired {
background-color: $gray-200;
}
</style>
<script>
import moment from 'moment';
import svgClock from '@/assets/svg/clock.svg';
import clockWhite from '@/assets/svg/clock-white.svg';
export default {
props: {
endDate: {
type: Object, // moment
},
},
data () {
return {
icons: Object.freeze({
clock: svgClock,
clockWhite,
}),
timer: '',
limitedString: '',
availabilityClass: 'available',
};
},
mounted () {
this.countdownString();
this.timer = setInterval(this.countdownString, 1000);
},
methods: {
countdownString () {
const diffDuration = moment.duration(moment(this.endDate).diff(moment()));
if (moment(this.endDate).isBefore()) {
this.limitedString = this.$t('noLongerAvailable');
this.availabilityClass = 'expired';
this.cancelAutoUpdate();
} else if (diffDuration.days() > 0) {
this.limitedString = this.$t('limitedAvailabilityDays', {
days: diffDuration.days(),
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else if (diffDuration.asMinutes() > 2) {
this.limitedString = this.$t('limitedAvailabilityHours', {
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else {
this.limitedString = this.$t('limitedAvailabilityMinutes', {
minutes: diffDuration.minutes(),
seconds: diffDuration.seconds(),
});
}
},
cancelAutoUpdate () {
clearInterval(this.timer);
},
},
beforeDestroy () {
this.cancelAutoUpdate();
},
};
</script>

View file

@ -48,6 +48,12 @@ export default {
},
mixins: [pinUtils],
props: ['hideLocked', 'hidePinned', 'searchBy', 'sortBy', 'category'],
data () {
return {
timer: '',
limitedString: '',
};
},
computed: {
...mapState({
content: 'content',
@ -60,9 +66,6 @@ export default {
return planGemLimits.convCap
+ this.user.purchased.plan.consecutive.gemCapExtra - this.user.purchased.plan.gemsBought;
},
limitedString () {
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
},
sortedMarketItems () {
let result = _map(this.category.items, e => ({
...e,
@ -103,10 +106,43 @@ export default {
return result;
},
},
mounted () {
this.countdownString();
this.timer = setInterval(this.countdownString, 1000);
},
methods: {
itemSelected (item) {
this.$root.$emit('buyModal::showItem', item);
},
countdownString () {
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
if (diffDuration.asSeconds() <= 0) {
this.limitedString = this.$t('noLongerAvailable');
} else if (diffDuration.days() > 0) {
this.limitedString = this.$t('limitedAvailabilityDays', {
days: diffDuration.days(),
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else if (diffDuration.asMinutes() > 2) {
this.limitedString = this.$t('limitedAvailabilityHours', {
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else {
this.limitedString = this.$t('limitedAvailabilityMinutes', {
minutes: diffDuration.minutes(),
seconds: diffDuration.seconds(),
});
}
},
cancelAutoUpdate () {
clearInterval(this.timer);
},
},
beforeDestroy () {
this.cancelAutoUpdate();
},
};
</script>

View file

@ -84,16 +84,10 @@
>
<questDialogDrops :item="item" />
</div>
<div
<countdown-banner
v-if="item.event"
class="limitedTime"
>
<span
class="svg-icon inline icon-16 clock-icon"
v-html="icons.clock"
></span>
<span class="limitedString">{{ limitedString }}</span>
</div>
:endDate="endDate"
/>
<div
slot="modal-footer"
class="clearfix"
@ -208,27 +202,6 @@
display: block;
}
.limitedTime {
height: 32px;
background-color: $purple-300;
width: calc(100% + 30px);
margin: 0 -15px; // the modal content has its own padding
font-size: 12px;
line-height: 1.33;
text-align: center;
color: $white;
display: flex;
align-items: center;
justify-content: center;
.limitedString {
height: 16px;
margin-left: 8px;
}
}
.notEnough {
pointer-events: none;
opacity: 0.55;
@ -268,7 +241,6 @@
</style>
<script>
import moment from 'moment';
import { mapState } from '@/libs/store';
import seasonalShopConfig from '@/../../common/script/libs/shops-seasonal.config';
@ -285,6 +257,7 @@ import notifications from '@/mixins/notifications';
import buyMixin from '@/mixins/buy';
import numberInvalid from '@/mixins/numberInvalid';
import PinBadge from '@/components/ui/pinBadge';
import CountdownBanner from '../countdownBanner';
import questDialogDrops from './questDialogDrops';
import questDialogContent from './questDialogContent';
@ -295,6 +268,7 @@ export default {
PinBadge,
questDialogDrops,
questDialogContent,
CountdownBanner,
},
mixins: [buyMixin, currencyMixin, notifications, numberInvalid],
props: {
@ -321,6 +295,7 @@ export default {
isPinned: false,
selectedAmountToBuy: 1,
endDate: seasonalShopConfig.dateRange.end,
};
},
computed: {
@ -344,9 +319,6 @@ export default {
if (this.priceType === 'hourglasses') return this.icons.hourglass;
return this.icons.gem;
},
limitedString () {
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
},
},
watch: {
item: function itemChanged () {

View file

@ -36,7 +36,7 @@
</dd>
</div>
</div>
<div v-if="quest.event && popoverVersion">
<div v-if="quest.event">
{{ limitedString }}
</div>
</div>
@ -131,10 +131,6 @@ export default {
quest: {
type: Object,
},
popoverVersion: {
type: Boolean,
default: false,
},
},
data () {
return {
@ -143,6 +139,8 @@ export default {
starHalf: svgStarHalf,
starEmpty: svgStarEmpty,
}),
timer: '',
limitedString: '',
};
},
computed: {
@ -153,9 +151,10 @@ export default {
return 1;
},
limitedString () {
return this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
},
},
mounted () {
this.countdownString();
this.timer = setInterval(this.countdownString, 1000);
},
methods: {
stars () {
@ -182,6 +181,35 @@ export default {
}
return collect.text;
},
countdownString () {
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
if (diffDuration.asSeconds() <= 0) {
this.limitedString = this.$t('noLongerAvailable');
} else if (diffDuration.days() > 0) {
this.limitedString = this.$t('limitedAvailabilityDays', {
days: diffDuration.days(),
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else if (diffDuration.asMinutes() > 2) {
this.limitedString = this.$t('limitedAvailabilityHours', {
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else {
this.limitedString = this.$t('limitedAvailabilityMinutes', {
minutes: diffDuration.minutes(),
seconds: diffDuration.seconds(),
});
}
},
cancelAutoUpdate () {
clearInterval(this.timer);
},
},
beforeDestroy () {
this.cancelAutoUpdate();
},
};
</script>

View file

@ -107,7 +107,7 @@
</div>
</div>
<div
v-if="item.event"
v-if="item.event && item.purchaseType !== 'quests'"
:class="item.purchaseType === 'gear' ? 'mt-4' : 'mt-2'"
>
{{ limitedString }}
@ -291,16 +291,18 @@ export default {
},
},
data () {
return Object.freeze({
return {
itemId: uuid(),
icons: {
icons: Object.freeze({
gems: svgGem,
gold: svgGold,
lock: svgLock,
hourglasses: svgHourglasses,
clock: svgClock,
},
});
}),
timer: '',
limitedString: '',
};
},
computed: {
showNotes () {
@ -314,10 +316,10 @@ export default {
}
return 'gold';
},
limitedString () {
return this.item.owned === false ? ''
: this.$t('limitedOffer', { date: moment(seasonalShopConfig.dateRange.end).format('LL') });
},
},
mounted () {
this.countdownString();
this.timer = setInterval(this.countdownString, 1000);
},
methods: {
click () {
@ -338,6 +340,35 @@ export default {
locked: this.item.locked,
};
},
countdownString () {
const diffDuration = moment.duration(moment(seasonalShopConfig.dateRange.end).diff(moment()));
if (diffDuration.asSeconds() <= 0) {
this.limitedString = this.$t('noLongerAvailable');
} else if (diffDuration.days() > 0) {
this.limitedString = this.$t('limitedAvailabilityDays', {
days: diffDuration.days(),
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else if (diffDuration.asMinutes() > 2) {
this.limitedString = this.$t('limitedAvailabilityHours', {
hours: diffDuration.hours(),
minutes: diffDuration.minutes(),
});
} else {
this.limitedString = this.$t('limitedAvailabilityMinutes', {
minutes: diffDuration.minutes(),
seconds: diffDuration.seconds(),
});
}
},
cancelAutoUpdate () {
clearInterval(this.timer);
},
},
beforeDestroy () {
this.cancelAutoUpdate();
},
};
</script>

View file

@ -104,7 +104,7 @@
"achievementRedLetterDayText": "Has tamed all Red Mounts.",
"achievementRedLetterDayModalText": "You tamed all the Red Mounts!",
"achievementLegendaryBestiary": "Legendary Bestiary",
"achievementLegendaryBestiaryText": "Has hatched all the mythical pets: Dragon, Flying Pig, Gryphon, Sea Serpent, and Unicorn!",
"achievementLegendaryBestiaryText": "Has hatched all standard colors of mythical pets: Dragon, Flying Pig, Gryphon, Sea Serpent, and Unicorn!",
"achievementLegendaryBestiaryModalText": "You collected all the mythical pets!",
"achievementSeasonalSpecialist": "Seasonal Specialist",
"achievementSeasonalSpecialistText": "Has completed all the Spring and Winter seasonal quests: Egg Hunt, Trapper Santa, and Find the Cub!",

View file

@ -28,10 +28,10 @@
"seasonalShopClosedTitle": "<%= linkStart %>Leslie<%= linkEnd %>",
"seasonalShopTitle": "<%= linkStart %>Seasonal Sorceress<%= linkEnd %>",
"seasonalShopClosedText": "The Seasonal Shop is currently closed!! Its only open during Habiticas four Grand Galas.",
"seasonalShopSummerText": "Happy Summer Splash!! Would you like to buy some rare items? Theyll only be available until July 31st!",
"seasonalShopFallText": "Happy Fall Festival!! Would you like to buy some rare items? Theyll only be available until October 31st!",
"seasonalShopWinterText": "Happy Winter Wonderland!! Would you like to buy some rare items? Theyll only be available until January 31st!",
"seasonalShopSpringText": "Happy Spring Fling!! Would you like to buy some rare items? Theyll only be available until April 30th!",
"seasonalShopSummerText": "Happy Summer Splash!! Would you like to buy some rare items? Be sure to get them before the Gala ends!",
"seasonalShopFallText": "Happy Fall Festival!! Would you like to buy some rare items? Be sure to get them before the Gala ends!",
"seasonalShopWinterText": "Happy Winter Wonderland!! Would you like to buy some rare items? Be sure to get them before the Gala ends!",
"seasonalShopSpringText": "Happy Spring Fling!! Would you like to buy some rare items? Be sure to get them before the Gala ends!",
"seasonalShopFallTextBroken": "Oh.... Welcome to the Seasonal Shop... We're stocking autumn Seasonal Edition goodies, or something... Everything here will be available to purchase during the Fall Festival event each year, but we're only open until October 31... I guess you should to stock up now, or you'll have to wait... and wait... and wait... <strong>*sigh*</strong>",
"seasonalShopBrokenText": "My pavilion!!!!!!! My decorations!!!! Oh, the Dysheartener's destroyed everything :( Please help defeat it in the Tavern so I can rebuild!",
"seasonalShopRebirth": "If you bought any of this equipment in the past but don't currently own it, you can repurchase it in the Rewards Column. Initially, you'll only be able to purchase the items for your current class (Warrior by default), but fear not, the other class-specific items will become available if you switch to that class.",
@ -199,5 +199,6 @@
"howItWorks": "How it Works",
"g1g1HowItWorks": "Type in the username of the account youd like to gift to. From there, pick the sub length youd like to gift and check out. Your account will automatically be rewarded with the same level of subscription you just gifted.",
"limitations": "Limitations",
"g1g1Limitations": "This is a limited time event that starts on December 17th at 8:00 AM ET (13:00 UTC) and will end January 7th at 8:00 PM ET (1:00 UTC). This promotion only applies when you gift to another Habitican. If you or your gift recipient already have a subscription, the gifted subscription will add months of credit that will only be used after the current subscription is canceled or expires."
"g1g1Limitations": "This is a limited time event that starts on December 17th at 8:00 AM ET (13:00 UTC) and will end January 7th at 8:00 PM ET (1:00 UTC). This promotion only applies when you gift to another Habitican. If you or your gift recipient already have a subscription, the gifted subscription will add months of credit that will only be used after the current subscription is canceled or expires.",
"noLongerAvailable": "This item is no longer available."
}

View file

@ -125,5 +125,8 @@
"welcome3": "Progress in life and the game!",
"welcome3notes": "As you improve your life, your avatar will level up and unlock pets, quests, equipment, and more!",
"imReady": "Enter Habitica",
"limitedOffer": "Available until <%= date %>"
"limitedOffer": "Available until <%= date %>",
"limitedAvailabilityDays": "Available for <%= days %>d <%= hours %>h <%= minutes %>m",
"limitedAvailabilityHours": "Available for <%= hours %>h <%= minutes %>m",
"limitedAvailabilityMinutes": "Available for <%= minutes %>m <%= seconds %>s"
}

View file

@ -164,18 +164,22 @@ const armor = {
},
springRogue: {
set: 'stealthyKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springWarrior: {
set: 'mightyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springMage: {
set: 'magicMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springHealer: {
set: 'lovingPupSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summerRogue: {
@ -234,18 +238,22 @@ const armor = {
},
spring2015Rogue: {
set: 'sneakySqueakerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Warrior: {
set: 'bewareDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Mage: {
set: 'magicianBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Healer: {
set: 'comfortingKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2015Rogue: {
@ -310,18 +318,22 @@ const armor = {
},
spring2016Rogue: {
set: 'cleverDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Warrior: {
set: 'braveMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Mage: {
set: 'grandMalkinSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Healer: {
set: 'springingBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2016Rogue: {
@ -380,18 +392,22 @@ const armor = {
},
spring2017Rogue: {
set: 'spring2017SneakyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Warrior: {
set: 'spring2017FelineWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Mage: {
set: 'spring2017CanineConjurorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Healer: {
set: 'spring2017FloralMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2017Rogue: {
@ -450,18 +466,22 @@ const armor = {
},
spring2018Rogue: {
set: 'spring2018DucklingRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Warrior: {
set: 'spring2018SunriseWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Mage: {
set: 'spring2018TulipMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Healer: {
set: 'spring2018GarnetHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2018Rogue: {
@ -526,18 +546,22 @@ const armor = {
},
spring2019Rogue: {
set: 'spring2019CloudRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Warrior: {
set: 'spring2019OrchidWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Mage: {
set: 'spring2019AmberMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Healer: {
set: 'spring2019RobinHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2019Rogue: {
@ -603,18 +627,22 @@ const armor = {
},
spring2020Rogue: {
set: 'spring2020LapisLazuliRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Warrior: {
set: 'spring2020BeetleWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Mage: {
set: 'spring2020PuddleMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Healer: {
set: 'spring2020IrisHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2020Rogue: {
@ -1171,18 +1199,22 @@ const head = {
},
springRogue: {
set: 'stealthyKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springWarrior: {
set: 'mightyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springMage: {
set: 'magicMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springHealer: {
set: 'lovingPupSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summerRogue: {
@ -1241,18 +1273,22 @@ const head = {
},
spring2015Rogue: {
set: 'sneakySqueakerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Warrior: {
set: 'bewareDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Mage: {
set: 'magicianBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Healer: {
set: 'comfortingKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2015Rogue: {
@ -1317,18 +1353,22 @@ const head = {
},
spring2016Rogue: {
set: 'cleverDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Warrior: {
set: 'braveMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Mage: {
set: 'grandMalkinSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Healer: {
set: 'springingBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2016Rogue: {
@ -1387,18 +1427,22 @@ const head = {
},
spring2017Rogue: {
set: 'spring2017SneakyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Warrior: {
set: 'spring2017FelineWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Mage: {
set: 'spring2017CanineConjurorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Healer: {
set: 'spring2017FloralMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2017Rogue: {
@ -1463,18 +1507,22 @@ const head = {
},
spring2018Rogue: {
set: 'spring2018DucklingRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Warrior: {
set: 'spring2018SunriseWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Mage: {
set: 'spring2018TulipMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Healer: {
set: 'spring2018GarnetHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2018Rogue: {
@ -1545,18 +1593,22 @@ const head = {
},
spring2019Rogue: {
set: 'spring2019CloudRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Warrior: {
set: 'spring2019OrchidWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Mage: {
set: 'spring2019AmberMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Healer: {
set: 'spring2019RobinHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2019Rogue: {
@ -1622,18 +1674,22 @@ const head = {
},
spring2020Rogue: {
set: 'spring2020LapisLazuliRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Warrior: {
set: 'spring2020BeetleWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Mage: {
set: 'spring2020PuddleMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Healer: {
set: 'spring2020IrisHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2020Rogue: {
@ -1720,75 +1776,75 @@ Object.keys(gearEvents).forEach(event => {
const headAccessory = {
springRogue: {
event: EVENTS.spring,
specialClass: 'rogue',
set: 'stealthyKittySet',
text: t('headAccessorySpecialSpringRogueText'),
notes: t('headAccessorySpecialSpringRogueNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springWarrior: {
event: EVENTS.spring,
specialClass: 'warrior',
set: 'mightyBunnySet',
text: t('headAccessorySpecialSpringWarriorText'),
notes: t('headAccessorySpecialSpringWarriorNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springMage: {
event: EVENTS.spring,
specialClass: 'wizard',
set: 'magicMouseSet',
text: t('headAccessorySpecialSpringMageText'),
notes: t('headAccessorySpecialSpringMageNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springHealer: {
event: EVENTS.spring,
specialClass: 'healer',
set: 'lovingPupSet',
text: t('headAccessorySpecialSpringHealerText'),
notes: t('headAccessorySpecialSpringHealerNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Rogue: {
event: EVENTS.spring2015,
specialClass: 'rogue',
set: 'sneakySqueakerSet',
text: t('headAccessorySpecialSpring2015RogueText'),
notes: t('headAccessorySpecialSpring2015RogueNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Warrior: {
event: EVENTS.spring2015,
specialClass: 'warrior',
set: 'bewareDogSet',
text: t('headAccessorySpecialSpring2015WarriorText'),
notes: t('headAccessorySpecialSpring2015WarriorNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Mage: {
event: EVENTS.spring2015,
specialClass: 'wizard',
set: 'magicianBunnySet',
text: t('headAccessorySpecialSpring2015MageText'),
notes: t('headAccessorySpecialSpring2015MageNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Healer: {
event: EVENTS.spring2015,
specialClass: 'healer',
set: 'comfortingKittySet',
text: t('headAccessorySpecialSpring2015HealerText'),
notes: t('headAccessorySpecialSpring2015HealerNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
bearEars: {
@ -1856,75 +1912,75 @@ const headAccessory = {
canBuy: () => true,
},
spring2016Rogue: {
event: EVENTS.spring2016,
specialClass: 'rogue',
set: 'cleverDogSet',
text: t('headAccessorySpecialSpring2016RogueText'),
notes: t('headAccessorySpecialSpring2016RogueNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Warrior: {
event: EVENTS.spring2016,
specialClass: 'warrior',
set: 'braveMouseSet',
text: t('headAccessorySpecialSpring2016WarriorText'),
notes: t('headAccessorySpecialSpring2016WarriorNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Mage: {
event: EVENTS.spring2016,
specialClass: 'wizard',
set: 'grandMalkinSet',
text: t('headAccessorySpecialSpring2016MageText'),
notes: t('headAccessorySpecialSpring2016MageNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Healer: {
event: EVENTS.spring2016,
specialClass: 'healer',
set: 'springingBunnySet',
text: t('headAccessorySpecialSpring2016HealerText'),
notes: t('headAccessorySpecialSpring2016HealerNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Rogue: {
event: EVENTS.spring2017,
specialClass: 'rogue',
set: 'spring2017SneakyBunnySet',
text: t('headAccessorySpecialSpring2017RogueText'),
notes: t('headAccessorySpecialSpring2017RogueNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Warrior: {
event: EVENTS.spring2017,
specialClass: 'warrior',
set: 'spring2017FelineWarriorSet',
text: t('headAccessorySpecialSpring2017WarriorText'),
notes: t('headAccessorySpecialSpring2017WarriorNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Mage: {
event: EVENTS.spring2017,
specialClass: 'wizard',
set: 'spring2017CanineConjurorSet',
text: t('headAccessorySpecialSpring2017MageText'),
notes: t('headAccessorySpecialSpring2017MageNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Healer: {
event: EVENTS.spring2017,
specialClass: 'healer',
set: 'spring2017FloralMouseSet',
text: t('headAccessorySpecialSpring2017HealerText'),
notes: t('headAccessorySpecialSpring2017HealerNotes'),
value: 20,
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
blackHeadband: {
@ -2071,14 +2127,17 @@ const shield = {
},
springRogue: {
set: 'stealthyKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springWarrior: {
set: 'mightyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springHealer: {
set: 'lovingPupSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summerRogue: {
@ -2119,14 +2178,17 @@ const shield = {
},
spring2015Rogue: {
set: 'sneakySqueakerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Warrior: {
set: 'bewareDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Healer: {
set: 'comfortingKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2015Rogue: {
@ -2167,14 +2229,17 @@ const shield = {
},
spring2016Rogue: {
set: 'cleverDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Warrior: {
set: 'braveMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Healer: {
set: 'springingBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2016Rogue: {
@ -2215,14 +2280,17 @@ const shield = {
},
spring2017Rogue: {
set: 'spring2017SneakyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Warrior: {
set: 'spring2017FelineWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Healer: {
set: 'spring2017FloralMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2017Rogue: {
@ -2263,14 +2331,17 @@ const shield = {
},
spring2018Rogue: {
set: 'spring2018DucklingRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Warrior: {
set: 'spring2018SunriseWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Healer: {
set: 'spring2018GarnetHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2018Rogue: {
@ -2319,14 +2390,17 @@ const shield = {
},
spring2019Rogue: {
set: 'spring2019CloudRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Warrior: {
set: 'spring2019OrchidWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Healer: {
set: 'spring2019RobinHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2019Rogue: {
@ -2384,14 +2458,17 @@ const shield = {
},
spring2020Rogue: {
set: 'spring2020LapisLazuliRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Warrior: {
set: 'spring2020BeetleWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Healer: {
set: 'spring2020IrisHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2020Warrior: {
@ -2604,18 +2681,22 @@ const weapon = {
},
springRogue: {
set: 'stealthyKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springWarrior: {
set: 'mightyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springMage: {
set: 'magicMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
springHealer: {
set: 'lovingPupSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summerRogue: {
@ -2668,18 +2749,22 @@ const weapon = {
},
spring2015Rogue: {
set: 'sneakySqueakerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Warrior: {
set: 'bewareDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Mage: {
set: 'magicianBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2015Healer: {
set: 'comfortingKittySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2015Rogue: {
@ -2732,18 +2817,22 @@ const weapon = {
},
spring2016Rogue: {
set: 'cleverDogSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Warrior: {
set: 'braveMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Mage: {
set: 'grandMalkinSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2016Healer: {
set: 'springingBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2016Rogue: {
@ -2796,18 +2885,22 @@ const weapon = {
},
spring2017Rogue: {
set: 'spring2017SneakyBunnySet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Warrior: {
set: 'spring2017FelineWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Mage: {
set: 'spring2017CanineConjurorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2017Healer: {
set: 'spring2017FloralMouseSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2017Rogue: {
@ -2860,18 +2953,22 @@ const weapon = {
},
spring2018Rogue: {
set: 'spring2018DucklingRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Warrior: {
set: 'spring2018SunriseWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Mage: {
set: 'spring2018TulipMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2018Healer: {
set: 'spring2018GarnetHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2018Rogue: {
@ -2924,18 +3021,22 @@ const weapon = {
},
spring2019Rogue: {
set: 'spring2019CloudRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Warrior: {
set: 'spring2019OrchidWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Mage: {
set: 'spring2019AmberMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2019Healer: {
set: 'spring2019RobinHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2019Rogue: {
@ -3003,18 +3104,22 @@ const weapon = {
},
spring2020Rogue: {
set: 'spring2020LapisLazuliRogueSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Warrior: {
set: 'spring2020BeetleWarriorSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Mage: {
set: 'spring2020PuddleMageSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
spring2020Healer: {
set: 'spring2020IrisHealerSet',
event: EVENTS.spring2021,
canBuy: () => CURRENT_EVENT && CURRENT_EVENT.season === 'spring',
},
summer2020Rogue: {

View file

@ -197,7 +197,7 @@ const premium = {
}),
event: EVENTS.spring2021,
canBuy () {
return moment().isBefore('2021-04-30T20:00-04:00');
return moment().isBefore(EVENTS.spring2021.end);
},
},
Glass: {
@ -353,7 +353,7 @@ const premium = {
previousDate: t('marchYYYY', { year: 2020 }),
}),
canBuy () {
return moment().isBefore('2021-04-30T20:00-04:00');
return moment().isBefore(EVENTS.spring2021.end);
},
},
Fluorite: {
@ -440,7 +440,7 @@ const premium = {
date: t('dateEndMarch'),
}),
canBuy () {
return moment().isBefore('2021-04-30T20:00-04:00');
return moment().isBefore(EVENTS.spring2021.end);
},
},
};
@ -455,7 +455,7 @@ const wacky = {
previousDate: t('marchYYYY', { year: 2019 }),
}),
canBuy () {
return moment().isBetween('2021-04-01T08:00-05:00', '2021-04-30T20:00-05:00');
return moment().isBetween('2021-04-01T08:00-05:00', EVENTS.spring2021.end);
},
},
Dessert: {

View file

@ -518,8 +518,9 @@ const quests = {
completion: t('questEggHuntCompletion'),
value: 1,
category: 'pet',
event: EVENTS.spring2021,
canBuy () {
return moment().isBefore('2021-04-30T20:00-05:00');
return moment().isBefore(EVENTS.spring2021.end);
},
collect: {
plainEgg: {
@ -3587,7 +3588,7 @@ const quests = {
category: 'hatchingPotion',
event: EVENTS.spring2021,
canBuy () {
return moment().isBetween('2021-04-01T08:00-05:00', '2021-04-30T20:00-05:00');
return moment().isBefore(EVENTS.spring2021.end);
},
boss: {
name: t('questWaffleBoss'),

View file

@ -1,10 +1,12 @@
import each from 'lodash/each';
import moment from 'moment';
import t from './translation';
import { NotAuthorized, BadRequest } from '../libs/errors';
import statsComputed from '../libs/statsComputed'; // eslint-disable-line import/no-cycle
import setDebuffPotionItems from '../libs/setDebuffPotionItems'; // eslint-disable-line import/no-cycle
import crit from '../fns/crit'; // eslint-disable-line import/no-cycle
import updateStats from '../fns/updateStats';
import { EVENTS } from './constants';
/*
---------------------------------------------------------------
@ -286,6 +288,9 @@ spells.special = {
previousPurchase: true,
target: 'user',
notes: t('spellSpecialSnowballAuraNotes'),
canOwn () {
return false;
},
cast (user, target, req) {
if (!user.items.special.snowball) throw new NotAuthorized(t('spellNotOwned')(req.language));
target.stats.buffs.snowball = true;
@ -319,6 +324,9 @@ spells.special = {
previousPurchase: true,
target: 'user',
notes: t('spellSpecialSpookySparklesNotes'),
canOwn () {
return false;
},
cast (user, target, req) {
if (!user.items.special.spookySparkles) throw new NotAuthorized(t('spellNotOwned')(req.language));
target.stats.buffs.snowball = false;
@ -352,6 +360,10 @@ spells.special = {
previousPurchase: true,
target: 'user',
notes: t('spellSpecialShinySeedNotes'),
event: EVENTS.spring2021,
canOwn () {
return moment().isBetween('2021-04-06T08:00-05:00', EVENTS.spring2021.end);
},
cast (user, target, req) {
if (!user.items.special.shinySeed) throw new NotAuthorized(t('spellNotOwned')(req.language));
target.stats.buffs.snowball = false;
@ -385,6 +397,9 @@ spells.special = {
previousPurchase: true,
target: 'user',
notes: t('spellSpecialSeafoamNotes'),
canOwn () {
return false;
},
cast (user, target, req) {
if (!user.items.special.seafoam) throw new NotAuthorized(t('spellNotOwned')(req.language));
target.stats.buffs.snowball = false;
@ -419,6 +434,9 @@ spells.special = {
silent: true,
target: 'user',
notes: t('nyeCardNotes'),
canOwn () {
return false;
},
cast (user, target) {
if (user === target) {
if (!user.achievements.nye) user.achievements.nye = 0;
@ -456,6 +474,9 @@ spells.special = {
silent: true,
target: 'user',
notes: t('valentineCardNotes'),
canOwn () {
return false;
},
cast (user, target) {
if (user === target) {
if (!user.achievements.valentine) user.achievements.valentine = 0;

View file

@ -178,6 +178,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang
class: `inventory_special_${item.key}`,
path: `spells.special.${item.key}`,
pinType: 'seasonalSpell',
event: item.event,
};
break;
case 'debuffPotion':
@ -215,6 +216,7 @@ export default function getItemInfo (user, type, item, officialPinnedItems, lang
purchaseType: 'quests',
path: `quests.${item.key}`,
pinType: 'seasonalQuest',
event: item.event,
};
break;
case 'gear':

View file

@ -18,8 +18,8 @@ export default {
currentSeason: SHOP_OPEN ? upperFirst(CURRENT_EVENT.season) : 'Closed',
dateRange: {
start: SHOP_OPEN ? moment(CURRENT_EVENT.start).format('YYYY-MM-DD') : moment().subtract(1, 'days').toDate(),
end: SHOP_OPEN ? moment(CURRENT_EVENT.end).format('YYYY-MM-DD') : moment().subtract(1, 'seconds').toDate(),
start: SHOP_OPEN ? moment(CURRENT_EVENT.start) : moment().subtract(1, 'days').toDate(),
end: SHOP_OPEN ? moment(CURRENT_EVENT.end) : moment().subtract(1, 'seconds').toDate(),
},
availableSets: SHOP_OPEN
@ -37,7 +37,7 @@ export default {
}
: {},
availableSpells: moment().isBetween('2021-04-06T08:00-05:00', '2021-04-30T20:00-05:00')
availableSpells: SHOP_OPEN && moment().isAfter('2021-04-06T08:00-05:00')
? [
'shinySeed',
]

View file

@ -157,6 +157,7 @@ shops.getMarketGearCategories = function getMarketGear (user, language) {
if (
gearItem.specialClass === classType
&& user.items.gear.owned[gearItem.key] !== false
&& gearItem.set === seasonalShopConfig.pinnedSets[gearItem.specialClass]
) return gearItem.canOwn(classShift);
return false;
});

View file

@ -100,6 +100,8 @@ function _formatUserData (user) {
if (user.purchased && user.purchased.plan.planId) {
properties.subscription = user.purchased.plan.planId;
} else {
properties.subscription = null;
}
if (user._ABtests) {