feat(event): Habit Birthday 2018
118
migrations/20180130_habit_birthday.js
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
var migrationName = '20180130_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 party robes: most recent user doesn't have of 2014-2018. 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('2018-01-01')}, // 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 push;
|
||||||
|
var set = {'migration':migrationName};
|
||||||
|
|
||||||
|
if (user.items && user.items.gear && user.items.gear.owned && user.items.gear.owned.hasOwnProperty('armor_special_birthday2017')) {
|
||||||
|
set['items.gear.owned.armor_special_birthday2018'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2018', '_id': monk.id()}};
|
||||||
|
} else 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;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2017', '_id': monk.id()}};
|
||||||
|
} 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;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2016', '_id': monk.id()}};
|
||||||
|
} 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;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday2015', '_id': monk.id()}};
|
||||||
|
} else {
|
||||||
|
set['items.gear.owned.armor_special_birthday'] = false;
|
||||||
|
push = {pinnedItems: {type: 'marketGear', path: 'gear.flat.armor_special_birthday', '_id': monk.id()}};
|
||||||
|
}
|
||||||
|
|
||||||
|
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, $push: push});
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
BIN
website/client/assets/images/npc/birthday/market_background.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
website/client/assets/images/npc/birthday/market_banner_npc.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
website/client/assets/images/npc/birthday/npc_bailey.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
website/client/assets/images/npc/birthday/npc_justin.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
website/client/assets/images/npc/birthday/npc_matt.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
BIN
website/client/assets/images/npc/birthday/quest_shop_npc.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 12 KiB |
BIN
website/client/assets/images/npc/birthday/tavern_background.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
website/client/assets/images/npc/birthday/tavern_npc.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
@@ -4,25 +4,35 @@
|
|||||||
.align-self-center.right-margin(:class='baileyClass')
|
.align-self-center.right-margin(:class='baileyClass')
|
||||||
.media-body
|
.media-body
|
||||||
h1.align-self-center(v-markdown='$t("newStuff")')
|
h1.align-self-center(v-markdown='$t("newStuff")')
|
||||||
h2 1/23/2018 - JANUARY SUBSCRIBER ITEMS, RESOLUTION PLOT-LINE, AND GUILDS FOR GOALS
|
h2 1/30/2018 - HABITICA BIRTHDAY CELEBRATION, LAST CHANCE FOR WINTER WONDERLAND ITEMS, AND CONTINUED RESOLUTION PLOT-LINE
|
||||||
hr
|
hr
|
||||||
h3 January Subscriber Items Revealed!
|
.promo_habit_birthday_2018.center-block
|
||||||
p(v-markdown='"The January Subscriber Item has been revealed: the Frost Sprite Item Set! You only have until January 31 to receive the item set when you [subscribe](/settings/subscription). If you\'re already an active subscriber, reload the site and then head to Inventory > Equipment to claim your gear!"')
|
h3 Habitica Birthday Party!
|
||||||
p Subscribers also receive the ability to buy gems for gold -- the longer you subscribe, the more gems you can buy per month! There are other perks as well, such as longer access to uncompressed data and a cute Jackalope pet. Best of all, subscriptions let us keep Habitica running. Thank you very much for your support -- it means a lot to us.
|
p January 31st is Habitica's Birthday! Thank you so much for being a part of our community - it means a lot.
|
||||||
.small by Beffymaroo
|
p Now come join us and the NPCs as we celebrate!
|
||||||
.promo_mystery_201801.center-block
|
h4 Cake for Everybody!
|
||||||
h3 Resolution Plot-Line: An Overheard Conversation
|
p(v-markdown='"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 [Alexander the Merchant](/shops/market) 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, [the wiki has spoilers](http://habitica.wikia.com/wiki/Food)."')
|
||||||
p As you stride through the streets of Habit City, you overhear a worried conference of whispers. Curious, you peek in to Productivity Plaza and discover Lemoness and Beffymaroo in solemn conversation.
|
h4 Party Robes
|
||||||
p "On one hand, there's always the risk of discouragement when the eagerness of a fresh New Year's resolution gives way to everyday difficulties," Lemoness is saying. "But that just doesn't seem to match these reports. Habiticans who were making real progress are abruptly giving up all their goals overnight."
|
p There are Party Robes available for free in the Rewards column! Don them with pride.
|
||||||
p "I agree," says Beffymaroo. "And look at these maps -- all the reports are happening in the exact same neighborhoods."
|
h4 Birthday Bash Achievement
|
||||||
p "Clustered discouragement, cropping up all over the city?" Lemoness shakes her head. "I won't tempt fate by calling it a coincidence. It's time to investigate."
|
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.
|
||||||
p Without further ado, both of them hurry away. What a strange conversation to overhear! Perhaps we'll learn more about this later....
|
|
||||||
.media
|
.media
|
||||||
.media-body
|
.media-body
|
||||||
h3 New Goals for the New Year: Guilds for Setting (and Keeping) Realistic Goals
|
h3 Last Chance for Frost Sprite Set
|
||||||
p(v-markdown='"There\'s a new [Guild Spotlight on the blog](https://habitica.wordpress.com/2018/01/23/new-goals-for-the-new-year-guilds-for-setting-and-keeping-realistic-goals/) that highlights the Guilds that can help you as set new goals for 2018 and strive to stay on track! Check it out now to find Habitica\'s best goal-setting communities."')
|
p(v-markdown='"Reminder: this is the final day to [subscribe](/user/settings/subscription) and receive the Frost Sprite Set! Subscribing also lets you buy gems for gold. The longer your subscription, the more gems you get!"')
|
||||||
|
p Thanks so much for your support! You help keep Habitica running.
|
||||||
.small by Beffymaroo
|
.small by Beffymaroo
|
||||||
.scene_task_list.left-margin
|
h3 Last Chance for Starry Night and Holly Hatching Potions
|
||||||
|
p(v-markdown='"Reminder: this is the final day to [buy Starry Night and Holly Hatching Potions!](/shops/market) If they come back, it won\'t be until next year at the earliest, so don\'t delay!"')
|
||||||
|
.small by Vampitch, JinjooHat, Lemoness, and SabreCat
|
||||||
|
h3 Resolution Plot-Line: Broken Buildings
|
||||||
|
p Lemoness, SabreCat, and Beffymaroo call an important meeting to address the rumors that are flying about this strange outbreak of Habiticans who are suddenly losing all faith in their ability to complete their New Year's Resolutions.
|
||||||
|
p “Thank you all for coming,” Lemoness says. “I'm afraid that we have some very serious news to share, but we ask that you remain calm.”
|
||||||
|
.promo_starry_potions.left-margin
|
||||||
|
p “While it's natural to feel a little disheartened as the end of January approaches,” Beffymaroo says, “these sudden outbreaks appear to have some strange magical origin. We're still investigating the exact cause, but we do know that the buildings where the affected Habiticans live often seem to sustain some damage immediately before the attack.”
|
||||||
|
p SabreCat clears his throat. “For this reason, we strongly encourage everyone to stay away from broken-down structures, and if you feel any strange tremors or hear odd sounds, please report them immediately.”
|
||||||
|
p(v-markdown='"“Stay safe, Habiticans.” Lemoness flashes her best comforting smile. “And remember that if your New Year\'s Resolution goals seem daunting, you can always seek support in the [New Year\'s Resolution Guild](https://habitica.com/groups/guild/6e6a8bd3-9f5f-4351-9188-9f11fcd80a99).”"')
|
||||||
|
p How mysterious! Hopefully they'll get to the bottom of this soon.
|
||||||
hr
|
hr
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -451,6 +451,8 @@
|
|||||||
"armorSpecialBirthday2016Notes": "Happy Birthday, Habitica! Wear these Ridiculous Party Robes to celebrate this wonderful day. Confers no benefit.",
|
"armorSpecialBirthday2016Notes": "Happy Birthday, Habitica! Wear these Ridiculous Party Robes to celebrate this wonderful day. Confers no benefit.",
|
||||||
"armorSpecialBirthday2017Text": "Whimsical Party Robes",
|
"armorSpecialBirthday2017Text": "Whimsical Party Robes",
|
||||||
"armorSpecialBirthday2017Notes": "Happy Birthday, Habitica! Wear these Whimsical Party Robes to celebrate this wonderful day. Confers no benefit.",
|
"armorSpecialBirthday2017Notes": "Happy Birthday, Habitica! Wear these Whimsical Party Robes to celebrate this wonderful day. Confers no benefit.",
|
||||||
|
"armorSpecialBirthday2018Text": "Fanciful Party Robes",
|
||||||
|
"armorSpecialBirthday2018Notes": "Happy Birthday, Habitica! Wear these Fanciful Party Robes to celebrate this wonderful day. Confers no benefit.",
|
||||||
|
|
||||||
"armorSpecialGaymerxText": "Rainbow Warrior Armor",
|
"armorSpecialGaymerxText": "Rainbow Warrior Armor",
|
||||||
"armorSpecialGaymerxNotes": "In celebration of the GaymerX Conference, this special armor is decorated with a radiant, colorful rainbow pattern! GaymerX is a game convention celebrating LGTBQ and gaming and is open to everyone.",
|
"armorSpecialGaymerxNotes": "In celebration of the GaymerX Conference, this special armor is decorated with a radiant, colorful rainbow pattern! GaymerX is a game convention celebrating LGTBQ and gaming and is open to everyone.",
|
||||||
|
|||||||
@@ -867,6 +867,12 @@ let armor = {
|
|||||||
value: 90,
|
value: 90,
|
||||||
con: 15,
|
con: 15,
|
||||||
},
|
},
|
||||||
|
birthday2018: {
|
||||||
|
text: t('armorSpecialBirthday2018Text'),
|
||||||
|
notes: t('armorSpecialBirthday2018Notes'),
|
||||||
|
value: 0,
|
||||||
|
canOwn: ownsItem('armor_special_birthday2018'),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let back = {
|
let back = {
|
||||||
|
|||||||
@@ -266,12 +266,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: {
|
||||||
|
|||||||
|
After Width: | Height: | Size: 747 B |
|
After Width: | Height: | Size: 692 B |
|
After Width: | Height: | Size: 708 B |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.9 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: 6.3 KiB |
|
After Width: | Height: | Size: 18 KiB |
BIN
website/raw_sprites/spritesmith_large/promo_starry_potions.png
Normal file
|
After Width: | Height: | Size: 17 KiB |