Implemented new Achievement and Badge: Joined a Challenge (Fixes #8613) (#8761)

* Added image

* Added new achievement to user schema

* Added new achievement to content

* Added new achievement to libs

* Added achievement text to locale

* Added achievement to notification model and controller

* Grant achievement on joining or creating first challenge

* Added achievement to modal template

* Compiled new sprites

* Added integration tests

* Fix linting error
This commit is contained in:
Kevin Smith
2017-06-07 17:43:16 +01:00
committed by Sabe Jones
parent 234328f2ba
commit c5d9ee1e0a
16 changed files with 503 additions and 461 deletions

View File

@@ -304,5 +304,15 @@ describe('POST /challenges', () => {
await expect(groupLeader.sync()).to.eventually.have.property('challenges').to.include(challenge._id); await expect(groupLeader.sync()).to.eventually.have.property('challenges').to.include(challenge._id);
}); });
it('awards achievement if this is creator\'s first challenge', async () => {
await groupLeader.post('/challenges', {
group: group._id,
name: 'Test Challenge',
shortName: 'TC Label',
});
groupLeader = await groupLeader.sync();
expect(groupLeader.achievements.joinedChallenge).to.be.true;
});
}); });
}); });

View File

@@ -123,5 +123,12 @@ describe('POST /challenges/:challengeId/join', () => {
await expect(authorizedUser.get('/tags')).to.eventually.have.length(userTagsLength + 1); await expect(authorizedUser.get('/tags')).to.eventually.have.length(userTagsLength + 1);
}); });
it('awards achievement if this is user\'s first challenge', async () => {
await authorizedUser.post(`/challenges/${challenge._id}/join`);
await authorizedUser.sync();
expect(authorizedUser.achievements.joinedChallenge).to.be.true;
});
}); });
}); });

View File

@@ -100,8 +100,8 @@ describe('POST /challenges/:challengeId/winner/:winnerId', () => {
await sleep(0.5); await sleep(0.5);
await expect(winningUser.sync()).to.eventually.have.deep.property('achievements.challenges').to.include(challenge.name); await expect(winningUser.sync()).to.eventually.have.deep.property('achievements.challenges').to.include(challenge.name);
expect(winningUser.notifications.length).to.equal(1); expect(winningUser.notifications.length).to.equal(2); // 2 because winningUser just joined the challenge, which now awards an achievement
expect(winningUser.notifications[0].type).to.equal('WON_CHALLENGE'); expect(winningUser.notifications[1].type).to.equal('WON_CHALLENGE');
}); });
it('gives winner gems as reward', async () => { it('gives winner gems as reward', async () => {

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

After

Width:  |  Height:  |  Size: 506 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -147,6 +147,10 @@ habitrpg.controller('NotificationCtrl',
$rootScope.playSound('Achievement_Unlocked'); $rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('joinedGuild', {size: 'md'}); Achievement.displayAchievement('joinedGuild', {size: 'md'});
break; break;
case 'CHALLENGE_JOINED_ACHIEVEMENT':
$rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('joinedChallenge', {size: 'md'});
break;
case 'NEW_CONTRIBUTOR_LEVEL': case 'NEW_CONTRIBUTOR_LEVEL':
$rootScope.playSound('Achievement_Unlocked'); $rootScope.playSound('Achievement_Unlocked');
Achievement.displayAchievement('contributor', {size: 'md'}); Achievement.displayAchievement('contributor', {size: 'md'});

File diff suppressed because it is too large Load Diff

View File

@@ -79,5 +79,7 @@
"onlyChalLeaderEditTasks": "Tasks belonging to a challenge can only be edited by the leader.", "onlyChalLeaderEditTasks": "Tasks belonging to a challenge can only be edited by the leader.",
"userAlreadyInChallenge": "User is already participating in this challenge.", "userAlreadyInChallenge": "User is already participating in this challenge.",
"cantOnlyUnlinkChalTask": "Only broken challenges tasks can be unlinked.", "cantOnlyUnlinkChalTask": "Only broken challenges tasks can be unlinked.",
"shortNameTooShort": "Tag Name must have at least 3 characters." "shortNameTooShort": "Tag Name must have at least 3 characters.",
"joinedChallenge": "Joined a Challenge",
"joinedChallengeText": "This user put themselves to the test by joining a Challenge!"
} }

View File

@@ -107,6 +107,11 @@ let basicAchievs = {
titleKey: 'joinedGuild', titleKey: 'joinedGuild',
textKey: 'joinedGuildText', textKey: 'joinedGuildText',
}, },
joinedChallenge: {
icon: 'achievement-challenge',
titleKey: 'joinedChallenge',
textKey: 'joinedChallengeText',
},
}; };
Object.assign(achievementsData, basicAchievs); Object.assign(achievementsData, basicAchievs);

