diff --git a/test/common/ops/rebirth.js b/test/common/ops/rebirth.js index 0d4902b567..2bb46fd3be 100644 --- a/test/common/ops/rebirth.js +++ b/test/common/ops/rebirth.js @@ -49,6 +49,7 @@ describe('shared.ops.rebirth', () => { let [, message] = rebirth(user); expect(message).to.equal(i18n.t('rebirthComplete')); + expect(user.flags.lastFreeRebirth).to.exist; }); it('rebirths a user with not enough gems but more than max level', () => { @@ -60,6 +61,16 @@ describe('shared.ops.rebirth', () => { expect(message).to.equal(i18n.t('rebirthComplete')); }); + it('rebirths a user using gems if over max level but rebirthed recently', () => { + user.stats.lvl = MAX_LEVEL + 1; + user.flags.lastFreeRebirth = new Date(); + + let [, message] = rebirth(user); + + expect(message).to.equal(i18n.t('rebirthComplete')); + expect(user.balance).to.equal(0); + }); + it('resets user\'s tasks values except for rewards to 0', () => { tasks[0].value = 1; tasks[1].value = 1; diff --git a/website/client/assets/scss/colors.scss b/website/client/assets/scss/colors.scss index a21990ccca..cfc0da1d78 100644 --- a/website/client/assets/scss/colors.scss +++ b/website/client/assets/scss/colors.scss @@ -35,6 +35,7 @@ $maroon-50: #C92B2B; $maroon-100: #DE3F3F; $maroon-500: #F19595; +$yellow-5: #EE9109; $yellow-10: #FFA623; $yellow-50: #FFB445; $yellow-100: #FFBE5D; @@ -60,9 +61,6 @@ $green-50: #3FDAA2; $green-100: #5AEAB2; $green-500: #A6FFDF; -$orange-10: #ee9109; -$orange-50: #bf7d1a; - $suggested-item-color: #D5C8FF; $healer-color: #cf8229; diff --git a/website/client/assets/svg/clock-white.svg b/website/client/assets/svg/clock-white.svg new file mode 100644 index 0000000000..fdbbc32e65 --- /dev/null +++ b/website/client/assets/svg/clock-white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/client/components/shops/buyModal.vue b/website/client/components/shops/buyModal.vue index 12016ef0d8..c2f267cd07 100644 --- a/website/client/components/shops/buyModal.vue +++ b/website/client/components/shops/buyModal.vue @@ -76,9 +76,14 @@ ) {{ $t('buyNow') }} div.limitedTime(v-if="item.event && item.owned == null") - span.svg-icon.inline.icon-16(v-html="icons.clock") + span.svg-icon.inline.icon-16.clock-icon(v-html="icons.clock") span.limitedString {{ limitedString }} + .free-rebirth.d-flex.align-items-center(v-if='item.key === "rebirth_orb" && item.value > 0') + .m-auto + span.svg-icon.inline.icon-16.mr-2.pt-015(v-html="icons.whiteClock") + span(v-html='$t("nextFreeRebirth", {days: nextFreeRebirth})') + div.clearfix(slot="modal-footer") span.balance.float-left {{ $t('yourBalance') }} balanceInfo( @@ -253,6 +258,18 @@ .gems-left { margin-top: .5em; } + + .free-rebirth { + background-color: $yellow-5; + color: $white; + height: 2rem; + line-height: 16px; + margin: auto -1rem -1rem; + } + + .pt-015 { + padding-top: 0.15rem; + } } @@ -268,6 +285,7 @@ import svgHourglasses from 'assets/svg/hourglass.svg'; import svgPin from 'assets/svg/pin.svg'; import svgClock from 'assets/svg/clock.svg'; + import svgWhiteClock from 'assets/svg/clock-white.svg'; import BalanceInfo from './balanceInfo.vue'; import currencyMixin from './_currencyMixin'; @@ -308,6 +326,7 @@ hourglasses: svgHourglasses, pin: svgPin, clock: svgClock, + whiteClock: svgWhiteClock, }), selectedAmountToBuy: 1, @@ -360,6 +379,9 @@ notEnoughCurrency () { return !this.enoughCurrency(this.getPriceClass(), this.item.value * this.selectedAmountToBuy); }, + nextFreeRebirth () { + return 45 - moment().diff(moment(this.user.flags.lastFreeRebirth), 'days'); + }, }, watch: { item: function itemChanged () { diff --git a/website/common/locales/en/rebirth.json b/website/common/locales/en/rebirth.json index b39e1ff92d..b290945f78 100644 --- a/website/common/locales/en/rebirth.json +++ b/website/common/locales/en/rebirth.json @@ -25,5 +25,6 @@ "rebirthName": "Orb of Rebirth", "reborn": "Reborn, max level <%= reLevel %>", "confirmReborn": "Are you sure?", - "rebirthComplete": "You have been reborn!" + "rebirthComplete": "You have been reborn!", + "nextFreeRebirth": "<%= days %> days until FREE Orb of Rebirth" } diff --git a/website/common/script/libs/getItemInfo.js b/website/common/script/libs/getItemInfo.js index d4317fc570..2450381381 100644 --- a/website/common/script/libs/getItemInfo.js +++ b/website/common/script/libs/getItemInfo.js @@ -4,6 +4,7 @@ import { BadRequest } from './errors'; import count from '../count'; import isPinned from './isPinned'; +import isFreeRebirth from './isFreeRebirth'; import getOfficialPinnedItems from './getOfficialPinnedItems'; import _mapValues from 'lodash/mapValues'; @@ -296,7 +297,7 @@ module.exports = function getItemInfo (user, type, item, officialPinnedItems, la class: 'rebirth_orb', text: i18n.t('rebirthName'), notes: i18n.t('rebirthPop'), - value: user.stats.lvl < 100 ? 6 : 0, + value: isFreeRebirth(user) ? 0 : 6, currency: 'gems', path: 'special.rebirth_orb', pinType: 'rebirth_orb', diff --git a/website/common/script/libs/isFreeRebirth.js b/website/common/script/libs/isFreeRebirth.js new file mode 100644 index 0000000000..16a42c2c63 --- /dev/null +++ b/website/common/script/libs/isFreeRebirth.js @@ -0,0 +1,14 @@ +import moment from 'moment'; +import { MAX_LEVEL } from '../constants'; + +module.exports = function isFreeRebirth (user) { + let daysFromLastFreeRebirth = user.flags.lastFreeRebirth; + + if (daysFromLastFreeRebirth) { + daysFromLastFreeRebirth = moment().diff(moment(user.flags.lastFreeRebirth), 'days'); + } else { + daysFromLastFreeRebirth = 999; + } + + return user.stats.lvl >= MAX_LEVEL && daysFromLastFreeRebirth > 45; +}; diff --git a/website/common/script/ops/rebirth.js b/website/common/script/ops/rebirth.js index 7531de25a6..86c12d7c5d 100644 --- a/website/common/script/ops/rebirth.js +++ b/website/common/script/ops/rebirth.js @@ -7,12 +7,14 @@ import { } from '../libs/errors'; import equip from './equip'; import { removePinnedGearByClass } from './pinnedGearUtils'; - +import isFreeRebirth from '../libs/isFreeRebirth'; const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp']; module.exports = function rebirth (user, tasks = [], req = {}, analytics) { - if (user.balance < 1.5 && user.stats.lvl < MAX_LEVEL) { + const notFree = !isFreeRebirth(user); + + if (user.balance < 1.5 && notFree) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } @@ -21,7 +23,7 @@ module.exports = function rebirth (user, tasks = [], req = {}, analytics) { category: 'behavior', }; - if (user.stats.lvl < MAX_LEVEL) { + if (notFree) { user.balance -= 1.5; analyticsData.acquireMethod = 'Gems'; analyticsData.gemCost = 6; @@ -95,6 +97,10 @@ module.exports = function rebirth (user, tasks = [], req = {}, analytics) { user.achievements.rebirthLevel = lvl; } + if (!notFree) { + user.flags.lastFreeRebirth = new Date(); + } + if (user.addNotification) user.addNotification('REBIRTH_ACHIEVEMENT'); user.stats.buffs = {}; diff --git a/website/server/models/user/schema.js b/website/server/models/user/schema.js index 70d2dd58ce..0119969c12 100644 --- a/website/server/models/user/schema.js +++ b/website/server/models/user/schema.js @@ -225,6 +225,7 @@ let schema = new Schema({ classSelected: {$type: Boolean, default: false}, mathUpdates: Boolean, rebirthEnabled: {$type: Boolean, default: false}, + lastFreeRebirth: Date, levelDrops: {$type: Schema.Types.Mixed, default: () => { return {}; }},