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 {};
}},