feat(event): Habit Birthday 2017
109
migrations/20170131_habit_birthday.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
var migrationName = '20170131_habit_birthday.js';
|
||||||
|
var authorName = 'Sabe'; // in case script author needs to know when their ...
|
||||||
|
var authorUuid = '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Award 2017 party robes if user has 2016 robes, 2016 robes if they have the 2015 robes,
|
||||||
|
* 2015 robes if they have the 2014 robes, and 2014 robes otherwise. Also cake!
|
||||||
|
*/
|
||||||
|
|
||||||
|
var monk = require('monk');
|
||||||
|
var connectionString = 'mongodb://localhost:27017/habitrpg?auto_reconnect=true'; // FOR TEST DATABASE
|
||||||
|
var dbUsers = monk(connectionString).get('users', { castIds: false });
|
||||||
|
|
||||||
|
function processUsers(lastId) {
|
||||||
|
// specify a query to limit the affected users (empty for all users):
|
||||||
|
var query = {
|
||||||
|
'migration':{$ne:migrationName},
|
||||||
|
// 'auth.timestamps.loggedin':{$gt:new Date('2017-01-24')}, // remove after first run to cover remaining users
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lastId) {
|
||||||
|
query._id = {
|
||||||
|
$gt: lastId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dbUsers.find(query, {
|
||||||
|
sort: {_id: 1},
|
||||||
|
limit: 250,
|
||||||
|
fields: [ // specify fields we are interested in to limit retrieved data (empty if we're not reading data)
|
||||||
|
'items.gear.owned'
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.then(updateUsers)
|
||||||
|
.catch(function (err) {
|
||||||
|
console.log(err);
|
||||||
|
return exiting(1, 'ERROR! ' + err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var progressCount = 1000;
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
function updateUsers (users) {
|
||||||
|
if (!users || users.length === 0) {
|
||||||
|
console.warn('All appropriate users found and modified.');
|
||||||
|
displayData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var userPromises = users.map(updateUser);
|
||||||
|
var lastUser = users[users.length - 1];
|
||||||
|
|
||||||
|
return Promise.all(userPromises)
|
||||||
|
.then(function () {
|
||||||
|
processUsers(lastUser._id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUser (user) {
|
||||||
|
count++;
|
||||||
|
|
||||||
|
var set = {'migration':migrationName};
|
||||||
|
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2016')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2017'] = false;
|
||||||
|
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2015')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2016'] = false;
|
||||||
|
} else if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2015'] = false;
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.armor_special_birthday'] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inc = {
|
||||||
|
'items.food.Cake_Skeleton':1,
|
||||||
|
'items.food.Cake_Base':1,
|
||||||
|
'items.food.Cake_CottonCandyBlue':1,
|
||||||
|
'items.food.Cake_CottonCandyPink':1,
|
||||||
|
'items.food.Cake_Shade':1,
|
||||||
|
'items.food.Cake_White':1,
|
||||||
|
'items.food.Cake_Golden':1,
|
||||||
|
'items.food.Cake_Zombie':1,
|
||||||
|
'items.food.Cake_Desert':1,
|
||||||
|
'items.food.Cake_Red':1,
|
||||||
|
'achievements.habitBirthdays':1
|
||||||
|
};
|
||||||
|
|
||||||
|
dbUsers.update({_id: user._id}, {$set:set, $inc:inc});
|
||||||
|
|
||||||
|
if (count % progressCount == 0) console.warn(count + ' ' + user._id);
|
||||||
|
if (user._id == authorUuid) console.warn(authorName + ' processed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayData() {
|
||||||
|
console.warn('\n' + count + ' users processed\n');
|
||||||
|
return exiting(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exiting(code, msg) {
|
||||||
|
code = code || 0; // 0 = success
|
||||||
|
if (code && !msg) { msg = 'ERROR!'; }
|
||||||
|
if (msg) {
|
||||||
|
if (code) { console.error(msg); }
|
||||||
|
else { console.log( msg); }
|
||||||
|
}
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = processUsers;
|
||||||
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 7.1 KiB |
@@ -218,6 +218,7 @@
|
|||||||
"hatchingPotionGhost": "Ghost",
|
"hatchingPotionGhost": "Ghost",
|
||||||
"hatchingPotionRoyalPurple": "Royal Purple",
|
"hatchingPotionRoyalPurple": "Royal Purple",
|
||||||
"hatchingPotionHolly": "Holly",
|
"hatchingPotionHolly": "Holly",
|
||||||
|
"hatchingPotionCupid": "Cupid",
|
||||||
|
|
||||||
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> pet.",
|
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> pet.",
|
||||||
"premiumPotionAddlNotes": "Not usable on quest pet eggs.",
|
"premiumPotionAddlNotes": "Not usable on quest pet eggs.",
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const CLASSES = [
|
|||||||
|
|
||||||
export const EVENTS = {
|
export const EVENTS = {
|
||||||
winter: { start: '2013-12-31', end: '2014-02-01' },
|
winter: { start: '2013-12-31', end: '2014-02-01' },
|
||||||
birthday: { start: '2016-01-29', end: '2016-02-02' },
|
birthday: { start: '2017-01-31', end: '2017-02-02' },
|
||||||
spring: { start: '2014-03-21', end: '2014-05-01' },
|
spring: { start: '2014-03-21', end: '2014-05-01' },
|
||||||
summer: { start: '2014-06-20', end: '2014-08-01' },
|
summer: { start: '2014-06-20', end: '2014-08-01' },
|
||||||
fall: { start: '2014-09-21', end: '2014-11-01' },
|
fall: { start: '2014-09-21', end: '2014-11-01' },
|
||||||
|
|||||||
@@ -531,6 +531,12 @@ let armor = {
|
|||||||
value: 90,
|
value: 90,
|
||||||
con: 15,
|
con: 15,
|
||||||
},
|
},
|
||||||
|
birthday2017: {
|
||||||
|
text: t('armorSpecialBirthday2017Text'),
|
||||||
|
notes: t('armorSpecialBirthday2017Notes'),
|
||||||
|
value: 0,
|
||||||
|
canOwn: ownsItem('armor_special_birthday2017'),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let back = {
|
let back = {
|
||||||
|
|||||||
@@ -149,12 +149,12 @@ api.specialMounts = stable.specialMounts;
|
|||||||
api.mountInfo = stable.mountInfo;
|
api.mountInfo = stable.mountInfo;
|
||||||
|
|
||||||
// For seasonal events, change these booleans:
|
// For seasonal events, change these booleans:
|
||||||
let canBuyNormalFood = true;
|
let canBuyNormalFood = false;
|
||||||
let canDropNormalFood = true;
|
let canDropNormalFood = false;
|
||||||
let canBuyCandyFood = false;
|
let canBuyCandyFood = false;
|
||||||
let canDropCandyFood = false;
|
let canDropCandyFood = false;
|
||||||
let canBuyCakeFood = false;
|
let canBuyCakeFood = true;
|
||||||
let canDropCakeFood = false;
|
let canDropCakeFood = true;
|
||||||
|
|
||||||
api.food = {
|
api.food = {
|
||||||
Meat: {
|
Meat: {
|
||||||
|
|||||||
@@ -85,6 +85,19 @@ function _setUpNewUser (user) {
|
|||||||
user._ABtests.checkInModals = '20161221_showCheckInPreviews'; // show 'preview' check-in modals
|
user._ABtests.checkInModals = '20161221_showCheckInPreviews'; // show 'preview' check-in modals
|
||||||
}
|
}
|
||||||
user.items.quests.dustbunnies = 1;
|
user.items.quests.dustbunnies = 1;
|
||||||
|
user.items.gear.owned.armor_special_birthday = false;
|
||||||
|
user.items.food = {
|
||||||
|
'Cake_Base': 1,
|
||||||
|
'Cake_CottonCandyBlue': 1,
|
||||||
|
'Cake_CottonCandyPink': 1,
|
||||||
|
'Cake_Desert': 1,
|
||||||
|
'Cake_Golden': 1,
|
||||||
|
'Cake_Red': 1,
|
||||||
|
'Cake_Shade': 1,
|
||||||
|
'Cake_Skeleton': 1,
|
||||||
|
'Cake_White': 1,
|
||||||
|
'Cake_Zombie': 1,
|
||||||
|
};
|
||||||
|
|
||||||
if (user.registeredThrough === 'habitica-web' || user.registeredThrough === 'habitica-android') {
|
if (user.registeredThrough === 'habitica-web' || user.registeredThrough === 'habitica-android') {
|
||||||
taskTypes = ['habit', 'daily', 'todo', 'reward', 'tag'];
|
taskTypes = ['habit', 'daily', 'todo', 'reward', 'tag'];
|
||||||
|
|||||||
@@ -1,5 +1,50 @@
|
|||||||
h2 1/26/2017 - BEFFYMAROO JOINS HABITICA STAFF; GUILD SPOTLIGHT ON HEALTH AND FITNESS
|
h2 1/31/2017 - HABITICA BIRTHDAY PARTY AND LAST CHANCE FOR MANY SPECIAL ITEMS
|
||||||
hr
|
hr
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
h3 Habitica Birthday Bash
|
||||||
|
p January 31st is Habitica's Birthday! Thank you so much for being a part of our community - it means a lot.
|
||||||
|
br
|
||||||
|
p Now come join us and the NPCs as we celebrate!
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
.Pet_Food_Cake_Base.pull-left.slight-right-margin
|
||||||
|
h3 Cake for Everybody!
|
||||||
|
p In honor of the festivities, everyone has been awarded an assortment of yummy cake to feed to your pets! Plus, for the next two days <a href='/#/options/inventory/drops'>Alexander the Merchant</a> is selling cake in his shop, and cake will sometimes drop when you complete your tasks. Cake works just like normal pet food, but if you want to know what type of pet likes each slice, <a href='http://habitica.wikia.com/wiki/Food' target='_blank'>the wiki has spoilers</a>.
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
h3 Party Robes
|
||||||
|
p There are Party Robes available for free in the <a href='/#/tasks'>Rewards column</a>! Don them with pride.
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
.promo_holly_potions.pull-left.slight-right-margin
|
||||||
|
.achievement-birthday2x.pull-right
|
||||||
|
h3 Birthday Bash Achievement
|
||||||
|
p In honor of Habitica's birthday, everyone has been awarded the Habitica Birthday Bash achievement! This achievement stacks for each Birthday Bash you celebrate with us.
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
h3 Last Chance for Winter Wonderland Outfits, Wintry Hair Colors, and Snowballs
|
||||||
|
p Today is the final day of the Winter Wonderland Festival, so if you still have any remaining Winter Wonderland Items that you want to buy, you'd better do it now! The <a href='/#/options/inventory/seasonalshop'>Seasonal Edition items</a> and <a href='/#/options/profile/avatar'>Hair Colors</a> won't be back until next December, and if the Limited Edition items return they will have increased prices or changed art, so be sure to snag them today! Due to the fact that the Wintry Skins were released later than the rest of the Seasonal Customization items, we've decided to make a special one-time exception to their end date, so they will be available until February 3rd rather than January 31st.
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
.promo_mystery_201701.pull-right
|
||||||
|
h3 Last Chance for Time-Freezer Set
|
||||||
|
p Reminder: it's the final day to <a href='/#/options/settings/subscription'>subscribe</a> and receive the Timefreezer Set! Subscribing also lets you buy gems for gold. The longer your subscription, the more gems you get!
|
||||||
|
br
|
||||||
|
p Thanks so much for your support! You help keep Habitica running.
|
||||||
|
p.small.muted by Lemoness
|
||||||
|
tr
|
||||||
|
td
|
||||||
|
h3 Last Chance for Holly and Peppermint Hatching Potions
|
||||||
|
p Reminder: the 31st is the final day to <a href='/#/options/inventory/drops'>buy Holly and Peppermint Hatching Potions</a>! If they come back, it won't be until next year at the earliest, so don't delay!
|
||||||
|
p.small.muted by Vampitch, Lemoness, and SabreCat
|
||||||
|
|
||||||
|
if menuItem !== 'oldNews'
|
||||||
|
hr
|
||||||
|
a(href='/static/old-news', target='_blank') Read older news
|
||||||
|
|
||||||
|
mixin oldNews
|
||||||
|
h2 1/26/2017 - BEFFYMAROO JOINS HABITICA STAFF; GUILD SPOTLIGHT ON HEALTH AND FITNESS
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
.promo_contrib_spotlight_beffymaroo.pull-right
|
.promo_contrib_spotlight_beffymaroo.pull-right
|
||||||
@@ -12,12 +57,6 @@ h2 1/26/2017 - BEFFYMAROO JOINS HABITICA STAFF; GUILD SPOTLIGHT ON HEALTH AND FI
|
|||||||
h3 Guild Spotlight: Health and Fitness
|
h3 Guild Spotlight: Health and Fitness
|
||||||
p There's a new <a href='https://habitica.wordpress.com/2017/01/26/starting-a-happy-healthy-2017-guilds-for-health-and-fitness/' target='_blank'>Guild Spotlight on the blog</a> that highlights the Guilds that can help you as you evaluate your Health and Fitness goals for 2017! Check it out now to find Habitica's best Health and Fitness communities.
|
p There's a new <a href='https://habitica.wordpress.com/2017/01/26/starting-a-happy-healthy-2017-guilds-for-health-and-fitness/' target='_blank'>Guild Spotlight on the blog</a> that highlights the Guilds that can help you as you evaluate your Health and Fitness goals for 2017! Check it out now to find Habitica's best Health and Fitness communities.
|
||||||
p.small.muted by Beffymaroo
|
p.small.muted by Beffymaroo
|
||||||
|
|
||||||
if menuItem !== 'oldNews'
|
|
||||||
hr
|
|
||||||
a(href='/static/old-news', target='_blank') Read older news
|
|
||||||
|
|
||||||
mixin oldNews
|
|
||||||
h2 1/24/2017 - JANUARY SUBSCRIBER ITEMS REVEALED
|
h2 1/24/2017 - JANUARY SUBSCRIBER ITEMS REVEALED
|
||||||
tr
|
tr
|
||||||
td
|
td
|
||||||
|
|||||||