View File

@@ -181,6 +181,7 @@ function _getBasicAchievements (user, language) {
_addSimple(result, user, {path: 'partyOn', language}); _addSimple(result, user, {path: 'partyOn', language});
_addSimple(result, user, {path: 'joinedGuild', language}); _addSimple(result, user, {path: 'joinedGuild', language});
_addSimple(result, user, {path: 'royallyLoyal', language}); _addSimple(result, user, {path: 'royallyLoyal', language});
_addSimple(result, user, {path: 'joinedChallenge', language});
_addSimpleWithMasterCount(result, user, {path: 'beastMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'beastMaster', language});
_addSimpleWithMasterCount(result, user, {path: 'mountMaster', language}); _addSimpleWithMasterCount(result, user, {path: 'mountMaster', language});

View File

@@ -228,6 +228,12 @@ api.createChallenge = {
let challengeValidationErrors = challenge.validateSync(); let challengeValidationErrors = challenge.validateSync();
if (challengeValidationErrors) throw challengeValidationErrors; if (challengeValidationErrors) throw challengeValidationErrors;
// Add achievement if user's first challenge
if (!user.achievements.joinedChallenge) {
user.achievements.joinedChallenge = true;
user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT');
}
let results = await Bluebird.all([challenge.save({ let results = await Bluebird.all([challenge.save({
validateBeforeSave: false, // already validate validateBeforeSave: false, // already validate
}), group.save()]); }), group.save()]);
@@ -286,6 +292,12 @@ api.joinChallenge = {
challenge.memberCount += 1; challenge.memberCount += 1;
// Add achievement if user's first challenge
if (!user.achievements.joinedChallenge) {
user.achievements.joinedChallenge = true;
user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT');
}
// Add all challenge's tasks to user's tasks and save the challenge // Add all challenge's tasks to user's tasks and save the challenge
let results = await Bluebird.all([challenge.syncToUser(user), challenge.save()]); let results = await Bluebird.all([challenge.syncToUser(user), challenge.save()]);

View File

@@ -115,6 +115,7 @@ let schema = new Schema({
getwell: Number, getwell: Number,
royallyLoyal: Boolean, royallyLoyal: Boolean,
joinedGuild: Boolean, joinedGuild: Boolean,
joinedChallenge: Boolean,
}, },
backer: { backer: {

View File

@@ -20,6 +20,7 @@ const NOTIFICATION_TYPES = [
'BOSS_DAMAGE', // Not used currently but kept to avoid validation errors 'BOSS_DAMAGE', // Not used currently but kept to avoid validation errors
'GUILD_PROMPT', 'GUILD_PROMPT',
'GUILD_JOINED_ACHIEVEMENT', 'GUILD_JOINED_ACHIEVEMENT',
'CHALLENGE_JOINED_ACHIEVEMENT',
]; ];
const Schema = mongoose.Schema; const Schema = mongoose.Schema;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 KiB

After

Width:  |  Height:  |  Size: 506 KiB

View File

@@ -163,3 +163,14 @@ script(id='modals/achievements/joinedGuild.html', type='text/ng-template')
br br
button.btn.btn-primary(ng-click='$close()')=env.t('huzzah') button.btn.btn-primary(ng-click='$close()')=env.t('huzzah')
+achievementFooter +achievementFooter
// Joined Challenge
script(id='modals/achievements/joinedChallenge.html', type='text/ng-template')
.modal-content(style='min-width:28em')
.modal-body.text-center
h3(style='margin-bottom:0')=env.t('modalAchievement')
+achievementAvatar('challenge',0)
p=env.t('joinedChallengeText')
br
button.btn.btn-primary(ng-click='$close()')=env.t('huzzah')
+achievementFooter