mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-05-22 21:56:53 +00:00
Merge branch 'release' into develop
This commit is contained in:
commit
7b4b4240cb
20 changed files with 405 additions and 121 deletions
2
package-lock.json
generated
2
package-lock.json
generated
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "habitica",
|
||||
"version": "4.189.1",
|
||||
"version": "4.189.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 () => {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
105
website/client/src/components/shops/countdownBanner.vue
Normal file
105
website/client/src/components/shops/countdownBanner.vue
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 () {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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!",
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@
|
|||
"seasonalShopClosedTitle": "<%= linkStart %>Leslie<%= linkEnd %>",
|
||||
"seasonalShopTitle": "<%= linkStart %>Seasonal Sorceress<%= linkEnd %>",
|
||||
"seasonalShopClosedText": "The Seasonal Shop is currently closed!! It’s only open during Habitica’s four Grand Galas.",
|
||||
"seasonalShopSummerText": "Happy Summer Splash!! Would you like to buy some rare items? They’ll only be available until July 31st!",
|
||||
"seasonalShopFallText": "Happy Fall Festival!! Would you like to buy some rare items? They’ll only be available until October 31st!",
|
||||
"seasonalShopWinterText": "Happy Winter Wonderland!! Would you like to buy some rare items? They’ll only be available until January 31st!",
|
||||
"seasonalShopSpringText": "Happy Spring Fling!! Would you like to buy some rare items? They’ll 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 you’d like to gift to. From there, pick the sub length you’d 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."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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':
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue