mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-15 05:37:22 +01:00
commitbde71cc45dAuthor: Kalista Payne <sabrecat@gmail.com> Date: Thu Mar 20 15:25:11 2025 -0500 fix(potions): add missing Cryptid release date commit1a50413402Author: Kalista Payne <sabrecat@gmail.com> Date: Tue Mar 18 16:52:19 2025 -0500 fix(wacky): revise text, address linting commit2d49a16f55Author: Phillip Thelen <phillip@habitica.com> Date: Tue Mar 18 16:05:17 2025 +0100 Fix displaying countdown for recurring event items commitcf20b30975Author: Kalista Payne <sabrecat@gmail.com> Date: Fri Mar 14 18:01:37 2025 -0500 fix(lint): line length commite39e490e41Author: Kalista Payne <sabrecat@gmail.com> Date: Fri Mar 14 17:55:12 2025 -0500 fix(foolin): correct animation and end date of potions commit164e2eefddAuthor: Kalista Payne <sabrecat@gmail.com> Date: Thu Mar 13 17:25:14 2025 -0500 fix(event): Panda Cub typo, shift start date commit394c922287Author: Kalista Payne <sabrecat@gmail.com> Date: Thu Mar 13 17:02:12 2025 -0500 fix(test): account for addition of Cryptid commitbea9e40338Author: Kalista Payne <sabrecat@gmail.com> Date: Thu Mar 13 16:52:37 2025 -0500 feat(event): April Fools 2025
497 lines
14 KiB
JavaScript
497 lines
14 KiB
JavaScript
import _mapValues from 'lodash/mapValues';
|
|
import i18n from '../i18n';
|
|
import content from '../content/index';
|
|
import { BadRequest } from './errors';
|
|
import * as count from '../count';
|
|
|
|
import isPinned from './isPinned';
|
|
import isFreeRebirth from './isFreeRebirth';
|
|
import getOfficialPinnedItems from './getOfficialPinnedItems';
|
|
|
|
import { ALWAYS_AVAILABLE_CUSTOMIZATIONS } from '../content/constants/schedule';
|
|
|
|
function lockQuest (quest, user) {
|
|
// checks series quests, including Masterclasser
|
|
if (quest.prereqQuests) {
|
|
if (!user.achievements.quests) return true;
|
|
for (const prereq of quest.prereqQuests) {
|
|
if (!user.achievements.quests[prereq]) return true;
|
|
}
|
|
}
|
|
// checks quest & user level against quest level
|
|
if (quest.lvl && user.stats.lvl < quest.lvl) return true;
|
|
|
|
// checks unlockCondition.incentiveThreshold for Lunar Battle
|
|
if (
|
|
quest.unlockCondition
|
|
&& quest.unlockCondition.incentiveThreshold
|
|
&& user.loginIncentives < quest.unlockCondition.incentiveThreshold
|
|
) return true;
|
|
// // then if we've passed all the checks
|
|
return false;
|
|
}
|
|
|
|
function isItemSuggested (officialPinnedItems, itemInfo) {
|
|
return officialPinnedItems.findIndex(officialItem => { // eslint-disable-line arrow-body-style
|
|
return officialItem.type === itemInfo.pinType && officialItem.path === itemInfo.path;
|
|
}) > -1;
|
|
}
|
|
|
|
function getDefaultGearProps (item, language) {
|
|
return {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
type: item.type,
|
|
specialClass: item.specialClass,
|
|
locked: false,
|
|
purchaseType: 'gear',
|
|
class: `shop_${item.key}`,
|
|
path: `gear.flat.${item.key}`,
|
|
str: item.str,
|
|
int: item.int,
|
|
per: item.per,
|
|
con: item.con,
|
|
klass: item.klass,
|
|
set: item.set,
|
|
season: item.season,
|
|
};
|
|
}
|
|
|
|
export default function getItemInfo (user, type, item, officialPinnedItems, language = 'en', matcher = null) {
|
|
if (officialPinnedItems === undefined) {
|
|
officialPinnedItems = getOfficialPinnedItems(user); // eslint-disable-line no-param-reassign
|
|
}
|
|
|
|
let itemInfo;
|
|
|
|
switch (type) { // eslint-disable-line default-case
|
|
case 'eggs':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: i18n.t('egg', { eggType: item.text(language) }, language),
|
|
notes: item.notes(language),
|
|
value: item.value,
|
|
class: `Pet_Egg_${item.key}`,
|
|
locked: false,
|
|
currency: 'gems',
|
|
purchaseType: 'eggs',
|
|
path: `eggs.${item.key}`,
|
|
pinType: 'eggs',
|
|
};
|
|
break;
|
|
case 'hatchingPotions':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: i18n.t('potion', { potionType: item.text(language) }),
|
|
notes: item.notes(language),
|
|
class: `Pet_HatchingPotion_${item.key}`,
|
|
value: item.value,
|
|
locked: false,
|
|
currency: 'gems',
|
|
purchaseType: 'hatchingPotions',
|
|
path: `hatchingPotions.${item.key}`,
|
|
pinType: 'hatchingPotions',
|
|
};
|
|
break;
|
|
case 'premiumHatchingPotion':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: i18n.t('potion', { potionType: item.text(language) }),
|
|
notes: `${item.notes(language)} ${item._addlNotes(language)}`,
|
|
class: `Pet_HatchingPotion_${item.key}`,
|
|
value: item.value,
|
|
locked: false,
|
|
currency: 'gems',
|
|
purchaseType: 'hatchingPotions',
|
|
path: item.wacky ? `wackyHatchingPotions.${item.key}` : `premiumHatchingPotions.${item.key}`,
|
|
pinType: 'premiumHatchingPotion',
|
|
};
|
|
break;
|
|
case 'food':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
class: `Pet_Food_${item.key}`,
|
|
value: item.value,
|
|
locked: false,
|
|
currency: 'gems',
|
|
purchaseType: 'food',
|
|
path: `food.${item.key}`,
|
|
pinType: 'food',
|
|
};
|
|
break;
|
|
case 'bundles':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
addlNotes: item.addlNotes ? item.addlNotes(language) : null,
|
|
value: item.value,
|
|
currency: 'gems',
|
|
class: `quest_bundle_${item.key}`,
|
|
purchaseType: 'bundles',
|
|
path: `bundles.${item.key}`,
|
|
pinType: 'bundles',
|
|
};
|
|
break;
|
|
case 'quests': // eslint-disable-line no-case-declarations
|
|
const locked = lockQuest(item, user); // eslint-disable-line no-case-declarations
|
|
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
addlNotes: item.addlNotes ? item.addlNotes(language) : null,
|
|
group: item.group,
|
|
value: item.goldValue ? item.goldValue : item.value,
|
|
locked,
|
|
previous: content.quests[item.previous]
|
|
? content.quests[item.previous].text(language)
|
|
: null,
|
|
unlockCondition: item.unlockCondition,
|
|
completed: user.achievements.quests[item.key] !== undefined,
|
|
drop: item.drop,
|
|
boss: item.boss,
|
|
collect: item.collect ? _mapValues(item.collect, o => ({
|
|
count: o.count,
|
|
text: o.text(),
|
|
})) : undefined,
|
|
lvl: item.lvl,
|
|
class: locked ? `inventory_quest_scroll_${item.key}_locked` : `inventory_quest_scroll_${item.key}`,
|
|
purchaseType: 'quests',
|
|
path: `quests.${item.key}`,
|
|
pinType: 'quests',
|
|
};
|
|
if (item.goldValue) {
|
|
itemInfo.currency = 'gold';
|
|
} else if (item.category === 'timeTravelers') {
|
|
itemInfo.currency = 'hourglasses';
|
|
} else {
|
|
itemInfo.currency = 'gems';
|
|
}
|
|
|
|
break;
|
|
case 'timeTravelers':
|
|
// TODO
|
|
itemInfo = {};
|
|
break;
|
|
case 'seasonalSpell':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
value: item.value,
|
|
type: 'special',
|
|
currency: 'gold',
|
|
locked: false,
|
|
purchaseType: 'special',
|
|
class: `inventory_special_${item.key}`,
|
|
path: `spells.special.${item.key}`,
|
|
pinType: 'seasonalSpell',
|
|
};
|
|
break;
|
|
case 'debuffPotion':
|
|
itemInfo = {
|
|
key: item.key,
|
|
mana: item.mana,
|
|
cast: item.cast,
|
|
immediateUse: item.immediateUse,
|
|
target: item.target,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
value: item.value,
|
|
type: 'debuffPotion',
|
|
currency: 'gold',
|
|
locked: false,
|
|
purchaseType: 'debuffPotion',
|
|
class: `shop_${item.key}`,
|
|
path: `spells.special.${item.key}`,
|
|
pinType: 'debuffPotion',
|
|
};
|
|
break;
|
|
case 'seasonalQuest':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
value: item.value,
|
|
type: 'quests',
|
|
currency: 'gems',
|
|
locked: false,
|
|
drop: item.drop,
|
|
boss: item.boss,
|
|
collect: item.collect,
|
|
class: `inventory_quest_scroll_${item.key}`,
|
|
purchaseType: 'quests',
|
|
path: `quests.${item.key}`,
|
|
pinType: 'seasonalQuest',
|
|
};
|
|
break;
|
|
case 'gear':
|
|
// spread operator not available
|
|
itemInfo = Object.assign(getDefaultGearProps(item, language), {
|
|
value: item.twoHanded || item.gearSet === 'animal' ? 2 : 1,
|
|
currency: 'gems',
|
|
pinType: 'gear',
|
|
});
|
|
break;
|
|
case 'marketGear':
|
|
itemInfo = Object.assign(getDefaultGearProps(item, language), {
|
|
value: item.value,
|
|
currency: 'gold',
|
|
pinType: 'marketGear',
|
|
canOwn: item.canOwn,
|
|
});
|
|
break;
|
|
case 'background':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
class: `icon_background_${item.key}`,
|
|
value: item.price,
|
|
currency: item.currency || 'gems',
|
|
purchaseType: 'backgrounds',
|
|
path: `backgrounds.${item.set}.${item.key}`,
|
|
pinType: 'background',
|
|
locked: false,
|
|
};
|
|
break;
|
|
case 'mystery_set':
|
|
itemInfo = {
|
|
items: item.items,
|
|
key: item.key,
|
|
text: item.text(language),
|
|
value: 1,
|
|
currency: 'hourglasses',
|
|
purchaseType: 'mystery_set',
|
|
class: `shop_set_mystery_${item.key}`,
|
|
path: `mystery.${item.key}`,
|
|
pinType: 'mystery_set',
|
|
};
|
|
break;
|
|
case 'potion':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(language),
|
|
value: item.value,
|
|
currency: 'gold',
|
|
purchaseType: 'potion',
|
|
class: `shop_${item.key}`,
|
|
path: 'potion',
|
|
pinType: 'potion',
|
|
};
|
|
break;
|
|
case 'armoire':
|
|
itemInfo = {
|
|
key: item.key,
|
|
text: item.text(language),
|
|
notes: item.notes(count.remainingGearInSet(user.items.gear.owned, 'armoire')),
|
|
value: item.value,
|
|
currency: 'gold',
|
|
purchaseType: 'armoire',
|
|
class: `shop_${item.key}`,
|
|
path: 'armoire',
|
|
pinType: 'armoire',
|
|
};
|
|
break;
|
|
case 'card': {
|
|
const spellInfo = content.spells.special[item.key];
|
|
|
|
itemInfo = {
|
|
key: item.key,
|
|
purchaseType: 'card',
|
|
class: `inventory_special_${item.key}`,
|
|
text: spellInfo.text(),
|
|
notes: spellInfo.notes(),
|
|
value: spellInfo.value,
|
|
currency: 'gold',
|
|
path: `cardTypes.${item.key}`,
|
|
pinType: 'card',
|
|
target: spellInfo.target,
|
|
cast: spellInfo.cast,
|
|
};
|
|
break;
|
|
}
|
|
case 'gem': {
|
|
itemInfo = {
|
|
key: 'gem',
|
|
purchaseType: 'gems',
|
|
class: 'shop_gem',
|
|
text: i18n.t('subGemName'),
|
|
notes: i18n.t('subGemPop'),
|
|
value: 20,
|
|
currency: 'gold',
|
|
path: 'special.gems',
|
|
pinType: 'gem',
|
|
locked: !user.purchased.plan.customerId,
|
|
};
|
|
break;
|
|
}
|
|
case 'rebirth_orb': {
|
|
itemInfo = {
|
|
key: 'rebirth_orb',
|
|
purchaseType: 'rebirth_orb',
|
|
class: 'rebirth_orb',
|
|
text: i18n.t('rebirthName'),
|
|
notes: i18n.t('rebirthPop'),
|
|
value: isFreeRebirth(user) ? 0 : 6,
|
|
currency: 'gems',
|
|
path: 'special.rebirth_orb',
|
|
pinType: 'rebirth_orb',
|
|
locked: !user.flags.rebirthEnabled,
|
|
};
|
|
break;
|
|
}
|
|
case 'fortify': {
|
|
itemInfo = {
|
|
key: 'fortify',
|
|
purchaseType: 'fortify',
|
|
class: 'inventory_special_fortify',
|
|
text: i18n.t('fortifyName'),
|
|
notes: i18n.t('fortifyPop'),
|
|
value: 4,
|
|
currency: 'gems',
|
|
path: 'special.fortify',
|
|
pinType: 'fortify',
|
|
};
|
|
break;
|
|
}
|
|
case 'timeTravelersStable': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
purchaseType: item.type,
|
|
class: `shop_${item.type}_${item.key}`,
|
|
text: content.timeTravelStable[item.type][item.key](language),
|
|
notes: '',
|
|
value: 1,
|
|
|
|
locked: false,
|
|
currency: 'hourglasses',
|
|
path: `timeTravelStable.${item.type}.${item.key}`,
|
|
pinType: 'timeTravelersStable',
|
|
};
|
|
break;
|
|
}
|
|
case 'haircolor': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_hair_bangs_${user.preferences.hair.bangs || 1}_${item.key}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
notes: '',
|
|
path: `hair.color.${item.key}`,
|
|
purchaseType: 'customization',
|
|
pinType: 'timeTravelersStable',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'color',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
case 'hairbase': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_hair_base_${item.key}_${user.preferences.hair.color}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
notes: '',
|
|
path: `hair.base.${item.key}`,
|
|
pinType: 'timeTravelersStable',
|
|
purchaseType: 'customization',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'base',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
case 'hairmustache': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_hair_mustache_${item.key}_${user.preferences.hair.color}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
notes: '',
|
|
path: `hair.mustache.${item.key}`,
|
|
pinType: 'timeTravelersStable',
|
|
purchaseType: 'customization',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'mustache',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
case 'hairbeard': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_hair_beard_${item.key}_${user.preferences.hair.color}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
notes: '',
|
|
path: `hair.beard.${item.key}`,
|
|
pinType: 'timeTravelersStable',
|
|
purchaseType: 'customization',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'beard',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
case 'shirt': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_${user.preferences.size}_shirt_${item.key}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
notes: '',
|
|
path: `shirt.${item.key}`,
|
|
pinType: 'timeTravelersStable',
|
|
purchaseType: 'customization',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'shirt',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
case 'skin': {
|
|
itemInfo = {
|
|
key: item.key,
|
|
class: `icon_skin_${item.key}`,
|
|
currency: 'gems',
|
|
locked: false,
|
|
path: `skin.${item.key}`,
|
|
notes: '',
|
|
pinType: 'timeTravelersStable',
|
|
purchaseType: 'customization',
|
|
set: item.set,
|
|
text: item.text(language),
|
|
type: 'skin',
|
|
value: item.price,
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (itemInfo) {
|
|
itemInfo.isSuggested = isItemSuggested(officialPinnedItems, itemInfo);
|
|
itemInfo.pinned = isPinned(user, itemInfo, officialPinnedItems);
|
|
} else {
|
|
throw new BadRequest(i18n.t('wrongItemType', { type }, language));
|
|
}
|
|
|
|
if (matcher && (!itemInfo.set
|
|
|| ALWAYS_AVAILABLE_CUSTOMIZATIONS.indexOf(itemInfo.set.key) === -1)) {
|
|
itemInfo.end = matcher.getEnd(itemInfo.key);
|
|
}
|
|
|
|
return itemInfo;
|
|
}
|