mirror of
https://github.com/sudoxnym/habitica.git
synced 2026-05-19 20:28:53 +00:00
Adventure Guide Prep (#11883)
* WIP(adventure): prereqs * WIP(drops): new modal * WIP(adventure): analytics fixes etc * feat(adventure): random egg+potion on 2nd task * fix(lint): noworkies * fix(modal): correctly construct classes * fix(tests): expectations and escape * fix(first-drops): address comments * fix(first-drops): don't give random drops until first drops * fix(drops): remove more Level 3 references * refactor(drops): no need for cloning * refactor(drops): unnecessary export * fix(first-drops): force sync * fix(first-drops): move to server * fix(first-drops): escape in case we get here with >0 items * fix(lint): line length * fix(pet-food): remove unused string
This commit is contained in:
parent
db1bda1bcd
commit
bd8e67a2ea
31 changed files with 273 additions and 144 deletions
|
|
@ -125,7 +125,7 @@ describe('PUT /user', () => {
|
|||
context('Sub-Level Protected Operations', () => {
|
||||
const protectedOperations = {
|
||||
'class stat': { 'stats.class': 'wizard' },
|
||||
'flags unless whitelisted': { 'flags.dropsEnabled': true },
|
||||
'flags unless whitelisted': { 'flags.chatRevoked': true },
|
||||
webhooks: { 'preferences.webhooks': [1, 2, 3] },
|
||||
sleep: { 'preferences.sleep': true },
|
||||
'disable classes': { 'preferences.disableClasses': true },
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ describe('PUT /user', () => {
|
|||
context('Sub-Level Protected Operations', () => {
|
||||
const protectedOperations = {
|
||||
'class stat': { 'stats.class': 'wizard' },
|
||||
'flags unless whitelisted': { 'flags.dropsEnabled': true },
|
||||
'flags unless whitelisted': { 'flags.chatRevoked': true },
|
||||
webhooks: { 'preferences.webhooks': [1, 2, 3] },
|
||||
sleep: { 'preferences.sleep': true },
|
||||
'disable classes': { 'preferences.disableClasses': true },
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ describe('common.fns.randomDrop', () => {
|
|||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
user._tmp = user._tmp ? user._tmp : {};
|
||||
user.items.eggs.Wolf = 0;
|
||||
task = generateTodo({ userId: user._id });
|
||||
predictableRandom = sandbox.stub().returns(0.5);
|
||||
});
|
||||
|
|
@ -34,10 +35,17 @@ describe('common.fns.randomDrop', () => {
|
|||
|
||||
context('drops enabled', () => {
|
||||
beforeEach(() => {
|
||||
user.flags.dropsEnabled = true;
|
||||
task.priority = 100000;
|
||||
});
|
||||
|
||||
it('awards an egg and a hatching potion if user has never received any', () => {
|
||||
delete user.items.eggs.Wolf;
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
||||
expect(user._tmp.firstDrops.egg).to.be.a.string;
|
||||
expect(user._tmp.firstDrops.hatchingPotion).to.be.a.string;
|
||||
});
|
||||
|
||||
it('does nothing if user.items.lastDrop.count is exceeded', () => {
|
||||
user.items.lastDrop.count = 100;
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
|
@ -46,7 +54,6 @@ describe('common.fns.randomDrop', () => {
|
|||
|
||||
it('drops something when the task is a todo', () => {
|
||||
expect(user._tmp).to.eql({});
|
||||
user.flags.dropsEnabled = true;
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
|
@ -56,7 +63,6 @@ describe('common.fns.randomDrop', () => {
|
|||
it('drops something when the task is a habit', () => {
|
||||
task = generateHabit({ userId: user._id });
|
||||
expect(user._tmp).to.eql({});
|
||||
user.flags.dropsEnabled = true;
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
|
@ -66,7 +72,6 @@ describe('common.fns.randomDrop', () => {
|
|||
it('drops something when the task is a daily', () => {
|
||||
task = generateDaily({ userId: user._id });
|
||||
expect(user._tmp).to.eql({});
|
||||
user.flags.dropsEnabled = true;
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
|
@ -76,7 +81,6 @@ describe('common.fns.randomDrop', () => {
|
|||
it('drops something when the task is a reward', () => {
|
||||
task = generateReward({ userId: user._id });
|
||||
expect(user._tmp).to.eql({});
|
||||
user.flags.dropsEnabled = true;
|
||||
predictableRandom.returns(0.1);
|
||||
|
||||
randomDrop(user, { task, predictableRandom });
|
||||
|
|
|
|||
|
|
@ -110,13 +110,6 @@ describe('common.fns.updateStats', () => {
|
|||
expect(user.stats.points).to.eql(10);
|
||||
});
|
||||
|
||||
it('add user notification when drops are enabled', () => {
|
||||
user.stats.lvl = 3;
|
||||
updateStats(user, { });
|
||||
expect(user.addNotification).to.be.calledOnce;
|
||||
expect(user.addNotification).to.be.calledWith('DROPS_ENABLED');
|
||||
});
|
||||
|
||||
it('add user notification when the user levels up', () => {
|
||||
const initialLvl = user.stats.lvl;
|
||||
updateStats(user, {
|
||||
|
|
@ -131,7 +124,7 @@ describe('common.fns.updateStats', () => {
|
|||
it('add user notification when rebirth is enabled', () => {
|
||||
user.stats.lvl = 51;
|
||||
updateStats(user, { });
|
||||
expect(user.addNotification).to.be.calledTwice; // once is for drops enabled
|
||||
expect(user.addNotification).to.be.calledOnce;
|
||||
expect(user.addNotification).to.be.calledWith('REBIRTH_ENABLED');
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -173,7 +173,6 @@ describe('shared.ops.rebirth', () => {
|
|||
|
||||
it('resets a user\'s flags', () => {
|
||||
user.flags.itemsEnabled = true;
|
||||
user.flags.dropsEnabled = true;
|
||||
user.flags.classSelected = true;
|
||||
user.flags.rebirthEnabled = true;
|
||||
user.flags.levelDrops = { test: 'test' };
|
||||
|
|
@ -181,7 +180,6 @@ describe('shared.ops.rebirth', () => {
|
|||
rebirth(user);
|
||||
|
||||
expect(user.flags.itemsEnabled).to.be.false;
|
||||
expect(user.flags.dropsEnabled).to.be.false;
|
||||
expect(user.flags.classSelected).to.be.false;
|
||||
expect(user.flags.rebirthEnabled).to.be.false;
|
||||
expect(user.flags.levelDrops).to.be.empty;
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
<template>
|
||||
<b-modal
|
||||
id="drops-enabled"
|
||||
:title="$t('dropsEnabled')"
|
||||
size="lg"
|
||||
:hide-footer="true"
|
||||
>
|
||||
<div class="modal-body">
|
||||
<div class="col-6 offset-3 text-center">
|
||||
<p></p>
|
||||
<div class="item-drop-icon Pet_Egg_Wolf"></div>
|
||||
<span v-html="firstDropText"></span>
|
||||
<p></p>
|
||||
<div class="item-drop-icon Pet_Currency_Gem"></div>
|
||||
<span v-html="$t('useGems')"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="col-12 text-center">
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
@click="close()"
|
||||
>
|
||||
{{ $t('close') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.item-drop-icon {
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { mapState } from '@/libs/store';
|
||||
import * as eggs from '@/../../common/script/content/eggs';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
eggs,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState({ user: 'user.data' }),
|
||||
firstDropText () {
|
||||
return this.$t('firstDrop', {
|
||||
eggText: this.eggs.all.Wolf.text(),
|
||||
eggNotes: this.eggs.all.Wolf.notes(),
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$root.$emit('bv::hide::modal', 'drops-enabled');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
131
website/client/src/components/achievements/firstDrops.vue
Normal file
131
website/client/src/components/achievements/firstDrops.vue
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<b-modal
|
||||
id="first-drops"
|
||||
size="md"
|
||||
:hide-header="true"
|
||||
:hide-footer="true"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div
|
||||
class="modal-close"
|
||||
@click="close()"
|
||||
>
|
||||
<div
|
||||
class="svg-icon"
|
||||
v-html="icons.close"
|
||||
v-once
|
||||
></div>
|
||||
</div>
|
||||
<h2
|
||||
class="mt-3 mb-4"
|
||||
v-once
|
||||
>{{ $t('foundNewItems') }}</h2>
|
||||
<div class="d-flex justify-content-center">
|
||||
<div
|
||||
class="item-box ml-auto mr-3"
|
||||
:class="eggClass"
|
||||
>
|
||||
</div>
|
||||
<div
|
||||
class="item-box mr-auto"
|
||||
:class="potionClass"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<p
|
||||
class="mt-4"
|
||||
v-once
|
||||
>{{ $t('foundNewItemsExplanation') }}</p>
|
||||
<p
|
||||
class="strong mb-4"
|
||||
v-once
|
||||
>{{ $t('foundNewItemsCTA') }}</p>
|
||||
<button
|
||||
class="btn btn-primary mb-2"
|
||||
@click="toInventory()"
|
||||
v-once
|
||||
>
|
||||
{{ $t('letsgo') }}
|
||||
</button>
|
||||
</div>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
#first-drops {
|
||||
.modal-body {
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
margin-top: 15vh;
|
||||
width: 21rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~@/assets/scss/colors.scss';
|
||||
|
||||
h2 {
|
||||
color: $purple-200;
|
||||
}
|
||||
|
||||
.item-box {
|
||||
background-color: $gray-600;
|
||||
}
|
||||
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
padding: 4px;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
cursor: pointer;
|
||||
.svg-icon {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import closeIcon from '@/assets/svg/close.svg';
|
||||
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
icons: Object.freeze({
|
||||
close: closeIcon,
|
||||
}),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
eggClass () {
|
||||
return `Pet_Egg_${this.$store.state.firstDropsOptions.egg}`;
|
||||
},
|
||||
potionClass () {
|
||||
return `Pet_HatchingPotion_${this.$store.state.firstDropsOptions.hatchingPotion}`;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.$store.state.firstDropsOptions = {
|
||||
egg: '',
|
||||
hatchingPotion: '',
|
||||
};
|
||||
this.$root.$emit('habitica::dismiss-modal', 'first-drops');
|
||||
},
|
||||
toInventory () {
|
||||
this.$router.push('/inventory/items');
|
||||
this.close();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -13,7 +13,6 @@
|
|||
<testing />
|
||||
<testingletiant />
|
||||
<rebirth-enabled />
|
||||
<drops-enabled />
|
||||
<contributor />
|
||||
<won-challenge />
|
||||
<ultimate-gear />
|
||||
|
|
@ -34,6 +33,7 @@
|
|||
<lost-masterclasser />
|
||||
<mind-over-matter />
|
||||
<onboarding-complete />
|
||||
<first-drops />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -128,7 +128,6 @@ import questInvitation from './achievements/questInvitation';
|
|||
import testing from './achievements/testing';
|
||||
import testingletiant from './achievements/testingletiant';
|
||||
import rebirthEnabled from './achievements/rebirthEnabled';
|
||||
import dropsEnabled from './achievements/dropsEnabled';
|
||||
import contributor from './achievements/contributor';
|
||||
import invitedFriend from './achievements/invitedFriend';
|
||||
import joinedChallenge from './achievements/joinedChallenge';
|
||||
|
|
@ -144,6 +143,7 @@ import mindOverMatter from './achievements/mindOverMatter';
|
|||
import loginIncentives from './achievements/login-incentives';
|
||||
import onboardingComplete from './achievements/onboardingComplete';
|
||||
import verifyUsername from './settings/verifyUsername';
|
||||
import firstDrops from './achievements/firstDrops';
|
||||
|
||||
const NOTIFICATIONS = {
|
||||
CHALLENGE_JOINED_ACHIEVEMENT: {
|
||||
|
|
@ -333,7 +333,6 @@ export default {
|
|||
testing,
|
||||
testingletiant,
|
||||
rebirthEnabled,
|
||||
dropsEnabled,
|
||||
contributor,
|
||||
loginIncentives,
|
||||
verifyUsername,
|
||||
|
|
@ -342,12 +341,12 @@ export default {
|
|||
mindOverMatter,
|
||||
justAddWater,
|
||||
onboardingComplete,
|
||||
firstDrops,
|
||||
},
|
||||
mixins: [notifications, guide],
|
||||
data () {
|
||||
// Levels that already display modals and should not trigger generic Level Up
|
||||
const unlockLevels = {
|
||||
3: 'drop system',
|
||||
10: 'class system',
|
||||
50: 'Orb of Rebirth',
|
||||
};
|
||||
|
|
@ -361,7 +360,7 @@ export default {
|
|||
const handledNotifications = {};
|
||||
|
||||
[
|
||||
'GUILD_PROMPT', 'DROPS_ENABLED', 'REBIRTH_ENABLED', 'WON_CHALLENGE', 'STREAK_ACHIEVEMENT',
|
||||
'GUILD_PROMPT', 'REBIRTH_ENABLED', 'WON_CHALLENGE', 'STREAK_ACHIEVEMENT',
|
||||
'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',
|
||||
|
|
@ -369,7 +368,7 @@ export default {
|
|||
'ACHIEVEMENT_MOUNT_MASTER', 'ACHIEVEMENT_TRIAD_BINGO', 'ACHIEVEMENT_DUST_DEVIL', 'ACHIEVEMENT_ARID_AUTHORITY',
|
||||
'ACHIEVEMENT_MONSTER_MAGUS', 'ACHIEVEMENT_UNDEAD_UNDERTAKER', 'ACHIEVEMENT_PRIMED_FOR_PAINTING',
|
||||
'ACHIEVEMENT_PEARLY_PRO', 'ACHIEVEMENT_TICKLED_PINK', 'ACHIEVEMENT_ROSY_OUTLOOK', 'ACHIEVEMENT',
|
||||
'ONBOARDING_COMPLETE',
|
||||
'ONBOARDING_COMPLETE', 'FIRST_DROPS',
|
||||
].forEach(type => {
|
||||
handledNotifications[type] = true;
|
||||
});
|
||||
|
|
@ -732,6 +731,13 @@ export default {
|
|||
|
||||
// @TODO: Use factory function instead
|
||||
switch (notification.type) { // eslint-disable-line default-case
|
||||
case 'FIRST_DROPS':
|
||||
if (notification.data) {
|
||||
this.$store.state.firstDropsOptions.egg = notification.data.egg;
|
||||
this.$store.state.firstDropsOptions.hatchingPotion = notification.data.hatchingPotion;
|
||||
this.$root.$emit('bv::show::modal', 'first-drops');
|
||||
}
|
||||
break;
|
||||
case 'GUILD_PROMPT':
|
||||
// @TODO: I'm pretty sure we can find better names for these
|
||||
if (notification.data.textletiant === -1) {
|
||||
|
|
@ -740,9 +746,6 @@ export default {
|
|||
this.$root.$emit('bv::show::modal', 'testingletiant');
|
||||
}
|
||||
break;
|
||||
case 'DROPS_ENABLED':
|
||||
this.$root.$emit('bv::show::modal', 'drops-enabled');
|
||||
break;
|
||||
case 'REBIRTH_ENABLED':
|
||||
this.$root.$emit('bv::show::modal', 'rebirth-enabled');
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1051,6 +1051,7 @@ export default {
|
|||
const tmp = response.data.data._tmp || {};
|
||||
const { crit } = tmp;
|
||||
const { drop } = tmp;
|
||||
const { firstDrops } = tmp;
|
||||
const { quest } = tmp;
|
||||
|
||||
if (crit) {
|
||||
|
|
@ -1068,6 +1069,15 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
if (firstDrops) {
|
||||
if (!user.items.eggs[firstDrops.egg]) Vue.set(user.items.eggs, firstDrops.egg, 0);
|
||||
if (!user.items.hatchingPotions[firstDrops.hatchingPotion]) {
|
||||
Vue.set(user.items.hatchingPotions, firstDrops.hatchingPotion, 0);
|
||||
}
|
||||
user.items.eggs[firstDrops.egg] += 1;
|
||||
user.items.hatchingPotions[firstDrops.hatchingPotion] += 1;
|
||||
}
|
||||
|
||||
if (drop) {
|
||||
let dropText;
|
||||
let dropNotes;
|
||||
|
|
@ -1088,7 +1098,7 @@ export default {
|
|||
}
|
||||
|
||||
if (!user.items[type][drop.key]) {
|
||||
Vue.set(user, `items.${type}.${drop.key}`, 0);
|
||||
Vue.set(user.items[type], drop.key, 0);
|
||||
}
|
||||
user.items[type][drop.key] += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div ng-if="user.flags.dropsEnabled">
|
||||
<div>
|
||||
<h3 v-once>
|
||||
{{ $t('pets') }}
|
||||
</h3>
|
||||
|
|
|
|||
|
|
@ -131,6 +131,10 @@ export default function () {
|
|||
backer: {},
|
||||
contributor: {},
|
||||
},
|
||||
firstDropsOptions: {
|
||||
egg: '',
|
||||
hatchingPotion: '',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@
|
|||
"onboardingCompleteDesc": "You earned <strong>5 achievements</strong> and <strong class=\"gold-amount\">100</strong> gold for completing the list.",
|
||||
"showAllAchievements": "Show All <%= category %>",
|
||||
"hideAchievements": "Hide <%= category %>",
|
||||
"foundNewItems": "You found new items!",
|
||||
"foundNewItemsExplanation": "Completing tasks gives you a chance to find items, like eggs, hatching potions, and food.",
|
||||
"foundNewItemsCTA": "Head to your Inventory and try combining your new hatching potion and egg!",
|
||||
"achievementLostMasterclasser": "Quest Completionist: Masterclasser Series",
|
||||
"achievementLostMasterclasserText": "Completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!",
|
||||
"achievementLostMasterclasserModalText": "You completed all sixteen quests in the Masterclasser Quest Series and solved the mystery of the Lost Masterclasser!",
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"armoireText": "Enchanted Armoire",
|
||||
"armoireNotesFull": "Open the Armoire to randomly receive special Equipment, Experience, or food! Equipment pieces remaining:",
|
||||
"armoireLastItem": "You've found the last piece of rare Equipment in the Enchanted Armoire.",
|
||||
"armoireNotesEmpty": "The Armoire will have new Equipment in the first week of every month. Until then, keep clicking for Experience and Food!",
|
||||
"armoireNotesEmpty": "The Armoire will have new Equipment in the first week of every month. Until then, keep clicking for Experience and Pet Food!",
|
||||
|
||||
"dropEggWolfText": "Wolf",
|
||||
"dropEggWolfMountText": "Wolf",
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@
|
|||
"webFaqAnswer5": "The best way is to invite them to a Party with you by clicking \"Party\" in the navigation bar! Parties can go on quests, battle monsters, and cast skills to support each other. You can also join Guilds together (click on \"Guilds\" in the navigation bar). Guilds are chat rooms focusing on a shared interest or the pursuit of a common goal, and can be public or private. You can join as many Guilds as you'd like, but only one Party. For more detailed info, check out the wiki pages on [Parties](http://habitica.fandom.com/wiki/Party) and [Guilds](http://habitica.fandom.com/wiki/Guilds).",
|
||||
|
||||
"faqQuestion6": "How do I get a Pet or Mount?",
|
||||
"iosFaqAnswer6": "At level 3, you will unlock the Drop System. Every time you complete a task, you'll have a random chance at receiving an egg, a hatching potion, or a piece of food. They will be stored in Menu > Items.\n\n To hatch a Pet, you'll need an egg and a hatching potion. Tap on the egg to determine the species you want to hatch, and select \"Hatch Egg.\" Then choose a hatching potion to determine its color! Go to Menu > Pets to equip your new Pet to your avatar by clicking on it. \n\n You can also grow your Pets into Mounts by feeding them under Menu > Pets. Tap on a Pet, and then select \"Feed Pet\"! You'll have to feed a pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). Once you have a Mount, go to Menu > Mounts and tap on it to equip it to your avatar.\n\n You can also get eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"androidFaqAnswer6": "At level 3, you will unlock the Drop System. Every time you complete a task, you'll have a random chance at receiving an egg, a hatching potion, or a piece of food. They will be stored in Menu > Items.\n\n To hatch a Pet, you'll need an egg and a hatching potion. Tap on the egg to determine the species you want to hatch, and select \"Hatch with potion.\" Then choose a hatching potion to determine its color! To equip your new Pet, go to Menu > Stable > Pets, select a species, click on the desired Pet, and select \"Use\"(Your avatar doesn't update to reflect the change). \n\n You can also grow your Pets into Mounts by feeding them under Menu > Stable [ > Pets ]. Tap on a Pet, and then select \"Feed\"! You'll have to feed a pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). To equip your Mount, go to Menu > Stable > Mounts, select a species, click on the desired Mount, and select \"Use\"(Your avatar doesn't update to reflect the change).\n\n You can also get eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"webFaqAnswer6": "At level 3, you will unlock the Drop System. Every time you complete a task, you'll have a random chance at receiving an egg, a hatching potion, or a piece of food. They will be stored under Inventory > Items. To hatch a Pet, you'll need an egg and a hatching potion. Once you have both an egg and a potion, go to Inventory > Stable to hatch your pet by clicking on its image. Once you've hatched a pet, you can equip it by clicking on it. You can also grow your Pets into Mounts by feeding them under Inventory > Stable. Drag a piece of food from the action bar at the bottom of the screen and drop it on a pet to feed it! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). Once you have a Mount, click on it to equip it to your avatar. You can also get eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"iosFaqAnswer6": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. They will be stored in Menu > Items.\n\n To hatch a Pet, you'll need an Egg and a Hatching Potion. Tap on the Egg to determine the species you want to hatch, and select \"Hatch Egg.\" Then choose a Hatching Potion to determine its color! Go to Menu > Pets and click your new Pet to equip it to your Avatar. \n\n You can also grow your Pets into Mounts by feeding them under Menu > Pets. Tap on a Pet, and select \"Feed Pet\"! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). Once you have a Mount, go to Menu > Mounts and tap on it to equip it to your Avatar.\n\n You can also get Eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"androidFaqAnswer6": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. They will be stored in Menu > Items.\n\n To hatch a Pet, you'll need an Egg and a Hatching Potion. Tap on the Egg to determine the species you want to hatch, and select \"Hatch with Potion.\" Then choose a Hatching Potion to determine its color! To equip your new Pet, go to Menu > Stable > Pets, select a species, click on the desired Pet, and select \"Use\"(Your Avatar doesn't update to reflect the change). \n\n You can also grow your Pets into Mounts by feeding them under Menu > Stable [ > Pets ]. Tap on a Pet, and then select \"Feed\"! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). To equip your Mount, go to Menu > Stable > Mounts, select a species, click on the desired Mount, and select \"Use\"(Your Avatar doesn't update to reflect the change).\n\n You can also get Eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
"webFaqAnswer6": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. They will be stored under Inventory > Items. To hatch a Pet, you'll need an Egg and a Hatching Potion. Once you have both an Egg and a Hatching Potion, go to Inventory > Stable, and click on the image to hatch your Pet. Once you've hatched a Pet, you can equip it by clicking on it. You can also grow your Pets into Mounts by feeding them under Inventory > Stable. Drag a piece of Pet Food from the action bar at the bottom of the screen and drop it on a Pet to feed it! You'll have to feed a Pet many times before it becomes a Mount, but if you can figure out its favorite food, it will grow more quickly. Use trial and error, or [see the spoilers here](http://habitica.fandom.com/wiki/Food#Food_Preferences). Once you have a Mount, click on it to equip it to your Avatar. You can also get Eggs for Quest Pets by completing certain Quests. (See below to learn more about Quests.)",
|
||||
|
||||
"faqQuestion7": "How do I become a Warrior, Mage, Rogue, or Healer?",
|
||||
"iosFaqAnswer7": "At level 10, you can choose to become a Warrior, Mage, Rogue, or Healer. (All players start as Warriors by default.) Each Class has different equipment options, different Skills that they can cast after level 11, and different advantages. Warriors can easily damage Bosses, withstand more damage from their tasks, and help make their Party tougher. Mages can also easily damage Bosses, as well as level up quickly and restore Mana for their party. Rogues earn the most gold and find the most item drops, and they can help their Party do the same. Finally, Healers can heal themselves and their Party members.\n\n If you don't want to choose a Class immediately -- for example, if you are still working to buy all the gear of your current class -- you can click “Decide Later” and choose later under Menu > Choose Class.",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"noItemsAvailableForType": "You have no <%= type %>.",
|
||||
"foodItemType": "Food",
|
||||
"foodItemType": "Pet Food",
|
||||
"eggsItemType": "Eggs",
|
||||
"hatchingPotionsItemType": "Hatching Potions",
|
||||
"specialItemType": "Special items",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
"mattBoch": "Matt Boch",
|
||||
"mattShall": "Shall I bring you your steed, <%= name %>? Once you've fed a pet enough food to turn it into a mount, it will appear here. Click a mount to saddle up!",
|
||||
"mattBochText1": "Welcome to the Stable! I'm Matt, the beast master. Starting at level 3, you will find eggs and potions to hatch pets with. When you hatch a pet in the Market, it will appear here! Click a pet's image to add it to your avatar. Feed them with the food you find after level 3, and they'll grow into hardy mounts.",
|
||||
"mattBochText1": "Welcome to the Stable! I’m Matt, the beastmaster. Every time you complete a task, you'll have a random chance at receiving an Egg or a Hatching Potion to hatch Pets. When you hatch a Pet, it will appear here! Click a Pet's image to add it to your Avatar. Feed them with the Pet Food you find, and they'll grow into hardy Mounts.",
|
||||
|
||||
"welcomeToTavern": "Welcome to The Tavern!",
|
||||
"sleepDescription": "Need a break? Check into Daniel's Inn to pause some of Habitica's more difficult game mechanics:",
|
||||
|
|
@ -153,9 +153,9 @@
|
|||
"tourPartyPage": "Your Party will help you stay accountable. Invite friends to unlock a Quest Scroll!",
|
||||
"tourGuildsPage": "Guilds are common-interest chat groups created by the players, for the players. Browse through the list and join the Guilds that interest you. Be sure to check out the popular Habitica Help: Ask a Question guild, where anyone can ask questions about Habitica!",
|
||||
"tourChallengesPage": "Challenges are themed task lists created by users! Joining a Challenge will add its tasks to your account. Compete against other users to win Gem prizes!",
|
||||
"tourMarketPage": "Starting at Level 3, eggs and hatching potions drop randomly when you complete tasks. They appear here - use them to hatch pets! You can also buy items from the Market.",
|
||||
"tourMarketPage": "Every time you complete a task, you'll have a random chance at receiving an Egg, a Hatching Potion, or a piece of Pet Food. You can also buy these items here.",
|
||||
"tourHallPage": "Welcome to the Hall of Heroes, where open-source contributors to Habitica are honored. Whether through code, art, music, writing, or even just helpfulness, they have earned Gems, exclusive equipment, and prestigious titles. You can contribute to Habitica, too!",
|
||||
"tourPetsPage": "This is the Stable! After reaching level 3, you will gather pet eggs and hatching potions as you complete tasks. When you hatch a pet in the Market, it will appear here! Click a pet's image to add it to your avatar. Feed them with the food you find after level 3, and they'll grow into powerful mounts.",
|
||||
"tourPetsPage": "Welcome to the Stable! Every time you complete a task, you'll have a random chance at receiving an Egg or a Hatching Potion to hatch Pets. When you hatch a Pet, it will appear here! Click a Pet's image to add it to your Avatar. Feed them with the Pet Food you find and they'll grow into hardy Mounts.",
|
||||
"tourMountsPage": "Once you've fed a pet enough food to turn it into a mount, it will appear here. Click a mount to saddle up!",
|
||||
"tourEquipmentPage": "This is where your Equipment is stored! Your Battle Gear affects your Stats. If you want to show different Equipment on your avatar without changing your Stats, click \"Enable Costume.\"",
|
||||
"equipmentAlreadyOwned": "You already own that piece of equipment",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"webStep2Text": "Now, start tackling your goals from the list! As you complete tasks and check them off in Habitica, you will gain [Experience](http://habitica.fandom.com/wiki/Experience_Points), which helps you level up, and [Gold](http://habitica.fandom.com/wiki/Gold_Points), which allows you to purchase Rewards. If you fall into bad habits or miss your Dailies, you will lose [Health](http://habitica.fandom.com/wiki/Health_Points). In that way, the Habitica Experience and Health bars serve as a fun indicator of your progress toward your goals. You'll start seeing your real life improve as your character advances in the game.",
|
||||
|
||||
"step3": "Step 3: Customize and Explore Habitica",
|
||||
"webStep3Text": "Once you're familiar with the basics, you can get even more out of Habitica with these nifty features:\n * Organize your tasks with [tags](http://habitica.fandom.com/wiki/Tags) (edit a task to add them).\n * Customize your [avatar](http://habitica.fandom.com/wiki/Avatar) by clicking the user icon in the upper-right corner.\n * Buy your [Equipment](http://habitica.fandom.com/wiki/Equipment) under Rewards or from the [Shops](<%= shopUrl %>), and change it under [Inventory > Equipment](<%= equipUrl %>).\n * Connect with other users via the [Tavern](http://habitica.fandom.com/wiki/Tavern).\n * Starting at Level 3, hatch [Pets](http://habitica.fandom.com/wiki/Pets) by collecting [eggs](http://habitica.fandom.com/wiki/Eggs) and [hatching potions](http://habitica.fandom.com/wiki/Hatching_Potions). [Feed](http://habitica.fandom.com/wiki/Food) them to create [Mounts](http://habitica.fandom.com/wiki/Mounts).\n * At level 10: Choose a particular [class](http://habitica.fandom.com/wiki/Class_System) and then use class-specific [skills](http://habitica.fandom.com/wiki/Skills) (levels 11 to 14).\n * Form a party with your friends (by clicking [Party](<%= partyUrl %>) in the navigation bar) to stay accountable and earn a Quest scroll.\n * Defeat monsters and collect objects on [quests](http://habitica.fandom.com/wiki/Quests) (you will be given a quest at level 15).",
|
||||
"webStep3Text": "Once you're familiar with the basics, you can get even more out of Habitica with these nifty features:\n * Organize your Tasks with [tags](http://habitica.fandom.com/wiki/Tags) (edit a Task to add them).\n * Customize your [Avatar](http://habitica.fandom.com/wiki/Avatar) by clicking the user icon in the upper-right corner.\n * Buy your [Equipment](http://habitica.fandom.com/wiki/Equipment) under Rewards or from the [Shops](<%= shopUrl %>), and change it under [Inventory > Equipment](<%= equipUrl %>).\n * Connect with other users via the [Tavern](http://habitica.fandom.com/wiki/Tavern).\n * Hatch [Pets](http://habitica.fandom.com/wiki/Pets) by collecting [Eggs](http://habitica.fandom.com/wiki/Eggs) and [Hatching Potions](http://habitica.fandom.com/wiki/Hatching_Potions). [Feed](http://habitica.fandom.com/wiki/Food) them to create [Mounts](http://habitica.fandom.com/wiki/Mounts).\n * At level 10: Choose a particular [Class](http://habitica.fandom.com/wiki/Class_System) and then use Class-specific [skills](http://habitica.fandom.com/wiki/Skills) (levels 11 to 14).\n * Form a Party with your friends (by clicking [Party](<%= partyUrl %>) in the navigation bar) to stay accountable and earn a Quest scroll.\n * Defeat monsters and collect objects on [Quests](http://habitica.fandom.com/wiki/Quests) (you will be given a quest at level 15).",
|
||||
|
||||
"overviewQuestions": "Have questions? Check out the [FAQ](<%= faqUrl %>)! If your question isn't mentioned there, you can ask for further help in the [Habitica Help guild](<%= helpGuildUrl %>).\n\nGood luck with your tasks!"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,11 +48,9 @@
|
|||
"inventoryText": "Click an egg to see usable potions highlighted in green and then click one of the highlighted potions to hatch your pet. If no potions are highlighted, click that egg again to deselect it, and instead click a potion first to have the usable eggs highlighted. You can also sell unwanted drops to Alexander the Merchant.",
|
||||
"haveHatchablePet": "You have a <%= potion %> hatching potion and <%= egg %> egg to hatch this pet! <b>Click</b> the paw print to hatch.",
|
||||
"quickInventory": "Quick Inventory",
|
||||
"foodText": "food",
|
||||
"food": "Food and Saddles",
|
||||
"noFoodAvailable": "You don't have any Food.",
|
||||
"food": "Pet Food and Saddles",
|
||||
"noFoodAvailable": "You don't have any Pet Food.",
|
||||
"noSaddlesAvailable": "You don't have any Saddles.",
|
||||
"noFood": "You don't have any food or saddles.",
|
||||
"dropsExplanation": "Get these items faster with Gems if you don't want to wait for them to drop when completing a task. <a href=\"http://habitica.fandom.com/wiki/Drops\">Learn more about the drop system.</a>",
|
||||
"dropsExplanationEggs": "Spend Gems to get eggs more quickly, if you don't want to wait for standard eggs to drop, or to repeat Quests to earn Quest eggs. <a href=\"http://habitica.fandom.com/wiki/Drops\">Learn more about the drop system.</a>",
|
||||
"premiumPotionNoDropExplanation": "Magic Hatching Potions cannot be used on eggs received from Quests. The only way to get Magic Hatching Potions is by buying them below, not from random drops.",
|
||||
|
|
@ -73,9 +71,7 @@
|
|||
"triadBingoText": "Has found all 90 pets, all 90 mounts, and found all 90 pets AGAIN (HOW DID YOU DO THAT!)",
|
||||
"triadBingoText2": " and has released a full stable a total of <%= count %> time(s)",
|
||||
"triadBingoAchievement": "You have earned the \"Triad Bingo\" achievement for finding all the pets, taming all the mounts, and finding all the pets again!",
|
||||
"dropsEnabled": "Drops Enabled!",
|
||||
"itemDrop": "An item has dropped!",
|
||||
"firstDrop": "You've unlocked the Drop System! Now when you complete tasks, you have a small chance of finding an item, including eggs, potions, and food! You just found a <strong><%= eggText %> Egg</strong>! <%= eggNotes %>",
|
||||
"useGems": "If you've got your eye on a pet, but can't wait any longer for it to drop, use Gems in <strong>Inventory > Market</strong> to buy one!",
|
||||
"hatchAPot": "Hatch a new <%= potion %> <%= egg %>?",
|
||||
"hatchedPet": "You hatched a new <%= potion %> <%= egg %>!",
|
||||
|
|
@ -125,9 +121,9 @@
|
|||
"foodWikiText": "What does my pet like to eat?",
|
||||
"foodWikiUrl": "http://habitica.fandom.com/wiki/Food_Preferences",
|
||||
"welcomeStable": "Welcome to the Stable!",
|
||||
"welcomeStableText": "I'm Matt, the Beast Master. Starting at level 3, you can hatch Pets from Eggs by using Potions you find! When you hatch a Pet from your Inventory, it will appear here! Click a Pet's image to add it to your avatar. Feed them here with the Food you find after level 3, and they'll grow into hardy Mounts.",
|
||||
"welcomeStableText": "Welcome to the Stable! I’m Matt, the beastmaster. Every time you complete a task, you'll have a random chance at receiving an Egg or a Hatching Potion to hatch Pets. When you hatch a Pet, it will appear here! Click a Pet's image to add it to your Avatar. Feed them with the Pet Food you find and they'll grow into hardy Mounts.",
|
||||
"petLikeToEat": "What does my pet like to eat?",
|
||||
"petLikeToEatText": "Pets will grow no matter what you feed them, but they'll grow faster if you feed them the one food that they like best. Experiment to find out the pattern, or see the answers here: <br/> <a href=\"http://habitica.fandom.com/wiki/Food_Preferences\" target=\"_blank\">http://habitica.fandom.com/wiki/Food_Preferences</a>",
|
||||
"petLikeToEatText": "Pets will grow no matter what you feed them, but they'll grow faster if you feed them the one Pet Food that they like best. Experiment to find out the pattern, or see the answers here: <br/> <a href=\"http://habitica.fandom.com/wiki/Food_Preferences\" target=\"_blank\">http://habitica.fandom.com/wiki/Food_Preferences</a>",
|
||||
"filterByStandard": "Standard",
|
||||
"filterByMagicPotion": "Magic Potion",
|
||||
"filterByQuest": "Quest",
|
||||
|
|
@ -136,7 +132,7 @@
|
|||
"sortByColor": "Color",
|
||||
"sortByHatchable": "Hatchable",
|
||||
"hatch": "Hatch!",
|
||||
"foodTitle": "Food",
|
||||
"foodTitle": "Pet Food",
|
||||
"dragThisFood": "Drag this <%= foodName %> to a Pet and watch it grow!",
|
||||
"clickOnPetToFeed": "Click on a Pet to feed <%= foodName %> and watch it grow!",
|
||||
"dragThisPotion": "Drag this <%= potionName %> to an Egg and hatch a new pet!",
|
||||
|
|
|
|||
26
website/common/script/fns/firstDrops.js
Normal file
26
website/common/script/fns/firstDrops.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
import { drops as eggs } from '../content/eggs';
|
||||
import { drops as hatchingPotions } from '../content/hatching-potions';
|
||||
import randomVal from '../libs/randomVal';
|
||||
|
||||
export default function firstDrops (user) {
|
||||
const eggDrop = randomVal(eggs);
|
||||
const potionDrop = randomVal(hatchingPotions);
|
||||
|
||||
user.items.eggs = {
|
||||
...user.items.eggs,
|
||||
[eggDrop.key]: user.items.eggs[eggDrop.key] || 0,
|
||||
};
|
||||
user.items.eggs[eggDrop.key] += 1;
|
||||
if (user.markModified) user.markModified('items.eggs');
|
||||
|
||||
user.items.hatchingPotions = {
|
||||
...user.items.hatchingPotions,
|
||||
[potionDrop.key]: user.items.hatchingPotions[potionDrop.key] || 0,
|
||||
};
|
||||
user.items.hatchingPotions[potionDrop.key] += 1;
|
||||
if (user.markModified) user.markModified('items.hatchingPotions');
|
||||
|
||||
if (user.addNotification) user.addNotification('FIRST_DROPS', { egg: eggDrop.key, hatchingPotion: potionDrop.key });
|
||||
|
||||
return ({ egg: eggDrop.key, hatchingPotion: potionDrop.key });
|
||||
}
|
||||
|
|
@ -4,12 +4,15 @@ import min from 'lodash/min';
|
|||
import reduce from 'lodash/reduce';
|
||||
import filter from 'lodash/filter';
|
||||
import pickBy from 'lodash/pickBy';
|
||||
import size from 'lodash/size';
|
||||
import moment from 'moment';
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import { daysSince } from '../cron';
|
||||
import { diminishingReturns } from '../statHelpers';
|
||||
import randomVal from '../libs/randomVal';
|
||||
import statsComputed from '../libs/statsComputed';
|
||||
import firstDrops from './firstDrops';
|
||||
|
||||
// TODO This is only used on the server
|
||||
// move to user model as an instance method?
|
||||
|
|
@ -30,6 +33,14 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
|||
let dropMultiplier;
|
||||
let rarity;
|
||||
|
||||
if (
|
||||
size(user.items.eggs) < 1
|
||||
&& size(user.items.hatchingPotions) < 1
|
||||
) {
|
||||
user._tmp.firstDrops = firstDrops(user);
|
||||
return;
|
||||
}
|
||||
|
||||
const predictableRandom = options.predictableRandom || trueRandom;
|
||||
const { task } = options;
|
||||
|
||||
|
|
@ -71,7 +82,7 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (user.flags && user.flags.dropsEnabled && predictableRandom() < chance) {
|
||||
if (predictableRandom() < chance) {
|
||||
rarity = predictableRandom();
|
||||
|
||||
if (rarity > 0.6) { // food 40% chance
|
||||
|
|
@ -135,7 +146,7 @@ export default function randomDrop (user, options, req = {}, analytics) {
|
|||
}, req.language);
|
||||
}
|
||||
|
||||
if (analytics) {
|
||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
||||
analytics.track('dropped item', {
|
||||
uuid: user._id,
|
||||
itemKey: drop.key,
|
||||
|
|
|
|||
|
|
@ -66,24 +66,6 @@ export default function updateStats (user, stats, req = {}, analytics) {
|
|||
if (!user.flags.itemsEnabled && (user.stats.exp > 10 || user.stats.lvl > 1)) {
|
||||
user.flags.itemsEnabled = true;
|
||||
}
|
||||
if (!user.flags.dropsEnabled && user.stats.lvl >= 3) {
|
||||
user.flags.dropsEnabled = true;
|
||||
if (user.addNotification) user.addNotification('DROPS_ENABLED');
|
||||
|
||||
if (user.items.eggs.Wolf > 0) {
|
||||
user.items.eggs = {
|
||||
...user.items.eggs,
|
||||
Wolf: user.items.eggs.Wolf + 1,
|
||||
};
|
||||
} else {
|
||||
user.items.eggs = {
|
||||
...user.items.eggs,
|
||||
Wolf: 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (user.markModified) user.markModified('items.eggs');
|
||||
}
|
||||
each({
|
||||
vice1: 30,
|
||||
atom1: 15,
|
||||
|
|
|
|||
|
|
@ -23,9 +23,16 @@ export function onOnboardingComplete (user) {
|
|||
}
|
||||
|
||||
// Add notification and awards (server)
|
||||
export function checkOnboardingStatus (user) {
|
||||
export function checkOnboardingStatus (user, analytics) {
|
||||
if (hasActiveOnboarding(user) && hasCompletedOnboarding(user) && user.addNotification) {
|
||||
user.addNotification('ONBOARDING_COMPLETE');
|
||||
if (analytics) {
|
||||
analytics.track('onboarding complete', {
|
||||
uuid: user._id,
|
||||
hitType: 'event',
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
onOnboardingComplete(user);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ export class AbstractBuyOperation {
|
|||
|
||||
this.extractAndValidateParams(this.user, this.req);
|
||||
|
||||
const resultObj = this.executeChanges(this.user, this.item, this.req);
|
||||
const resultObj = this.executeChanges(this.user, this.item, this.req, this.analytics);
|
||||
|
||||
if (this.analytics) {
|
||||
this.sendToAnalytics(this.analyticsData());
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin
|
|||
}
|
||||
}
|
||||
|
||||
executeChanges (user, item, req) {
|
||||
executeChanges (user, item, req, analytics) {
|
||||
let message;
|
||||
|
||||
if (user.preferences.autoEquip) {
|
||||
|
|
@ -70,7 +70,7 @@ export class BuyMarketGearOperation extends AbstractGoldItemOperation { // eslin
|
|||
|
||||
if (!user.achievements.purchasedEquipment && user.addAchievement) {
|
||||
user.addAchievement('purchasedEquipment');
|
||||
checkOnboardingStatus(user);
|
||||
checkOnboardingStatus(user, analytics);
|
||||
}
|
||||
|
||||
removePinnedGearAddPossibleNewOnes(user, `gear.flat.${item.key}`, item.key);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import findIndex from 'lodash/findIndex';
|
|||
import get from 'lodash/get';
|
||||
import keys from 'lodash/keys';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import moment from 'moment';
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import {
|
||||
|
|
@ -34,7 +35,7 @@ function evolve (user, pet, req) {
|
|||
}, req.language);
|
||||
}
|
||||
|
||||
export default function feed (user, req = {}) {
|
||||
export default function feed (user, req = {}, analytics) {
|
||||
let pet = get(req, 'params.pet');
|
||||
const foodK = get(req, 'params.food');
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ export default function feed (user, req = {}) {
|
|||
|
||||
if (!user.achievements.fedPet && user.addAchievement) {
|
||||
user.addAchievement('fedPet');
|
||||
checkOnboardingStatus(user);
|
||||
checkOnboardingStatus(user, analytics);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -118,6 +119,16 @@ export default function feed (user, req = {}) {
|
|||
}
|
||||
});
|
||||
|
||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
||||
analytics.track('pet feed', {
|
||||
uuid: user._id,
|
||||
foodKey: food.key,
|
||||
petKey: pet.key,
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
user.items.pets[pet.key],
|
||||
message,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import forEach from 'lodash/forEach';
|
|||
import get from 'lodash/get';
|
||||
import keys from 'lodash/keys';
|
||||
import upperFirst from 'lodash/upperFirst';
|
||||
import moment from 'moment';
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import {
|
||||
|
|
@ -13,7 +14,7 @@ import {
|
|||
import errorMessage from '../libs/errorMessage';
|
||||
import { checkOnboardingStatus } from '../libs/onboarding';
|
||||
|
||||
export default function hatch (user, req = {}) {
|
||||
export default function hatch (user, req = {}, analytics) {
|
||||
const egg = get(req, 'params.egg');
|
||||
const hatchingPotion = get(req, 'params.hatchingPotion');
|
||||
|
||||
|
|
@ -55,7 +56,7 @@ export default function hatch (user, req = {}) {
|
|||
|
||||
if (!user.achievements.hatchedPet && user.addAchievement) {
|
||||
user.addAchievement('hatchedPet');
|
||||
checkOnboardingStatus(user);
|
||||
checkOnboardingStatus(user, analytics);
|
||||
}
|
||||
|
||||
forEach(content.animalColorAchievements, achievement => {
|
||||
|
|
@ -78,6 +79,15 @@ export default function hatch (user, req = {}) {
|
|||
}
|
||||
});
|
||||
|
||||
if (analytics && moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
||||
analytics.track('pet hatch', {
|
||||
uuid: user._id,
|
||||
petKey: pet,
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
user.items,
|
||||
i18n.t('messageHatched', req.language),
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ function _updateCounter (task, direction, times) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function scoreTask (options = {}, req = {}) {
|
||||
export default function scoreTask (options = {}, req = {}, analytics) {
|
||||
const {
|
||||
user, task, direction, times = 1, cron = false,
|
||||
} = options;
|
||||
|
|
@ -347,7 +347,7 @@ export default function scoreTask (options = {}, req = {}) {
|
|||
|
||||
if (!user.achievements.completedTask && cron === false && direction === 'up' && user.addAchievement) {
|
||||
user.addAchievement('completedTask');
|
||||
checkOnboardingStatus(user);
|
||||
checkOnboardingStatus(user, analytics);
|
||||
}
|
||||
|
||||
return [delta];
|
||||
|
|
|
|||
|
|
@ -797,10 +797,11 @@ api.scoreTask = {
|
|||
|
||||
const wasCompleted = task.completed;
|
||||
|
||||
const [delta] = common.ops.scoreTask({ task, user, direction }, req);
|
||||
const firstTask = !user.achievements.completedTask;
|
||||
const [delta] = common.ops.scoreTask({ task, user, direction }, req, res.analytics);
|
||||
// Drop system (don't run on the client,
|
||||
// as it would only be discarded since ops are sent to the API, not the results)
|
||||
if (direction === 'up') common.fns.randomDrop(user, { task, delta }, req, res.analytics);
|
||||
if (direction === 'up' && !firstTask) common.fns.randomDrop(user, { task, delta }, req, res.analytics);
|
||||
|
||||
// If a todo was completed or uncompleted move it in or out of the user.tasksOrder.todos list
|
||||
// TODO move to common code?
|
||||
|
|
@ -884,7 +885,7 @@ api.scoreTask = {
|
|||
}
|
||||
|
||||
// Track when new users (first 7 days) score tasks
|
||||
if (moment().diff(user.auth.timestamps.created, 'days') < 7) {
|
||||
if (moment().diff(user.auth.timestamps.created, 'days') < 7 && user.flags.welcomed) {
|
||||
res.analytics.track('task score', {
|
||||
uuid: user._id,
|
||||
hitType: 'event',
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export async function createTasks (req, res, options = {}) {
|
|||
// are the onboarding ones
|
||||
if (!user.achievements.createdTask && user.flags.welcomed) {
|
||||
user.addAchievement('createdTask');
|
||||
shared.onboarding.checkOnboardingStatus(user);
|
||||
shared.onboarding.checkOnboardingStatus(user, res.analytics);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ export default new Schema({
|
|||
reorderTask: { $type: Boolean, default: false },
|
||||
},
|
||||
},
|
||||
dropsEnabled: { $type: Boolean, default: false },
|
||||
dropsEnabled: { $type: Boolean, default: false }, // unused
|
||||
itemsEnabled: { $type: Boolean, default: false },
|
||||
newStuff: { $type: Boolean, default: false },
|
||||
rewrite: { $type: Boolean, default: true },
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import validator from 'validator';
|
|||
import baseModel from '../libs/baseModel';
|
||||
|
||||
const NOTIFICATION_TYPES = [
|
||||
'DROPS_ENABLED',
|
||||
'DROPS_ENABLED', // unused
|
||||
'REBIRTH_ENABLED',
|
||||
'WON_CHALLENGE',
|
||||
'STREAK_ACHIEVEMENT',
|
||||
|
|
@ -34,6 +34,7 @@ const NOTIFICATION_TYPES = [
|
|||
'NEW_STUFF',
|
||||
'NEW_CHAT_MESSAGE',
|
||||
'LEVELED_UP',
|
||||
'FIRST_DROPS',
|
||||
'ONBOARDING_COMPLETE',
|
||||
'ACHIEVEMENT_ALL_YOUR_BASE',
|
||||
'ACHIEVEMENT_BACK_TO_BASICS',
|
||||
|
|
|
|||
Loading…
Reference in a new issue