mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-14 21:27:23 +01:00
feat(content): new pet color achievements
This commit is contained in:
104
migrations/archive/2019/20190917_pet_color_achievements.js
Normal file
104
migrations/archive/2019/20190917_pet_color_achievements.js
Normal file
@@ -0,0 +1,104 @@
|
||||
/* eslint-disable no-console */
|
||||
const MIGRATION_NAME = '20190917_pet_color_achievements';
|
||||
import { model as User } from '../../../website/server/models/user';
|
||||
|
||||
const progressCount = 1000;
|
||||
let count = 0;
|
||||
|
||||
async function updateUser (user) {
|
||||
count++;
|
||||
|
||||
let set = {
|
||||
migration: MIGRATION_NAME,
|
||||
};
|
||||
|
||||
if (user && user.items && user.items.pets) {
|
||||
const pets = user.items.pets;
|
||||
if (pets['Wolf-Base'] > 0
|
||||
&& pets['TigerCub-Base'] > 0
|
||||
&& pets['PandaCub-Base'] > 0
|
||||
&& pets['LionCub-Base'] > 0
|
||||
&& pets['Fox-Base'] > 0
|
||||
&& pets['FlyingPig-Base'] > 0
|
||||
&& pets['Dragon-Base'] > 0
|
||||
&& pets['Cactus-Base'] > 0
|
||||
&& pets['BearCub-Base'] > 0) {
|
||||
set['achievements.backToBasics'] = true;
|
||||
}
|
||||
if (pets['Wolf-Desert'] > 0
|
||||
&& pets['TigerCub-Desert'] > 0
|
||||
&& pets['PandaCub-Desert'] > 0
|
||||
&& pets['LionCub-Desert'] > 0
|
||||
&& pets['Fox-Desert'] > 0
|
||||
&& pets['FlyingPig-Desert'] > 0
|
||||
&& pets['Dragon-Desert'] > 0
|
||||
&& pets['Cactus-Desert'] > 0
|
||||
&& pets['BearCub-Desert'] > 0) {
|
||||
set['achievements.dustDevil'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (user && user.items && user.items.mounts) {
|
||||
const mounts = user.items.mounts;
|
||||
if (mounts['Wolf-Base']
|
||||
&& mounts['TigerCub-Base']
|
||||
&& mounts['PandaCub-Base']
|
||||
&& mounts['LionCub-Base']
|
||||
&& mounts['Fox-Base']
|
||||
&& mounts['FlyingPig-Base']
|
||||
&& mounts['Dragon-Base']
|
||||
&& mounts['Cactus-Base']
|
||||
&& mounts['BearCub-Base'] ) {
|
||||
set['achievements.allYourBase'] = true;
|
||||
}
|
||||
if (mounts['Wolf-Desert']
|
||||
&& mounts['TigerCub-Desert']
|
||||
&& mounts['PandaCub-Desert']
|
||||
&& mounts['LionCub-Desert']
|
||||
&& mounts['Fox-Desert']
|
||||
&& mounts['FlyingPig-Desert']
|
||||
&& mounts['Dragon-Desert']
|
||||
&& mounts['Cactus-Desert']
|
||||
&& mounts['BearCub-Desert'] ) {
|
||||
set['achievements.aridAuthority'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (count % progressCount === 0) console.warn(`${count} ${user._id}`);
|
||||
|
||||
return await User.update({ _id: user._id }, { $set: set }).exec();
|
||||
}
|
||||
|
||||
module.exports = async function processUsers () {
|
||||
let query = {
|
||||
migration: { $ne: MIGRATION_NAME },
|
||||
'auth.timestamps.loggedin': { $gt: new Date('2019-09-01') },
|
||||
};
|
||||
|
||||
const fields = {
|
||||
_id: 1,
|
||||
items: 1,
|
||||
};
|
||||
|
||||
while (true) { // eslint-disable-line no-constant-condition
|
||||
const users = await User // eslint-disable-line no-await-in-loop
|
||||
.find(query)
|
||||
.limit(250)
|
||||
.sort({_id: 1})
|
||||
.select(fields)
|
||||
.lean()
|
||||
.exec();
|
||||
|
||||
if (users.length === 0) {
|
||||
console.warn('All appropriate users found and modified.');
|
||||
console.warn(`\n${count} users processed\n`);
|
||||
break;
|
||||
} else {
|
||||
query._id = {
|
||||
$gt: users[users.length - 1]._id,
|
||||
};
|
||||
}
|
||||
|
||||
await Promise.all(users.map(updateUser)); // eslint-disable-line no-await-in-loop
|
||||
}
|
||||
};
|
||||
@@ -187,6 +187,24 @@ describe('shared.ops.feed', () => {
|
||||
expect(user.achievements.allYourBase).to.eql(true);
|
||||
});
|
||||
|
||||
it('awards Arid Authority achievement', () => {
|
||||
user.items.pets['Wolf-Spooky'] = 5;
|
||||
user.items.food.Milk = 2;
|
||||
user.items.mounts = {
|
||||
'Wolf-Desert': true,
|
||||
'TigerCub-Desert': true,
|
||||
'PandaCub-Desert': true,
|
||||
'LionCub-Desert': true,
|
||||
'Fox-Desert': true,
|
||||
'FlyingPig-Desert': true,
|
||||
'Dragon-Desert': true,
|
||||
'Cactus-Desert': true,
|
||||
'BearCub-Desert': true,
|
||||
};
|
||||
feed(user, {params: {pet: 'Wolf-Spooky', food: 'Milk'}});
|
||||
expect(user.achievements.aridAuthority).to.eql(true);
|
||||
});
|
||||
|
||||
it('evolves the pet into a mount when feeding user.items.pets[pet] >= 50', () => {
|
||||
user.items.pets['Wolf-Base'] = 49;
|
||||
user.items.food.Milk = 2;
|
||||
|
||||
@@ -177,6 +177,24 @@ describe('shared.ops.hatch', () => {
|
||||
hatch(user, {params: {egg: 'Wolf', hatchingPotion: 'Spooky'}});
|
||||
expect(user.achievements.backToBasics).to.eql(true);
|
||||
});
|
||||
|
||||
it('awards Dust Devil achievement', () => {
|
||||
user.items.pets = {
|
||||
'Wolf-Desert': 5,
|
||||
'TigerCub-Desert': 5,
|
||||
'PandaCub-Desert': 10,
|
||||
'LionCub-Desert': 5,
|
||||
'Fox-Desert': 5,
|
||||
'FlyingPig-Desert': 5,
|
||||
'Dragon-Desert': 5,
|
||||
'Cactus-Desert': 15,
|
||||
'BearCub-Desert': 5,
|
||||
};
|
||||
user.items.eggs = {Wolf: 1};
|
||||
user.items.hatchingPotions = {Spooky: 1};
|
||||
hatch(user, {params: {egg: 'Wolf', hatchingPotion: 'Spooky'}});
|
||||
expect(user.achievements.dustDevil).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,6 +165,16 @@ const NOTIFICATIONS = {
|
||||
label: ($t) => `${$t('achievement')}: ${$t('achievementBackToBasics')}`,
|
||||
modalId: 'generic-achievement',
|
||||
},
|
||||
ACHIEVEMENT_DUST_DEVIL: {
|
||||
achievement: true,
|
||||
label: ($t) => `${$t('achievement')}: ${$t('achievementDustDevil')}`,
|
||||
modalId: 'generic-achievement',
|
||||
},
|
||||
ACHIEVEMENT_ARID_AUTHORITY: {
|
||||
achievement: true,
|
||||
label: ($t) => `${$t('achievement')}: ${$t('achievementAridAuthority')}`,
|
||||
modalId: 'generic-achievement',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
@@ -220,7 +230,7 @@ export default {
|
||||
'ULTIMATE_GEAR_ACHIEVEMENT', 'REBIRTH_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
|
||||
'CHALLENGE_JOINED_ACHIEVEMENT', 'INVITED_FRIEND_ACHIEVEMENT', 'NEW_CONTRIBUTOR_LEVEL',
|
||||
'CRON', 'SCORED_TASK', 'LOGIN_INCENTIVE', 'ACHIEVEMENT_ALL_YOUR_BASE', 'ACHIEVEMENT_BACK_TO_BASICS',
|
||||
'GENERIC_ACHIEVEMENT',
|
||||
'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY', 'GENERIC_ACHIEVEMENT',
|
||||
].forEach(type => {
|
||||
handledNotifications[type] = true;
|
||||
});
|
||||
@@ -595,6 +605,8 @@ export default {
|
||||
case 'NEW_CONTRIBUTOR_LEVEL':
|
||||
case 'ACHIEVEMENT_ALL_YOUR_BASE':
|
||||
case 'ACHIEVEMENT_BACK_TO_BASICS':
|
||||
case 'ACHIEVEMENT_DUST_DEVIL':
|
||||
case 'ACHIEVEMENT_ARID_AUTHORITY':
|
||||
case 'GENERIC_ACHIEVEMENT':
|
||||
this.showNotificationWithModal(notification);
|
||||
break;
|
||||
|
||||
@@ -18,5 +18,11 @@
|
||||
"achievementBackToBasicsModalText": "You collected all the Base Pets!",
|
||||
"achievementAllYourBase": "All Your Base",
|
||||
"achievementAllYourBaseText": "Has tamed all Base Mounts.",
|
||||
"achievementAllYourBaseModalText": "You tamed all the Base Mounts!"
|
||||
"achievementAllYourBaseModalText": "You tamed all the Base Mounts!",
|
||||
"achievementDustDevil": "Dust Devil",
|
||||
"achievementDustDevilText": "Has collected all Desert Pets.",
|
||||
"achievementDustDevilModalText": "You collected all the Desert Pets!",
|
||||
"achievementAridAuthority": "Arid Authority",
|
||||
"achievementAridAuthorityText": "Has tamed all Desert Mounts.",
|
||||
"achievementAridAuthorityModalText": "You tamed all the Desert Mounts!"
|
||||
}
|
||||
|
||||
@@ -147,6 +147,16 @@ let basicAchievs = {
|
||||
titleKey: 'achievementAllYourBase',
|
||||
textKey: 'achievementAllYourBaseText',
|
||||
},
|
||||
dustDevil: {
|
||||
icon: 'achievement-dustDevil',
|
||||
titleKey: 'achievementDustDevil',
|
||||
textKey: 'achievementDustDevilText',
|
||||
},
|
||||
aridAuthority: {
|
||||
icon: 'achievement-aridAuthority',
|
||||
titleKey: 'achievementAridAuthority',
|
||||
textKey: 'achievementAridAuthorityText',
|
||||
},
|
||||
};
|
||||
Object.assign(achievementsData, basicAchievs);
|
||||
|
||||
|
||||
@@ -256,14 +256,7 @@ export const QUEST_SERIES_ACHIEVEMENTS = {
|
||||
],
|
||||
};
|
||||
|
||||
export const BASE_PETS_MOUNTS = [
|
||||
'Wolf-Base',
|
||||
'TigerCub-Base',
|
||||
'PandaCub-Base',
|
||||
'LionCub-Base',
|
||||
'Fox-Base',
|
||||
'FlyingPig-Base',
|
||||
'Dragon-Base',
|
||||
'Cactus-Base',
|
||||
'BearCub-Base',
|
||||
export const ANIMAL_COLOR_ACHIEVEMENTS = [
|
||||
{color: 'Base', petAchievement: 'backToBasics', petNotificationType: 'ACHIEVEMENT_BACK_TO_BASICS', mountAchievement: 'allYourBase', mountNotificationType: 'ACHIEVEMENT_ALL_YOUR_BASE'},
|
||||
{color: 'Desert', petAchievement: 'dustDevil', petNotificationType: 'ACHIEVEMENT_DUST_DEVIL', mountAchievement: 'aridAuthority', mountNotificationType: 'ACHIEVEMENT_ARID_AUTHORITY'},
|
||||
];
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
GEAR_TYPES,
|
||||
ITEM_LIST,
|
||||
QUEST_SERIES_ACHIEVEMENTS,
|
||||
BASE_PETS_MOUNTS,
|
||||
ANIMAL_COLOR_ACHIEVEMENTS,
|
||||
} from './constants';
|
||||
|
||||
let api = module.exports;
|
||||
@@ -38,7 +38,7 @@ import officialPinnedItems from './officialPinnedItems';
|
||||
|
||||
api.achievements = achievements;
|
||||
api.questSeriesAchievements = QUEST_SERIES_ACHIEVEMENTS;
|
||||
api.basePetsMounts = BASE_PETS_MOUNTS;
|
||||
api.animalColorAchievements = ANIMAL_COLOR_ACHIEVEMENTS;
|
||||
|
||||
api.quests = quests;
|
||||
api.questsByLevel = questsByLevel;
|
||||
|
||||
@@ -188,6 +188,8 @@ function _getBasicAchievements (user, language) {
|
||||
_addSimple(result, user, {path: 'justAddWater', language});
|
||||
_addSimple(result, user, {path: 'backToBasics', language});
|
||||
_addSimple(result, user, {path: 'allYourBase', language});
|
||||
_addSimple(result, user, {path: 'dustDevil', language});
|
||||
_addSimple(result, user, {path: 'aridAuthority', language});
|
||||
|
||||
_addSimpleWithMasterCount(result, user, {path: 'beastMaster', language});
|
||||
_addSimpleWithMasterCount(result, user, {path: 'mountMaster', language});
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import forEach from 'lodash/forEach';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import get from 'lodash/get';
|
||||
import keys from 'lodash/keys';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
@@ -90,21 +93,24 @@ module.exports = function feed (user, req = {}) {
|
||||
user.items.food[food.key]--;
|
||||
if (user.markModified) user.markModified('items.food');
|
||||
|
||||
if (!user.achievements.allYourBase) {
|
||||
const mountIndex = findIndex(content.basePetsMounts, (animal) => {
|
||||
return !user.items.mounts[animal];
|
||||
});
|
||||
if (mountIndex === -1) {
|
||||
user.achievements.allYourBase = true;
|
||||
if (user.addNotification) {
|
||||
user.addNotification('ACHIEVEMENT_ALL_YOUR_BASE', {
|
||||
achievement: 'allYourBase',
|
||||
message: `${i18n.t('modalAchievement')} ${i18n.t('achievementAllYourBase')}`,
|
||||
modalText: i18n.t('achievementAllYourBaseModalText'),
|
||||
});
|
||||
forEach(content.animalColorAchievements, (achievement) => {
|
||||
if (!user.achievements[achievement.mountAchievement]) {
|
||||
const mountIndex = findIndex(keys(content.dropEggs), (animal) => {
|
||||
return !user.items.mounts[`${animal}-${achievement.color}`];
|
||||
});
|
||||
if (mountIndex === -1) {
|
||||
user.achievements[achievement.mountAchievement] = true;
|
||||
if (user.addNotification) {
|
||||
const achievementString = `achievement${upperFirst(achievement.mountAchievement)}`;
|
||||
user.addNotification(achievement.mountNotificationType, {
|
||||
achievement: achievement.mountAchievement,
|
||||
message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`,
|
||||
modalText: i18n.t(`${achievementString}ModalText`),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
userPets[pet.key],
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import forEach from 'lodash/forEach';
|
||||
import get from 'lodash/get';
|
||||
import keys from 'lodash/keys';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
@@ -40,21 +43,24 @@ module.exports = function hatch (user, req = {}) {
|
||||
user.markModified('items.hatchingPotions');
|
||||
}
|
||||
|
||||
if (!user.achievements.backToBasics) {
|
||||
const petIndex = findIndex(content.basePetsMounts, (animal) => {
|
||||
return isNaN(user.items.pets[animal]) || user.items.pets[animal] <= 0;
|
||||
});
|
||||
if (petIndex === -1) {
|
||||
user.achievements.backToBasics = true;
|
||||
if (user.addNotification) {
|
||||
user.addNotification('ACHIEVEMENT_BACK_TO_BASICS', {
|
||||
achievement: 'backToBasics',
|
||||
message: `${i18n.t('modalAchievement')} ${i18n.t('achievementBackToBasics')}`,
|
||||
modalText: i18n.t('achievementBackToBasicsModalText'),
|
||||
});
|
||||
forEach(content.animalColorAchievements, (achievement) => {
|
||||
if (!user.achievements[achievement.petAchievement]) {
|
||||
const petIndex = findIndex(keys(content.dropEggs), (animal) => {
|
||||
return isNaN(user.items.pets[`${animal}-${achievement.color}`]) || user.items.pets[`${animal}-${achievement.color}`] <= 0;
|
||||
});
|
||||
if (petIndex === -1) {
|
||||
user.achievements[achievement.petAchievement] = true;
|
||||
if (user.addNotification) {
|
||||
const achievementString = `achievement${upperFirst(achievement.petAchievement)}`;
|
||||
user.addNotification(achievement.petNotificationType, {
|
||||
achievement: achievement.petAchievement,
|
||||
message: `${i18n.t('modalAchievement')} ${i18n.t(achievementString)}`,
|
||||
modalText: i18n.t(`${achievementString}ModalText`),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
user.items,
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -3,7 +3,7 @@ import { authWithHeaders } from '../../middlewares/auth';
|
||||
let api = {};
|
||||
|
||||
// @TODO export this const, cannot export it from here because only routes are exported from controllers
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'NEW DISCOUNTED PET QUEST BUNDLE: ROCKING REPTILES';
|
||||
const LAST_ANNOUNCEMENT_TITLE = 'NEW PET COLLECTION BADGES!';
|
||||
const worldDmg = { // @TODO
|
||||
bailey: false,
|
||||
};
|
||||
@@ -30,15 +30,14 @@ api.getNews = {
|
||||
<div class="mr-3 ${baileyClass}"></div>
|
||||
<div class="media-body">
|
||||
<h1 class="align-self-center">${res.t('newStuff')}</h1>
|
||||
<h2>9/10/2019 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
<h2>9/17/2019 - ${LAST_ANNOUNCEMENT_TITLE}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="promo_rocking_reptiles_bundle center-block"></div>
|
||||
<p>If you're looking to add some scaly friends to your Habitica stable, you're in luck! From now until Sept 30, you can purchase the Rocking Reptiles Pet Quest Bundle and receive the Alligator, Snake, and Velociraptor quests, all for only 7 Gems! That's a discount of 5 Gems from the price of purchasing them separately. Check it out in the <a href='/shops/quests'>Quest Shop</a> today!</p>
|
||||
<div class="small">Art by Gully, Willow The Witty, mfonda, UncommonCriminal, tabbytoes, EmeraldOx, LordDarkly, PainterProphet, Seraphina, Anna Glassman, Procyon, and Lilith of Alfheim</div>
|
||||
<div class="small mb-3">Writing by Mike.Antonacci, lilackbar, Daniel The Bard, and felipena</div>
|
||||
<p>If snakes are something you'd prefer not to see in Habitica due to a phobia, check out the <a href='http://habitica.wikia.com/wiki/Phobia_Protection_Extension' target='_blank'>Phobia Protection Extension</a> which will hide any pets, mounts, backgrounds, quest bosses, or equipment featuring snakes (as well as spiders, rats, bees, zombies, skeletons, or any combination thereof). We hope that it helps make everyone's Habitica experience fun!</p>
|
||||
<div class="promo_desert_pet_achievements center-block"></div>
|
||||
<p>We're releasing a new achievement so you can celebrate your successes in the world of Habitican pet collecting! Earn the Dust Devil and Arid Authority achievements by collecting Desert pets and mounts and you'll earn a nifty badge for your profile.</p>
|
||||
<p>If you already have all the Desert pets and/or mounts in your stable, you'll receive the badge automatically! Check your profile and celebrate your new achievement with pride.</p>
|
||||
<div class="small mb-3">by Piyo and SabreCat</div>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
@@ -124,6 +124,8 @@ let schema = new Schema({
|
||||
justAddWater: Boolean,
|
||||
backToBasics: Boolean,
|
||||
allYourBase: Boolean,
|
||||
dustDevil: Boolean,
|
||||
aridAuthority: Boolean,
|
||||
},
|
||||
|
||||
backer: {
|
||||
|
||||
@@ -37,6 +37,8 @@ const NOTIFICATION_TYPES = [
|
||||
'ACHIEVEMENT_JUST_ADD_WATER',
|
||||
'ACHIEVEMENT_LOST_MASTERCLASSER',
|
||||
'ACHIEVEMENT_MIND_OVER_MATTER',
|
||||
'ACHIEVEMENT_DUST_DEVIL',
|
||||
'ACHIEVEMENT_ARID_AUTHORITY',
|
||||
];
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
Reference in New Issue
Block a user