New cards — Congratulations, Get Well (#8655)

* Add card and achievement sprite for Congrats card

* Add data regarding Congrats card

* Add Get Well card

* Add Get Well images

* Add schema

* Remove `if (!target.flags) target.flags = {};` code from cards

* Remove white backgrounds for congrats sprites

* add inital tests for cards

* Fix card tests

* Fix invalid urls in tests

* Update POST-user_class_cast_spellId.test.js

* Update POST-user_class_cast_spellId.test.js

* Update POST-user_class_cast_spellId.test.js

* Update congrats card sprite

* Fix card logic

* Fix user schema

* Change achievement values for new cards to Number

* Resize congrats and getwell cards

This will make them be sized properly

* Separate Market from Drops

* Extract cards to new section

* fix(sprites): revert spritesheet changes

* Add flags if target does not have them
This commit is contained in:
MathWhiz
2017-06-06 19:04:54 -07:00
committed by Sabe Jones
parent d6c62262f1
commit c9427ad34c
14 changed files with 210 additions and 93 deletions

View File

@@ -221,6 +221,27 @@ describe('POST /user/class/cast/:spellId', () => {
expect(syncedGroupTask.value).to.equal(0); expect(syncedGroupTask.value).to.equal(0);
}); });
it('increases both user\'s achievement values', async () => {
let party = await createAndPopulateGroup({
members: 1,
});
let leader = party.groupLeader;
let recipient = party.members[0];
await leader.update({'stats.gp': 10});
await leader.post(`/user/class/cast/birthday?targetId=${recipient._id}`);
await leader.sync();
await recipient.sync();
expect(leader.achievements.birthday).to.equal(1);
expect(recipient.achievements.birthday).to.equal(1);
});
it('only increases user\'s achievement one if target == caster', async () => {
await user.update({'stats.gp': 10});
await user.post(`/user/class/cast/birthday?targetId=${user._id}`);
await user.sync();
expect(user.achievements.birthday).to.equal(1);
});
// TODO find a way to have sinon working in integration tests // TODO find a way to have sinon working in integration tests
// it doesn't work when tests are running separately from server // it doesn't work when tests are running separately from server
it('passes correct target to spell when targetType === \'task\''); it('passes correct target to spell when targetType === \'task\'');

View File

