mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 13:47:33 +01:00
feat(achievements): new pet-related cheevos
This commit is contained in:
@@ -1882,6 +1882,36 @@ describe('Group Model', () => {
|
||||
expect(updatedSleepingParticipatingMember.achievements.lostMasterclasser).to.not.eql(true);
|
||||
});
|
||||
|
||||
it('gives out other pet-related quest achievements', async () => {
|
||||
quest = questScrolls.rock;
|
||||
party.quest.key = quest.key;
|
||||
|
||||
questLeader.achievements.quests = {
|
||||
mayhemMistiflying1: 1,
|
||||
yarn: 1,
|
||||
mayhemMistiflying2: 1,
|
||||
egg: 1,
|
||||
mayhemMistiflying3: 1,
|
||||
slime: 2,
|
||||
};
|
||||
await questLeader.save();
|
||||
await party.finishQuest(quest);
|
||||
|
||||
let [
|
||||
updatedLeader,
|
||||
updatedParticipatingMember,
|
||||
updatedSleepingParticipatingMember,
|
||||
] = await Promise.all([
|
||||
User.findById(questLeader._id).exec(),
|
||||
User.findById(participatingMember._id).exec(),
|
||||
User.findById(sleepingParticipatingMember._id).exec(),
|
||||
]);
|
||||
|
||||
expect(updatedLeader.achievements.mindOverMatter).to.eql(true);
|
||||
expect(updatedParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
|
||||
expect(updatedSleepingParticipatingMember.achievements.mindOverMatter).to.not.eql(true);
|
||||
});
|
||||
|
||||
it('gives xp and gold', async () => {
|
||||
await party.finishQuest(quest);
|
||||
|
||||
|
||||
@@ -169,6 +169,24 @@ describe('shared.ops.feed', () => {
|
||||
expect(user.items.pets['Wolf-Base']).to.equal(7);
|
||||
});
|
||||
|
||||
it('awards All Your Base achievement', () => {
|
||||
user.items.pets['Wolf-Spooky'] = 5;
|
||||
user.items.food.Milk = 2;
|
||||
user.items.mounts = {
|
||||
'Wolf-Base': true,
|
||||
'TigerCub-Base': true,
|
||||
'PandaCub-Base': true,
|
||||
'LionCub-Base': true,
|
||||
'Fox-Base': true,
|
||||
'FlyingPig-Base': true,
|
||||
'Dragon-Base': true,
|
||||
'Cactus-Base': true,
|
||||
'BearCub-Base': true,
|
||||
};
|
||||
feed(user, {params: {pet: 'Wolf-Spooky', food: 'Milk'}});
|
||||
expect(user.achievements.allYourBase).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;
|
||||
|
||||
@@ -159,6 +159,24 @@ describe('shared.ops.hatch', () => {
|
||||
expect(user.items.eggs).to.eql({Wolf: 0});
|
||||
expect(user.items.hatchingPotions).to.eql({Base: 0});
|
||||
});
|
||||
|
||||
it('awards Back to Basics achievement', () => {
|
||||
user.items.pets = {
|
||||
'Wolf-Base': 5,
|
||||
'TigerCub-Base': 5,
|
||||
'PandaCub-Base': 10,
|
||||
'LionCub-Base': 5,
|
||||
'Fox-Base': 5,
|
||||
'FlyingPig-Base': 5,
|
||||
'Dragon-Base': 5,
|
||||
'Cactus-Base': 15,
|
||||
'BearCub-Base': 5,
|
||||
};
|
||||
user.items.eggs = {Wolf: 1};
|
||||
user.items.hatchingPotions = {Spooky: 1};
|
||||
hatch(user, {params: {egg: 'Wolf', hatchingPotion: 'Spooky'}});
|
||||
expect(user.achievements.backToBasics).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,5 +5,13 @@
|
||||
"levelup": "By accomplishing your real life goals, you leveled up and are now fully healed!",
|
||||
"reachedLevel": "You Reached Level <%= level %>",
|
||||
"achievementLostMasterclasser": "Quest Completionist: Masterclasser Series",
|
||||
"achievementLostMasterclasserText": "Completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!"
|
||||
"achievementLostMasterclasserText": "Completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!",
|
||||
"achievementMindOverMatter": "Mind Over Matter",
|
||||
"achievementMindOverMatterText": "Has completed Rock, Egg, Slime, and Yarn pet quests.",
|
||||
"achievementJustAddWater": "Just Add Water",
|
||||
"achievementJustAddWaterText": "Has completed Octopus, Seahorse, Cuttlefish, Whale, Turtle, Nudibranch, Sea Serpent, and Dolphin pet quests.",
|
||||
"achievementBackToBasics": "Back to Basics",
|
||||
"achievementBackToBasicsText": "Has collected all Base Pets.",
|
||||
"achievementAllYourBase": "All Your Base",
|
||||
"achievementAllYourBaseText": "Has tamed all Base Mounts."
|
||||
}
|
||||
|
||||
@@ -127,6 +127,26 @@ let basicAchievs = {
|
||||
titleKey: 'achievementLostMasterclasser',
|
||||
textKey: 'achievementLostMasterclasserText',
|
||||
},
|
||||
mindOverMatter: {
|
||||
icon: 'achievement-mindOverMatter',
|
||||
titleKey: 'achievementMindOverMatter',
|
||||
textKey: 'achievementMindOverMatterText',
|
||||
},
|
||||
justAddWater: {
|
||||
icon: 'achievement-justAddWater',
|
||||
titleKey: 'achievementJustAddWater',
|
||||
textKey: 'achievementJustAddWaterText',
|
||||
},
|
||||
backToBasics: {
|
||||
icon: 'achievement-backToBasics',
|
||||
titleKey: 'achievementBackToBasics',
|
||||
textKey: 'achievementBackToBasicsText',
|
||||
},
|
||||
allYourBase: {
|
||||
icon: 'achievement-allYourBase',
|
||||
titleKey: 'achievementAllYourBase',
|
||||
textKey: 'achievementAllYourBaseText',
|
||||
},
|
||||
};
|
||||
Object.assign(achievementsData, basicAchievs);
|
||||
|
||||
|
||||
@@ -212,3 +212,52 @@ export const USER_CAN_OWN_QUEST_CATEGORIES = [
|
||||
'hatchingPotion',
|
||||
'pet',
|
||||
];
|
||||
|
||||
export const QUEST_SERIES_ACHIEVEMENTS = {
|
||||
lostMasterclasser: [
|
||||
'dilatoryDistress1',
|
||||
'dilatoryDistress2',
|
||||
'dilatoryDistress3',
|
||||
'mayhemMistiflying1',
|
||||
'mayhemMistiflying2',
|
||||
'mayhemMistiflying3',
|
||||
'stoikalmCalamity1',
|
||||
'stoikalmCalamity2',
|
||||
'stoikalmCalamity3',
|
||||
'taskwoodsTerror1',
|
||||
'taskwoodsTerror2',
|
||||
'taskwoodsTerror3',
|
||||
'lostMasterclasser1',
|
||||
'lostMasterclasser2',
|
||||
'lostMasterclasser3',
|
||||
'lostMasterclasser4',
|
||||
],
|
||||
mindOverMatter: [
|
||||
'egg',
|
||||
'rock',
|
||||
'slime',
|
||||
'yarn',
|
||||
],
|
||||
justAddWater: [
|
||||
'octopus',
|
||||
'dilatory_derby',
|
||||
'kraken',
|
||||
'whale',
|
||||
'turtle',
|
||||
'nudibranch',
|
||||
'seaserpent',
|
||||
'dolphin',
|
||||
],
|
||||
};
|
||||
|
||||
export const BASE_PETS_MOUNTS = [
|
||||
'Wolf-Base',
|
||||
'TigerCub-Base',
|
||||
'PandaCub-Base',
|
||||
'LionCub-Base',
|
||||
'Fox-Base',
|
||||
'FlyingPig-Base',
|
||||
'Dragon-Base',
|
||||
'Cactus-Base',
|
||||
'BearCub-Base',
|
||||
];
|
||||
|
||||
@@ -7,6 +7,8 @@ import {
|
||||
CLASSES,
|
||||
GEAR_TYPES,
|
||||
ITEM_LIST,
|
||||
QUEST_SERIES_ACHIEVEMENTS,
|
||||
BASE_PETS_MOUNTS,
|
||||
} from './constants';
|
||||
|
||||
let api = module.exports;
|
||||
@@ -35,6 +37,8 @@ import loginIncentives from './loginIncentives';
|
||||
import officialPinnedItems from './officialPinnedItems';
|
||||
|
||||
api.achievements = achievements;
|
||||
api.questSeriesAchievements = QUEST_SERIES_ACHIEVEMENTS;
|
||||
api.basePetsMounts = BASE_PETS_MOUNTS;
|
||||
|
||||
api.quests = quests;
|
||||
api.questsByLevel = questsByLevel;
|
||||
|
||||
@@ -184,6 +184,10 @@ function _getBasicAchievements (user, language) {
|
||||
_addSimple(result, user, {path: 'joinedChallenge', language});
|
||||
_addSimple(result, user, {path: 'invitedFriend', language});
|
||||
_addSimple(result, user, {path: 'lostMasterclasser', language});
|
||||
_addSimple(result, user, {path: 'mindOverMatter', language});
|
||||
_addSimple(result, user, {path: 'justAddWater', language});
|
||||
_addSimple(result, user, {path: 'backToBasics', language});
|
||||
_addSimple(result, user, {path: 'allYourBase', language});
|
||||
|
||||
_addSimpleWithMasterCount(result, user, {path: 'beastMaster', language});
|
||||
_addSimpleWithMasterCount(result, user, {path: 'mountMaster', language});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
BadRequest,
|
||||
@@ -89,6 +90,15 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
userPets[pet.key],
|
||||
message,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import findIndex from 'lodash/findIndex';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
BadRequest,
|
||||
@@ -39,6 +40,15 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
user.items,
|
||||
i18n.t('messageHatched', req.language),
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 716 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -41,6 +41,7 @@ import { getGroupChat, translateMessage } from '../libs/chat/group-chat';
|
||||
import { model as UserNotification } from './userNotification';
|
||||
|
||||
const questScrolls = shared.content.quests;
|
||||
const questSeriesAchievements = shared.content.questSeriesAchievements;
|
||||
const Schema = mongoose.Schema;
|
||||
|
||||
export const INVITES_LIMIT = 100; // must not be greater than MAX_EMAIL_INVITES_BY_USER
|
||||
@@ -849,25 +850,6 @@ schema.methods.finishQuest = async function finishQuest (quest) {
|
||||
}
|
||||
});
|
||||
|
||||
let masterClasserQuests = [
|
||||
'dilatoryDistress1',
|
||||
'dilatoryDistress2',
|
||||
'dilatoryDistress3',
|
||||
'mayhemMistiflying1',
|
||||
'mayhemMistiflying2',
|
||||
'mayhemMistiflying3',
|
||||
'stoikalmCalamity1',
|
||||
'stoikalmCalamity2',
|
||||
'stoikalmCalamity3',
|
||||
'taskwoodsTerror1',
|
||||
'taskwoodsTerror2',
|
||||
'taskwoodsTerror3',
|
||||
'lostMasterclasser1',
|
||||
'lostMasterclasser2',
|
||||
'lostMasterclasser3',
|
||||
'lostMasterclasser4',
|
||||
];
|
||||
|
||||
// Send webhooks in background
|
||||
// @TODO move the find users part to a worker as well, not just the http request
|
||||
User.find({
|
||||
@@ -893,24 +875,27 @@ schema.methods.finishQuest = async function finishQuest (quest) {
|
||||
});
|
||||
});
|
||||
|
||||
_.forEach(questSeriesAchievements, async (questList, achievement) => {
|
||||
if (questList.includes(questK)) {
|
||||
let questAchievementQuery = {};
|
||||
questAchievementQuery[`achievements.${achievement}`] = {$ne: true};
|
||||
|
||||
_.forEach(questList, (questName) => {
|
||||
if (questName !== questK) {
|
||||
questAchievementQuery[`achievements.quests.${questName}`] = {$gt: 0};
|
||||
}
|
||||
});
|
||||
|
||||
let questAchievementUpdate = {$set: {}};
|
||||
questAchievementUpdate.$set[`achievements.${achievement}`] = true;
|
||||
|
||||
promises.push(participants.map(userId => {
|
||||
return _updateUserWithRetries(userId, questAchievementUpdate, null, questAchievementQuery);
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
|
||||
if (masterClasserQuests.includes(questK)) {
|
||||
let lostMasterclasserQuery = {
|
||||
'achievements.lostMasterclasser': {$ne: true},
|
||||
};
|
||||
masterClasserQuests.forEach(questName => {
|
||||
lostMasterclasserQuery[`achievements.quests.${questName}`] = {$gt: 0};
|
||||
});
|
||||
let lostMasterclasserUpdate = {
|
||||
$set: {'achievements.lostMasterclasser': true},
|
||||
};
|
||||
|
||||
let lostMasterClasserPromises = participants.map(userId => {
|
||||
return _updateUserWithRetries(userId, lostMasterclasserUpdate, null, lostMasterclasserQuery);
|
||||
});
|
||||
await Promise.all(lostMasterClasserPromises);
|
||||
}
|
||||
};
|
||||
|
||||
function _isOnQuest (user, progress, group) {
|
||||
|
||||
@@ -120,6 +120,10 @@ let schema = new Schema({
|
||||
joinedChallenge: Boolean,
|
||||
invitedFriend: Boolean,
|
||||
lostMasterclasser: Boolean,
|
||||
mindOverMatter: Boolean,
|
||||
justAddWater: Boolean,
|
||||
backToBasics: Boolean,
|
||||
allYourBase: Boolean,
|
||||
},
|
||||
|
||||
backer: {
|
||||
|
||||
Reference in New Issue
Block a user