@@ -174,7 +174,7 @@ describe('achievements', () => {
}); });
it('card achievements exist with counts', () => { it('card achievements exist with counts', () => {
let cardTypes = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday']; let cardTypes = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday', 'congrats', 'getwell'];
cardTypes.forEach((card) => { cardTypes.forEach((card) => {
let cardAchiev = seasonalAchievs[`${card}Cards`]; let cardAchiev = seasonalAchievs[`${card}Cards`];

Binary file not shown.

After

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

View File

@@ -154,6 +154,7 @@
"achievementBewilder": "Savior of Mistiflying", "achievementBewilder": "Savior of Mistiflying",
"achievementBewilderText": "Helped defeat the Be-Wilder during the 2016 Spring Fling Event!", "achievementBewilderText": "Helped defeat the Be-Wilder during the 2016 Spring Fling Event!",
"checkOutProgress": "Check out my progress in Habitica!", "checkOutProgress": "Check out my progress in Habitica!",
"cards": "Cards",
"cardReceived": "Received a card!", "cardReceived": "Received a card!",
"cardReceivedFrom": "<%= cardType %> from <%= userName %>", "cardReceivedFrom": "<%= cardType %> from <%= userName %>",
"greetingCard": "Greeting Card", "greetingCard": "Greeting Card",
@@ -180,6 +181,25 @@
"birthday0": "Happy birthday to you!", "birthday0": "Happy birthday to you!",
"birthdayCardAchievementTitle": "Birthday Bonanza", "birthdayCardAchievementTitle": "Birthday Bonanza",
"birthdayCardAchievementText": "Many happy returns! Sent or received <%= count %> birthday cards.", "birthdayCardAchievementText": "Many happy returns! Sent or received <%= count %> birthday cards.",
"congratsCard": "Congratulations Card",
"congratsCardExplanation": "You both recieve the Congratulatory Companion achievement!",
"congratsCardNotes": "Send a Congratulations card to a party member.",
"congrats0": "Congratulations on your success!",
"congrats1": "I'm so proud of you!",
"congrats2": "Well done!",
"congrats3": "A round of applause for you!",
"congrats4": "Bask in your well-deserved success!",
"congratsCardAchievementTitle": "Congratulatory Companion",
"congratsCardAchievementText": "It's great to celebrate your friends' achievements! Sent or received <%= count %> congratulations cards.",
"getwellCard": "Get Well Card",
"getwellCardExplanation": "You both recieve the Caring Confidant achievement!",
"getwellCardNotes": "Send a Get Well card to a party member.",
"getwell0": "Hope you feel better soon!",
"getwell1": "Take care! <3",
"getwell2": "You're in my thoughts!",
"getwell3": "Sorry you're not feeling your best!",
"getwellCardAchievementTitle": "Caring Confidant",
"getwellCardAchievementText": "Well-wishes are always appreciated. Sent or received <%= count %> get well cards.",
"streakAchievement": "You earned a streak achievement!", "streakAchievement": "You earned a streak achievement!",
"firstStreakAchievement": "21-Day Streak", "firstStreakAchievement": "21-Day Streak",
"streakAchievementCount": "<%= streaks %> 21-Day Streaks", "streakAchievementCount": "<%= streaks %> 21-Day Streaks",

View File

@@ -181,7 +181,7 @@ let ultimateGearAchievs = ['healer', 'rogue', 'warrior', 'mage'].reduce((achievs
}, {}); }, {});
Object.assign(achievementsData, ultimateGearAchievs); Object.assign(achievementsData, ultimateGearAchievs);
let cardAchievs = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday'].reduce((achievs, type) => { let cardAchievs = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday', 'congrats', 'getwell'].reduce((achievs, type) => {
achievs[`${type}Cards`] = { achievs[`${type}Cards`] = {
icon: `achievement-${type}`, icon: `achievement-${type}`,
titleKey: `${type}CardAchievementTitle`, titleKey: `${type}CardAchievementTitle`,

View File

@@ -144,6 +144,16 @@ api.cardTypes = {
messageOptions: 1, messageOptions: 1,
yearRound: true, yearRound: true,
}, },
congrats: {
key: 'congrats',
messageOptions: 5,
yearRound: true,
},
getwell: {
key: 'getwell',
messageOptions: 4,
yearRound: true,
},
}; };
api.special = api.spells.special; api.special = api.spells.special;

View File

@@ -511,6 +511,62 @@ spells.special = {
if (!target.flags) target.flags = {}; if (!target.flags) target.flags = {};
target.flags.cardReceived = true; target.flags.cardReceived = true;
user.stats.gp -= 10;
},
},
congrats: {
text: t('congratsCard'),
mana: 0,
value: 10,
immediateUse: true,
silent: true,
target: 'user',
notes: t('congratsCardNotes'),
cast (user, target) {
if (user === target) {
if (!user.achievements.congrats) user.achievements.congrats = 0;
user.achievements.congrats++;
} else {
each([user, target], (u) => {
if (!u.achievements.congrats) u.achievements.congrats = 0;
u.achievements.congrats++;
});
}
if (!target.items.special.congratsReceived) target.items.special.congratsReceived = [];
target.items.special.congratsReceived.push(user.profile.name);
if (!target.flags) target.flags = {};
target.flags.cardReceived = true;
user.stats.gp -= 10;
},
},
getwell: {
text: t('getwellCard'),
mana: 0,
value: 10,
immediateUse: true,
silent: true,
target: 'user',
notes: t('getwellCardNotes'),
cast (user, target) {
if (user === target) {
if (!user.achievements.getwell) user.achievements.getwell = 0;
user.achievements.getwell++;
} else {
each([user, target], (u) => {
if (!u.achievements.getwell) u.achievements.getwell = 0;
u.achievements.getwell++;
});
}
if (!target.items.special.getwellReceived) target.items.special.getwellReceived = [];
target.items.special.getwellReceived.push(user.profile.name);
if (!target.flags) target.flags = {};
target.flags.cardReceived = true;
user.stats.gp -= 10; user.stats.gp -= 10;
}, },
}, },

View File

@@ -240,7 +240,7 @@ function _getSeasonalAchievements (user, language) {
_addPlural(result, user, {path: 'costumeContests', language}); _addPlural(result, user, {path: 'costumeContests', language});
let cardAchievements = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday']; let cardAchievements = ['greeting', 'thankyou', 'nye', 'valentine', 'birthday', 'congrats', 'getwell'];
cardAchievements.forEach(path => { cardAchievements.forEach(path => {
_addSimpleWithCount(result, user, {path, key: `${path}Cards`, language}); _addSimpleWithCount(result, user, {path, key: `${path}Cards`, language});
}); });

View File

@@ -111,6 +111,8 @@ let schema = new Schema({
birthday: Number, birthday: Number,
partyUp: Boolean, partyUp: Boolean,
partyOn: Boolean, partyOn: Boolean,
congrats: Number,
getwell: Number,
royallyLoyal: Boolean, royallyLoyal: Boolean,
joinedGuild: Boolean, joinedGuild: Boolean,
}, },
@@ -288,6 +290,10 @@ let schema = new Schema({
thankyouReceived: Array, thankyouReceived: Array,
birthday: {type: Number, default: 0}, birthday: {type: Number, default: 0},
birthdayReceived: Array, birthdayReceived: Array,
congrats: {type: Number, default: 0},
congratsReceived: Array,
getwell: {type: Number, default: 0},
getwellReceived: Array,
}, },
// -------------- Animals ------------------- // -------------- Animals -------------------

View File

@@ -79,93 +79,4 @@
ng-click='openCardsModal(type.key, type.messageOptions)') ng-click='openCardsModal(type.key, type.messageOptions)')
.badge.badge-info.stack-count {{user.items.special[received].length}} .badge.badge-info.stack-count {{user.items.special[received].length}}
.col-md-6.border-left include market.jade
h2=env.t('market')
.npc_alex_container
.pull-left-sm.col-centered(class="#{env.worldDmg.market ? 'npc_alex_broken' : 'npc_alex'}")
.popover.static-popover.fade.right.in.pull-left-sm
.arrow.hidden-xs
h3.popover-title
a(target='_blank', href='http://www.kickstarter.com/profile/523661924')=env.t('alexander')
.popover-content
p=env.t('welcomeMarket')
hr(ng-show='selectedEgg || selectedPotion || selectedFood')
div(ng-show='selectedEgg || selectedPotion || selectedFood')
.pull-left.customize-option(class='Pet_Egg_{{selectedEgg.key}}' ng-show='selectedEgg')
p(ng-show='selectedEgg')
!=env.t('displayEggForGold', {itemType: "{{selectedEgg.text()}}"})
.pull-left.customize-option(class='Pet_HatchingPotion_{{selectedPotion.key}}' ng-show='selectedPotion')
p(ng-show='selectedPotion')
!=env.t('displayPotionForGold', {itemType: "{{selectedPotion.text()}}"})
.pull-left.customize-option(class='Pet_Food_{{selectedFood.key}}' ng-show='selectedFood')
p(ng-show='selectedFood')
!=env.t('displayItemForGold', {itemType: "{{selectedFood.text()}}"})
.clearfix
button.btn.btn-primary.btn-block(ng-show='selectedEgg', ng-click='sellInventory()')=env.t('sellForGold', {itemType: "{{selectedEgg.text()}}", gold: "{{selectedEgg.value}}"})
button.btn.btn-primary.btn-block(ng-show='selectedPotion', ng-click='sellInventory()')=env.t('sellForGold', {itemType: "{{selectedPotion.text()}}", gold: "{{selectedPotion.value}}"})
button.btn.btn-primary.btn-block(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text()}}", gold: "{{selectedFood.value}}"})
menu.inventory-list(type='list')
li.customize-menu(ng-repeat='category in marketShopCategories')
menu.pets-menu(label='{{category.text}}', ng-if='category.items.length > 0')
p.muted(ng-bind-html='category.notes')
div(ng-repeat='item in category.items')
button.customize-option(class='{{item.class}}',
popover='{{item.notes}}', popover-append-to-body='true',
popover-title!='{{item.text}}',
popover-trigger='mouseenter', popover-placement='top',
ng-click='purchase(item.purchaseType, item)')
p {{item.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems(ng-if='item.currency === "gems"')
span(class='shop_gold', ng-if='item.currency === "gold"')
li.customize-menu
menu.pets-menu(label=env.t('special'))
div
button.customize-option(class='inventory_special_fortify',
popover=env.t('fortifyPop'),
popover-title=env.t('fortifyName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("reroll")')
p
| 4&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-show='user.flags.rebirthEnabled')
button.customize-option(class='rebirth_orb',
popover=env.t('rebirthPop'), popover-title=env.t('rebirthName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("rebirth")')
p(ng-show='user.stats.lvl < 100')
| 6&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-show='petCount >= 90 || mountCount >= 90')
button.customize-option(popover=env.t('petKeyPop'), popover-title=env.t('petKeyName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("pet-key", {size:"lg", controller:"InventoryCtrl"})', class='pet_key')
p(ng-show='petCount < 90 || mountCount < 90 || !user.achievements.triadBingo')
| 4&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-if='user.purchased.plan.customerId', ng-class='::{transparent:(Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought) < 1}')
button.customize-option(popover=env.t('subGemPop'), popover-title=env.t('subGemName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='User.purchase({params:{type:"gems",key:"gem"}})')
span.Pet_Currency_Gem.inline-gems
.badge.badge-success.stack-count {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}
p
| 20&nbsp;
span.shop_gold
div(ng-repeat='type in Content.cardTypes', ng-show='type.yearRound')
button.customize-option(class='inventory_special_{{::type.key}}',
popover='{{::Content.spells.special[type.key].notes()}}',
popover-title='{{::Content.spells.special[type.key].text()}}',
popover-trigger='mouseenter', popover-placement='right',
popover-append-to-body='true',
ng-click='castStart(Content.spells.special[type.key])')
p
| {{Content.spells.special[type.key].value}}
span(class='shop_gold')

View File

@@ -0,0 +1,93 @@
.col-md-6.border-left
h2=env.t('market')
.npc_alex_container
.pull-left-sm.col-centered(class="#{env.worldDmg.market ? 'npc_alex_broken' : 'npc_alex'}")
.popover.static-popover.fade.right.in.pull-left-sm
.arrow.hidden-xs
h3.popover-title
a(target='_blank', href='http://www.kickstarter.com/profile/523661924')=env.t('alexander')
.popover-content
p=env.t('welcomeMarket')
hr(ng-show='selectedEgg || selectedPotion || selectedFood')
div(ng-show='selectedEgg || selectedPotion || selectedFood')
.pull-left.customize-option(class='Pet_Egg_{{selectedEgg.key}}' ng-show='selectedEgg')
p(ng-show='selectedEgg')
!=env.t('displayEggForGold', {itemType: "{{selectedEgg.text()}}"})
.pull-left.customize-option(class='Pet_HatchingPotion_{{selectedPotion.key}}' ng-show='selectedPotion')
p(ng-show='selectedPotion')
!=env.t('displayPotionForGold', {itemType: "{{selectedPotion.text()}}"})
.pull-left.customize-option(class='Pet_Food_{{selectedFood.key}}' ng-show='selectedFood')
p(ng-show='selectedFood')
!=env.t('displayItemForGold', {itemType: "{{selectedFood.text()}}"})
.clearfix
button.btn.btn-primary.btn-block(ng-show='selectedEgg', ng-click='sellInventory()')=env.t('sellForGold', {itemType: "{{selectedEgg.text()}}", gold: "{{selectedEgg.value}}"})
button.btn.btn-primary.btn-block(ng-show='selectedPotion', ng-click='sellInventory()')=env.t('sellForGold', {itemType: "{{selectedPotion.text()}}", gold: "{{selectedPotion.value}}"})
button.btn.btn-primary.btn-block(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text()}}", gold: "{{selectedFood.value}}"})
menu.inventory-list(type='list')
li.customize-menu(ng-repeat='category in marketShopCategories')
menu.pets-menu(label='{{category.text}}', ng-if='category.items.length > 0')
p.muted(ng-bind-html='category.notes')
div(ng-repeat='item in category.items')
button.customize-option(class='{{item.class}}',
popover='{{item.notes}}', popover-append-to-body='true',
popover-title!='{{item.text}}',
popover-trigger='mouseenter', popover-placement='top',
ng-click='purchase(item.purchaseType, item)')
p {{item.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems(ng-if='item.currency === "gems"')
span(class='shop_gold', ng-if='item.currency === "gold"')
li.customize-menu
menu.pets-menu(label=env.t('cards'))
div(ng-repeat='type in Content.cardTypes', ng-show='type.yearRound')
button.customize-option(class='inventory_special_{{::type.key}}',
popover='{{::Content.spells.special[type.key].notes()}}',
popover-title='{{::Content.spells.special[type.key].text()}}',
popover-trigger='mouseenter', popover-placement='right',
popover-append-to-body='true',
ng-click='castStart(Content.spells.special[type.key])')
p
| {{Content.spells.special[type.key].value}}
span(class='shop_gold')
li.customize-menu
menu.pets-menu(label=env.t('special'))
div
button.customize-option(class='inventory_special_fortify',
popover=env.t('fortifyPop'),
popover-title=env.t('fortifyName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("reroll")')
p
| 4&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-show='user.flags.rebirthEnabled')
button.customize-option(class='rebirth_orb',
popover=env.t('rebirthPop'), popover-title=env.t('rebirthName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("rebirth")')
p(ng-show='user.stats.lvl < 100')
| 6&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-show='petCount >= 90 || mountCount >= 90')
button.customize-option(popover=env.t('petKeyPop'), popover-title=env.t('petKeyName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='openModal("pet-key", {size:"lg", controller:"InventoryCtrl"})', class='pet_key')
p(ng-show='petCount < 90 || mountCount < 90 || !user.achievements.triadBingo')
| 4&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-if='user.purchased.plan.customerId', ng-class='::{transparent:(Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought) < 1}')
button.customize-option(popover=env.t('subGemPop'), popover-title=env.t('subGemName'),
popover-trigger='mouseenter', popover-placement='top',
popover-append-to-body='true',
ng-click='User.purchase({params:{type:"gems",key:"gem"}})')
span.Pet_Currency_Gem.inline-gems
.badge.badge-success.stack-count {{Shared.planGemLimits.convCap + User.user.purchased.plan.consecutive.gemCapExtra - User.user.purchased.plan.gemsBought}}
p
| 20&nbsp;
span.shop_gold