Merge branch 'develop' into refactor-content

This commit is contained in:
Blade Barringer
2015-10-01 21:08:00 -05:00
86 changed files with 5065 additions and 4461 deletions

File diff suppressed because one or more lines are too long

View File

@@ -42,7 +42,7 @@
}
.promo_dilatoryDistress {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -837px -843px;
background-position: -291px -843px;
width: 90px;
height: 90px;
}
@@ -66,7 +66,7 @@
}
.promo_enchanted_armoire_201509 {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -291px -843px;
background-position: -837px -843px;
width: 90px;
height: 90px;
}
@@ -216,7 +216,7 @@
}
.promo_pastel_skin {
background-image: url(spritesmith-largeSprites-0.png);
background-position: 0px -668px;
background-position: -331px -668px;
width: 330px;
height: 83px;
}
@@ -240,7 +240,7 @@
}
.promo_shimmer_hair {
background-image: url(spritesmith-largeSprites-0.png);
background-position: -331px -668px;
background-position: 0px -668px;
width: 330px;
height: 83px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 146 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 240 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 142 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 131 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

After

Width:  |  Height:  |  Size: 328 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 144 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 145 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -9,125 +9,133 @@
"dropEggWolfText": "Wolf",
"dropEggWolfMountText": "Wolf",
"dropEggWolfAdjective": "loyal",
"dropEggWolfAdjective": "a loyal",
"dropEggTigerCubText": "Tiger Cub",
"dropEggTigerCubMountText": "Tiger",
"dropEggTigerCubAdjective": "fierce",
"dropEggTigerCubAdjective": "a fierce",
"dropEggPandaCubText": "Panda Cub",
"dropEggPandaCubMountText": "Panda",
"dropEggPandaCubAdjective": "gentle",
"dropEggPandaCubAdjective": "a gentle",
"dropEggLionCubText": "Lion Cub",
"dropEggLionCubMountText": "Lion",
"dropEggLionCubAdjective": "regal",
"dropEggLionCubAdjective": "a regal",
"dropEggFoxText": "Fox",
"dropEggFoxMountText": "Fox",
"dropEggFoxAdjective": "wily",
"dropEggFoxAdjective": "a wily",
"dropEggFlyingPigText": "Flying Pig",
"dropEggFlyingPigMountText": "Flying Pig",
"dropEggFlyingPigAdjective": "whimsical",
"dropEggFlyingPigAdjective": "a whimsical",
"dropEggDragonText": "Dragon",
"dropEggDragonMountText": "Dragon",
"dropEggDragonAdjective": "mighty",
"dropEggDragonAdjective": "a mighty",
"dropEggCactusText": "Cactus",
"dropEggCactusMountText": "Cactus",
"dropEggCactusAdjective": "prickly",
"dropEggCactusAdjective": "a prickly",
"dropEggFlyingPigText": "Flying Pig",
"dropEggFlyingPigAdjective": "a whimsical",
"dropEggDragonText": "Dragon",
"dropEggDragonAdjective": "a mighty",
"dropEggCactusText": "Cactus",
"dropEggBearCubText": "Bear Cub",
"dropEggBearCubMountText": "Bear",
"dropEggBearCubAdjective": "cuddly",
"dropEggBearCubAdjective": "a cuddly",
"questEggGryphonText": "Gryphon",
"questEggGryphonMountText": "Gryphon",
"questEggGryphonAdjective": "proud",
"questEggGryphonAdjective": "a proud",
"questEggHedgehogText": "Hedgehog",
"questEggHedgehogMountText": "Hedgehog",
"questEggHedgehogAdjective": "spiky",
"questEggHedgehogAdjective": "a spiky",
"questEggDeerText": "Deer",
"questEggDeerMountText": "Deer",
"questEggDeerAdjective": "elegant",
"questEggDeerAdjective": "an elegant",
"questEggEggText": "Egg",
"questEggEggMountText": "Egg Basket",
"questEggEggAdjective": "colorful",
"questEggEggAdjective": "a colorful",
"questEggRatText": "Rat",
"questEggRatMountText": "Rat",
"questEggRatAdjective": "dirty",
"questEggRatAdjective": "a dirty",
"questEggOctopusText": "Octopus",
"questEggOctopusMountText": "Octopus",
"questEggOctopusAdjective": "slippery",
"questEggOctopusAdjective": "a slippery",
"questEggSeahorseText": "Seahorse",
"questEggSeahorseMountText": "Seahorse",
"questEggSeahorseAdjective": "prize",
"questEggSeahorseAdjective": "a prize",
"questEggParrotText": "Parrot",
"questEggParrotMountText": "Parrot",
"questEggParrotAdjective": "vibrant",
"questEggParrotAdjective": "a vibrant",
"questEggRoosterText": "Rooster",
"questEggRoosterMountText": "Rooster",
"questEggRoosterAdjective": "strutting",
"questEggRoosterAdjective": "a strutting",
"questEggSpiderText": "Spider",
"questEggSpiderMountText": "Spider",
"questEggSpiderAdjective": "creepy",
"questEggSpiderAdjective": "a creepy",
"questEggOwlText": "Owl",
"questEggOwlMountText": "Owl",
"questEggOwlAdjective": "wise",
"questEggOwlAdjective": "a wise",
"questEggPenguinText": "Penguin",
"questEggPenguinMountText": "Penguin",
"questEggPenguinAdjective": "perspicacious",
"questEggPenguinAdjective": "a perspicacious",
"questEggTRexText": "Tyrannosaur",
"questEggTRexMountText": "Tyrannosaur",
"questEggTRexAdjective": "tiny-armed",
"questEggTRexAdjective": "a tiny-armed",
"questEggRockText": "Rock",
"questEggRockMountText": "Rock",
"questEggRockAdjective": "lively",
"questEggRockAdjective": "a lively",
"questEggBunnyText": "Bunny",
"questEggBunnyMountText": "Bunny",
"questEggBunnyAdjective": "snuggly",
"questEggBunnyAdjective": "a snuggly",
"questEggSlimeText": "Marshmallow Slime",
"questEggSlimeMountText": "Marshmallow Slime",
"questEggSlimeAdjective": "sweet",
"questEggSlimeAdjective": "a sweet",
"questEggSheepText": "Sheep",
"questEggSheepMountText": "Sheep",
"questEggSheepAdjective": "woolly",
"questEggSheepAdjective": "a woolly",
"questEggCuttlefishText": "Cuttlefish",
"questEggCuttlefishMountText": "Cuttlefish",
"questEggCuttlefishAdjective": "cuddly",
"questEggCuttlefishAdjective": "a cuddly",
"questEggWhaleText": "Whale",
"questEggWhaleMountText": "Whale",
"questEggWhaleAdjective": "splashy",
"questEggWhaleAdjective": "a splashy",
"questEggCheetahText": "Cheetah",
"questEggCheetahMountText": "Cheetah",
"questEggCheetahAdjective": "honest",
"questEggCheetahAdjective": "an honest",
"questEggHorseText": "Horse",
"questEggHorseMountText": "Horse",
"questEggHorseAdjective": "galloping",
"questEggHorseAdjective": "a galloping",
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into a <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"eggNotes": "Find a hatching potion to pour on this egg, and it will hatch into <%= eggAdjective(locale) %> <%= eggText(locale) %>.",
"hatchingPotionBase": "Base",
"hatchingPotionWhite": "White",
@@ -139,8 +147,10 @@
"hatchingPotionCottonCandyPink": "Cotton Candy Pink",
"hatchingPotionCottonCandyBlue": "Cotton Candy Blue",
"hatchingPotionGolden": "Golden",
"hatchingPotionSpooky": "Spooky",
"hatchingPotionNotes": "Pour this on an egg, and it will hatch as a <%= potText(locale) %> pet.",
"premiumPotionAddlNotes": "Not usable on quest pet eggs.",
"foodMeat": "Meat",
"foodMilk": "Milk",

View File

@@ -170,6 +170,8 @@
"weaponArmoireIronCrookNotes": "Fiercely hammered from iron, this iron crook is good at herding sheep. Increases Perception and Strength by <%= attrs %> each. Enchanted Armoire: Horned Iron Set (Item 3 of 3)",
"weaponArmoireGoldWingStaffText": "Gold Wing Staff",
"weaponArmoireGoldWingStaffNotes": "The wings on this staff constantly flutter and twist. Increases all attributes by <%= attrs %> each. Enchanted Armoire: Independent Item.",
"weaponArmoireBatWandText": "Bat Wand",
"weaponArmoireBatWandNotes": "This wand can turn any task into a bat! Wave it about and watch them fly away. Increases Intelligence by <%= int %> and Perception by <%= per %>. Enchanted Armoire: Independent Item.",
"armor": "armor",
@@ -542,6 +544,10 @@
"headArmoireRedFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a radiant red color. Increases Constitution, Intelligence, and Perception by <%= attrs %> each. Enchanted Armoire: Independent Item.",
"headArmoirePlagueDoctorHatText": "Plague Doctor Hat",
"headArmoirePlagueDoctorHatNotes": "An authentic hat worn by the doctors who battle the Plague of Procrastination! Increases Strength by <%= str %>, Intelligence by <%= int %>, and Constitution by <%= con %>. Enchanted Armoire: Plague Doctor Set (Item 1 of 3).",
"headArmoireBlackCatText": "Black Cat Hat",
"headArmoireBlackCatNotes": "This black hat is... purring. And twitching its tail. And breathing? Yeah, you just have a sleeping cat on your head. Increases Intelligence and Perception by <%= attrs %> each. Enchanted Armoire: Independent Item.",
"headArmoireOrangeCatText": "Orange Cat Hat",
"headArmoireOrangeCatNotes": "This orange hat is... purring. And twitching its tail. And breathing? Yeah, you just have a sleeping cat on your head. Increases Strength and Constitution by <%= attrs %> each. Enchanted Armoire: Independent Item.",
"offhand": "shield-hand item",
@@ -639,6 +645,8 @@
"shieldArmoireGladiatorShieldText": "Gladiator Shield",
"shieldArmoireGladiatorShieldNotes": "To be a gladiator you must.... eh, whatever, just bash them with your shield. Increases Constitution by <%= con %> and Strength by <%= str %>. Enchanted Armoire: Gladiator Set (Item 3 of 3).",
"shieldArmoireMidnightShieldText": "Midnight Shield",
"shieldArmoireMidnightShieldNotes": "This shield is most powerful at the stroke of midnight! Increases Constitution by <%= con %> and Strength by <%= str %>. Enchanted Armoire: Independent Item.",
"back": "Back Accessory",
"backBase0Text": "No Back Accessory",

View File

@@ -59,5 +59,6 @@
"monsterOfScienceSet": "Monster of Science (Warrior)",
"witchyWizardSet": "Witchy Wizard (Mage)",
"mummyMedicSet": "Mummy Medic (Healer)",
"vampireSmiterSet": "Vampire Smiter (Rogue)"
"vampireSmiterSet": "Vampire Smiter (Rogue)",
"fallEventAvailability": "Available until October 31"
}

View File

@@ -5,6 +5,7 @@
"messageTagNotFound": "Tag not found.",
"messagePetNotFound": ":pet not found in user.items.pets",
"messageFoodNotFound": ":food not found in user.items.food",
"messageNotAvailable": "This item is not currently available for purchase.",
"messageCannotFeedPet": "Can't feed this pet.",
"messageAlreadyMount": "You already have that mount. Try feeding another pet.",
"messageEvolve": "You have tamed <%= egg %>, let's go for a ride!",
@@ -14,6 +15,7 @@
"messageEquipped": " <%= itemText %> equipped.",
"messageUnEquipped": "<%= itemText %> un-equipped.",
"messageMissingEggPotion": "You're missing either that egg or that potion",
"messageInvalidEggPotionCombo": "You can't hatch Quest Pet Eggs with Magic Hatching Potions! Try a different egg.",
"messageAlreadyPet": "You already have that pet. Try hatching a different combination!",
"messageHatched": "Your egg hatched! Visit your stable to equip your pet.",
"messageNotEnoughGold": "Not Enough Gold",

View File

@@ -1,11 +1,13 @@
{
"pets": "Pets",
"petsFound": "Pets Found",
"magicPets": "Magic Potion Pets",
"rarePets": "Rare Pets",
"questPets": "Quest Pets",
"mounts": "Mounts",
"mountsTamed": "Mounts Tamed",
"questMounts": "Quest Mounts",
"magicMounts": "Magic Potion Mounts",
"rareMounts": "Rare Mounts",
"etherealLion": "Ethereal Lion",
"veteranWolf": "Veteran Wolf",
@@ -24,13 +26,15 @@
"eggSingular": "egg",
"noEggs": "You don't have any eggs.",
"hatchingPotions": "Hatching Potions",
"magicHatchingPotions": "<strong>Magic Hatching Potions</strong>",
"hatchingPotion": "hatching potion",
"noHatchingPotions": "You don't have any hatching potions.",
"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.",
"foodText": "food",
"food": "Food and 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.wikia.com/wiki/Drops\">Learn more about the drop system.</a>",
"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.wikia.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.",
"beastMasterProgress": "Beast Master Progress",
"stableBeastMasterProgress": "Beast Master Progress: <%= number %> Pets Found",
"beastAchievement": "You have earned the \"Beast Master\" Achievement for collecting all the pets!",

View File

@@ -767,7 +767,7 @@ api.wrap = (user, main=true) ->
if food.key is 'Saddle'
evolve()
else
if food.target is potion
if food.target is potion or content.hatchingPotions[potion].premium
userPets[pet] += 5
message = i18n.t('messageLikesFood', {egg: petDisplayName, foodText: food.text(req.language)}, req.language)
else
@@ -823,7 +823,8 @@ api.wrap = (user, main=true) ->
item = content[type][key]
price = item.value / 4
return cb?({code:404,message:":key not found for Content.#{type}"},req) unless item
return cb?({code:401, message: i18n.t('notEnoughGems', req.language)}) if (user.balance < price) or !user.balance
return cb?({code:403, message: i18n.t('messageNotAvailable', req.language)}) if not item.canBuy(user)
return cb?({code:403, message: i18n.t('notEnoughGems', req.language)}) if (user.balance < price) or !user.balance
user.balance -= price
if type is 'gear' then user.items.gear.owned[key] = true
else
@@ -1071,10 +1072,11 @@ api.wrap = (user, main=true) ->
hatch: (req, cb) ->
{egg, hatchingPotion} = req.params
return cb?({code:404,message:"Please specify query.egg & query.hatchingPotion"}) unless egg and hatchingPotion
return cb?({code:401,message:i18n.t('messageMissingEggPotion', req.language)}) unless user.items.eggs[egg] > 0 and user.items.hatchingPotions[hatchingPotion] > 0
return cb?({code:400,message:"Please specify query.egg & query.hatchingPotion"}) unless egg and hatchingPotion
return cb?({code:403,message:i18n.t('messageMissingEggPotion', req.language)}) unless user.items.eggs[egg] > 0 and user.items.hatchingPotions[hatchingPotion] > 0
return cb?({code:403,message:i18n.t('messageInvalidEggPotionCombo', req.language)}) if content.hatchingPotions[hatchingPotion].premium and not content.dropEggs[egg]
pet = "#{egg}-#{hatchingPotion}"
return cb?({code:401, message:i18n.t('messageAlreadyPet', req.language)}) if user.items.pets[pet] and user.items.pets[pet] > 0
return cb?({code:403, message:i18n.t('messageAlreadyPet', req.language)}) if user.items.pets[pet] and user.items.pets[pet] > 0
user.items.pets[pet] = 5
user.items.eggs[egg]--
user.items.hatchingPotions[hatchingPotion]--
@@ -1530,7 +1532,7 @@ api.wrap = (user, main=true) ->
# Eggs: 30% chance
else if rarity > .3
drop = user.fns.randomVal _.where(content.eggs,{canBuy:true})
drop = user.fns.randomVal content.dropEggs
user.items.eggs[drop.key] ?= 0
user.items.eggs[drop.key]++
drop.type = 'Egg'

View File

@@ -12,6 +12,15 @@ const DROP_EGGS = [
'BearCub',
];
let eggs = generateEggs(DROP_EGGS, {type: 'drop', canBuy: true});
let eggDefaults = {
type: 'drop',
canBuy: () => {
return () => {
return true;
};
},
};
let eggs = generateEggs(DROP_EGGS, eggDefaults);
export default eggs;

View File

@@ -24,6 +24,41 @@ const QUEST_EGGS = [
'Horse',
];
let eggs = generateEggs(QUEST_EGGS, {type: 'quest', canBuy: false});
let eggDefaults = {
type: 'quest',
canBuy: (key) => {
return _generateQuestAchievementRequirement(key);
},
};
let eggs = generateEggs(QUEST_EGGS, eggDefaults);
// Exceptions to normal defaults
eggs.TRex.canBuy = (user) => {
let achievements = user.achievements.quests;
if (achievements) {
return achievements.trex > 0 ||
achievements.trex_undead > 0;
}
}
eggs.Deer.canBuy = _generateQuestAchievementRequirement('ghost_stag');
eggs.Seahorse.canBuy = _generateQuestAchievementRequirement('dilatory_derby');
eggs.Parrot.canBuy = _generateQuestAchievementRequirement('harpy');
eggs.Cuttlefish.canBuy = _generateQuestAchievementRequirement('kraken');
eggs.Egg.canBuy = () => { return false; }
function _generateQuestAchievementRequirement(name) {
return (user) => {
let achievements = user.achievements.quests;
let questKey = name.toLowerCase();
if (achievements) {
return achievements[questKey] > 0;
}
};
}
export default eggs;

View File

@@ -194,7 +194,29 @@ let head = {
canOwn: ((u) => {
return u.items.gear.owned.head_armoire_plagueDoctorHat != null;
})
}
},
blackCat: {
notes: t('headArmoireBlackCatNotes', {
attrs: 9
}),
value: 100,
int: 9,
per: 9,
canOwn: (function(u) {
return u.items.gear.owned.head_armoire_blackCat != null;
})
},
orangeCat: {
notes: t('headArmoireOrangeCatNotes', {
attrs: 9
}),
value: 100,
con: 9,
str: 9,
canOwn: (function(u) {
return u.items.gear.owned.head_armoire_orangeCat != null;
})
},
};
let shield = {
@@ -279,6 +301,14 @@ let weapon = {
canOwn: (function(u) {
return u.items.gear.owned.weapon_armoire_goldWingStaff != null;
})
},
batWand: {
value: 100,
int: 10,
per: 2,
canOwn: (function(u) {
return u.items.gear.owned.weapon_armoire_batWand != null;
})
}
};

View File

@@ -126,24 +126,28 @@ let armor = {
event: events.fall,
specialClass: 'rogue',
value: 90,
canBuy: () => { return true; },
per: 15
},
fallWarrior: {
event: events.fall,
specialClass: 'warrior',
value: 90,
canBuy: () => { return true; },
con: 9
},
fallMage: {
event: events.fall,
specialClass: 'wizard',
value: 90,
canBuy: () => { return true; },
int: 9
},
fallHealer: {
event: events.fall,
specialClass: 'healer',
value: 90,
canBuy: () => { return true; },
con: 15
},
winter2015Rogue: {
@@ -453,24 +457,28 @@ let head = {
event: events.fall,
specialClass: 'rogue',
value: 60,
canBuy: () => { return true; },
per: 9
},
fallWarrior: {
event: events.fall,
specialClass: 'warrior',
value: 60,
canBuy: () => { return true; },
str: 9
},
fallMage: {
event: events.fall,
specialClass: 'wizard',
value: 60,
canBuy: () => { return true; },
per: 7
},
fallHealer: {
event: events.fall,
specialClass: 'healer',
value: 60,
canBuy: () => { return true; },
int: 7
},
winter2015Rogue: {
@@ -627,6 +635,7 @@ let headAccessory = {
text: t('headAccessoryBearEarsText'),
notes: t('headAccessoryBearEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_bearEars != null;
})
@@ -636,6 +645,7 @@ let headAccessory = {
text: t('headAccessoryCactusEarsText'),
notes: t('headAccessoryCactusEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_cactusEars != null;
})
@@ -645,6 +655,7 @@ let headAccessory = {
text: t('headAccessoryFoxEarsText'),
notes: t('headAccessoryFoxEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_foxEars != null;
})
@@ -654,6 +665,7 @@ let headAccessory = {
text: t('headAccessoryLionEarsText'),
notes: t('headAccessoryLionEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_lionEars != null;
})
@@ -663,6 +675,7 @@ let headAccessory = {
text: t('headAccessoryPandaEarsText'),
notes: t('headAccessoryPandaEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_pandaEars != null;
})
@@ -672,6 +685,7 @@ let headAccessory = {
text: t('headAccessoryPigEarsText'),
notes: t('headAccessoryPigEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_pigEars != null;
})
@@ -681,6 +695,7 @@ let headAccessory = {
text: t('headAccessoryTigerEarsText'),
notes: t('headAccessoryTigerEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_tigerEars != null;
})
@@ -690,6 +705,7 @@ let headAccessory = {
text: t('headAccessoryWolfEarsText'),
notes: t('headAccessoryWolfEarsNotes'),
value: 20,
canBuy: () => { return true; },
canOwn: ((u) => {
return u.items.gear.owned.headAccessory_special_wolfEars != null;
})
@@ -799,18 +815,21 @@ let shield = {
event: events.fall,
specialClass: 'rogue',
value: 80,
canBuy: () => { return true; },
str: 8
},
fallWarrior: {
event: events.fall,
specialClass: 'warrior',
value: 70,
canBuy: () => { return true; },
con: 7
},
fallHealer: {
event: events.fall,
specialClass: 'healer',
value: 70,
canBuy: () => { return true; },
con: 9
},
winter2015Rogue: {
@@ -1036,12 +1055,14 @@ let weapon = {
event: events.fall,
specialClass: 'rogue',
value: 80,
canBuy: () => { return true; },
str: 8
},
fallWarrior: {
event: events.fall,
specialClass: 'warrior',
value: 90,
canBuy: () => { return true; },
str: 15
},
fallMage: {
@@ -1049,6 +1070,7 @@ let weapon = {
specialClass: 'wizard',
twoHanded: true,
value: 160,
canBuy: () => { return true; },
int: 15,
per: 7
},
@@ -1056,6 +1078,7 @@ let weapon = {
event: events.fall,
specialClass: 'healer',
value: 90,
canBuy: () => { return true; },
int: 9
},
winter2015Rogue: {

View File

@@ -1,6 +1,10 @@
import {setHatchingPotionDefaults} from './helpers';
import {
merge,
translator as t,
setHatchingPotionDefaults
} from './helpers';
let hatchingPotions = {
let dropPotions = {
Base: {
value: 2,
},
@@ -30,9 +34,25 @@ let hatchingPotions = {
},
Golden: {
value: 5,
}
},
};
setHatchingPotionDefaults(hatchingPotions);
let premiumPotions = {
Spooky: {
value: 2,
addlNotes: t('premiumPotionAddlNotes'),
premium: true,
limited: true,
},
};
export default hatchingPotions;
setHatchingPotionDefaults(dropPotions);
setHatchingPotionDefaults(premiumPotions);
let allPotions = merge([dropPotions, premiumPotions]);
export default {
all: allPotions,
drop: dropPotions,
premium: premiumPotions,
};

View File

@@ -59,13 +59,14 @@ export function setSpellDefaults (className, spells) {
export function setFoodDefaults(food, options={}) {
each(food, (item, name) => {
let formattedName = formatForTranslator(name);
let canBuy = () => { return options.canBuy; };
defaults(item, {
key: name,
text: translator(`food${formattedName}`),
notes: translator('foodNotes'),
value: 1,
canBuy: options.canBuy || false,
canBuy: canBuy,
canDrop: options.canDrop || false,
});
});
@@ -78,6 +79,7 @@ export function setHatchingPotionDefaults(hatchingPotions) {
key: key,
value: 2,
text: text,
canBuy: () => { return true },
notes: translator('hatchingPotionNotes', {
potText: text
}),
@@ -93,7 +95,7 @@ export function setQuestDefaults(quests) {
key: key,
text: translator(`quest${formattedName}Text`),
notes: translator(`quest${formattedName}Notes`),
canBuy: true,
canBuy: () => { return true; },
value: 4,
};
@@ -149,6 +151,7 @@ export function setGearSetDefaults(gearSet, options={}) {
let gearDefaults = {
text: translator(text),
notes: translator(notes, attributes),
canBuy: () => { return false; },
}
defaults(item, gearDefaults);
@@ -183,11 +186,11 @@ export function generateBackgrounds(sets) {
export function generateEggs(set, options={}) {
let eggs = {};
let type = options.type;
let canBuy = options.canBuy;
each(set, (pet) => {
let text = translator(`${type}Egg${pet}Text`);
let adj = translator(`${type}Egg${pet}Adjective`);
let canBuy = options.canBuy(pet);
eggs[pet] = {
text: text,

View File

@@ -26,7 +26,11 @@ import {
specialMounts,
} from './pets-mounts/index';
import timeTravelStable from './time-traveler-stable';
import hatchingPotions from './hatching-potions';
import {
all as allHatchingPotions,
drop as dropHatchingPotions,
premium as premiumHatchingPotions,
} from './hatching-potions';
import food from './food/index';
import {
all as allQuests,
@@ -66,7 +70,9 @@ export default {
special: special,
// Item Drops
hatchingPotions: hatchingPotions,
dropHatchingPotions: dropHatchingPotions,
premiumHatchingPotions: premiumHatchingPotions,
hatchingPotions: allHatchingPotions,
food: food,
// Eggs

View File

@@ -16,21 +16,26 @@ import {transform, defaults} from 'lodash';
// <fake_potion> - if a unique animal, potion is Base, if it's a special version of an existing animal, the fake potion is a different from a normal hatching potion
//--------------------------------------------------
import hatchingPotions from '../hatching-potions';
import {
drop as dropHatchingPotions,
premium as premiumHatchingPotions,
} from '../hatching-potions';
import dropEggs from '../eggs/drops';
import questEggs from '../eggs/quest';
import specialPets from './special-pets';
import specialMounts from './special-mounts';
let dropPets = generateAnimalSet(dropEggs);
let questPets = generateAnimalSet(questEggs);
let dropMounts = generateAnimalSet(dropEggs);
let questMounts = generateAnimalSet(questEggs);
let dropPets = generateAnimalSet(dropEggs, dropHatchingPotions);
let premiumPets = generateAnimalSet(dropEggs, premiumHatchingPotions);
let questPets = generateAnimalSet(questEggs, dropHatchingPotions);
let dropMounts = generateAnimalSet(dropEggs, dropHatchingPotions);
let premiumMounts = generateAnimalSet(dropEggs, premiumHatchingPotions);
let questMounts = generateAnimalSet(questEggs, dropHatchingPotions);
function generateAnimalSet(set) {
return transform(set, (m, egg) => {
defaults(m, transform(hatchingPotions, (m2, pot) => {
function generateAnimalSet(eggSet, potionSet) {
return transform(eggSet, (m, egg) => {
defaults(m, transform(potionSet, (m2, pot) => {
return m2[egg.key + "-" + pot.key] = true;
}));
});
@@ -39,6 +44,8 @@ function generateAnimalSet(set) {
export default {
dropPets: dropPets,
dropMounts: dropMounts,
premiumPets: premiumPets,
premiumMounts: premiumMounts,
questPets: questPets,
questMounts: questMounts,
specialPets: specialPets,

View File

@@ -117,7 +117,7 @@ let holidayQuests = {
let questDefaults = (name) => {
return {
completion: t(`quest${name}Completion`),
canBuy: false,
canBuy: () => { return false; },
category: 'pet',
}
};

View File

@@ -160,7 +160,7 @@ let questDefaults = (name) => {
return {
completion: t(`quest${name}Completion`),
value: 0,
canBuy: false,
canBuy: () => { return false; },
category: 'world',
}
};

View File

@@ -544,7 +544,7 @@ describe 'User', ->
expect(quest.notes()).to.be.an('string')
expect(quest.completion()).to.be.an('string') if quest.completion
expect(quest.previous).to.be.an('string') if quest.previous
expect(quest.value).to.be.greaterThan 0 if quest.canBuy
expect(quest.value).to.be.greaterThan 0 if quest.canBuy()
expect(quest.drop.gp).to.not.be.lessThan 0
expect(quest.drop.exp).to.not.be.lessThan 0
expect(quest.category).to.match(/pet|unlockable|gold|world/)

View File

@@ -26,6 +26,16 @@ describe('count', function() {
expect(beastMasterTotal).to.eql(1);
});
it('does not count pets hatched with premium potions', function() {
var pets = {
"Wolf-Spooky": 5,
"Dragon-Spooky": 5,
"FlyingPig-Base": 5
}
var beastMasterTotal = count.beastMasterProgress(pets);
expect(beastMasterTotal).to.eql(1);
});
it('does not count special pets', function() {
var pets = {
"Wolf-Base": 2,
@@ -65,6 +75,15 @@ describe('count', function() {
expect(mountMasterTotal).to.eql(2);
});
it('does not count premium mounts', function() {
var mounts = {
"Dragon-Red": true,
"FlyingPig-Spooky": true
}
var mountMasterTotal = count.mountMasterProgress(mounts);
expect(mountMasterTotal).to.eql(1);
});
it('does not count quest mounts', function() {
var mounts = { "Dragon-Red": true, "Gryphon-Base": true };
var mountMasterTotal = count.mountMasterProgress(mounts);

View File

@@ -0,0 +1,139 @@
var sinon = require('sinon');
var chai = require('chai');
chai.use(require('sinon-chai'))
var expect = chai.expect
require('coffee-script');
var shared = require('../../common/script/index.coffee');
var content = require('../../common/script/content/index.coffee');
describe('user.ops.hatch', function() {
var user;
beforeEach(function() {
user = {
items: {
eggs: {},
hatchingPotions: {},
pets: {}
}
};
shared.wrap(user);
});
context('Pet Hatching', function() {
context('failure conditions', function() {
it('does not allow hatching without specifying egg and potion', function(done) {
user.ops.hatch({params:{}},function(response) {
expect(response.message).to.eql('Please specify query.egg & query.hatchingPotion');
expect(user.items.pets).to.be.empty;
done();
});
});
it('does not allow hatching if user lacks specified egg', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Base':1};
user.ops.hatch({params:{egg:'Dragon',hatchingPotion:'Base'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageMissingEggPotion'));
expect(user.items.pets).to.be.empty;
expect(user.items.eggs).to.eql({'Wolf':1});
expect(user.items.hatchingPotions).to.eql({'Base':1});
done();
});
});
it('does not allow hatching if user lacks specified hatching potion', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Base':1};
user.ops.hatch({params:{egg:'Wolf',hatchingPotion:'Golden'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageMissingEggPotion'));
expect(user.items.pets).to.be.empty;
expect(user.items.eggs).to.eql({'Wolf':1});
expect(user.items.hatchingPotions).to.eql({'Base':1});
done();
});
});
it('does not allow hatching if user already owns target pet', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Base':1};
user.items.pets = {'Wolf-Base':10};
user.ops.hatch({params:{egg:'Wolf',hatchingPotion:'Base'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageAlreadyPet'));
expect(user.items.pets).to.eql({'Wolf-Base':10});
expect(user.items.eggs).to.eql({'Wolf':1});
expect(user.items.hatchingPotions).to.eql({'Base':1});
done();
});
});
it('does not allow hatching quest pet egg using premium potion', function(done) {
user.items.eggs = {'Cheetah':1};
user.items.hatchingPotions = {'Spooky':1};
user.ops.hatch({params:{egg:'Cheetah',hatchingPotion:'Spooky'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageInvalidEggPotionCombo'));
expect(user.items.pets).to.be.empty;
expect(user.items.eggs).to.eql({'Cheetah':1});
expect(user.items.hatchingPotions).to.eql({'Spooky':1});
done();
});
});
});
context('successful hatching', function() {
it('hatches a basic pet', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Base':1};
user.ops.hatch({params:{egg:'Wolf',hatchingPotion:'Base'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageHatched'));
expect(user.items.pets).to.eql({'Wolf-Base':5});
expect(user.items.eggs).to.eql({'Wolf':0});
expect(user.items.hatchingPotions).to.eql({'Base':0});
done();
});
});
it('hatches a quest pet', function(done) {
user.items.eggs = {'Cheetah':1};
user.items.hatchingPotions = {'Base':1};
user.ops.hatch({params:{egg:'Cheetah',hatchingPotion:'Base'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageHatched'));
expect(user.items.pets).to.eql({'Cheetah-Base':5});
expect(user.items.eggs).to.eql({'Cheetah':0});
expect(user.items.hatchingPotions).to.eql({'Base':0});
done();
});
});
it('hatches a premium pet', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Spooky':1};
user.ops.hatch({params:{egg:'Wolf',hatchingPotion:'Spooky'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageHatched'));
expect(user.items.pets).to.eql({'Wolf-Spooky':5});
expect(user.items.eggs).to.eql({'Wolf':0});
expect(user.items.hatchingPotions).to.eql({'Spooky':0});
done();
});
});
it('hatches a pet previously raised to a mount', function(done) {
user.items.eggs = {'Wolf':1};
user.items.hatchingPotions = {'Base':1};
user.items.pets = {'Wolf-Base':-1};
user.ops.hatch({params:{egg:'Wolf',hatchingPotion:'Base'}}, function(response) {
expect(response.message).to.eql(shared.i18n.t('messageHatched'));
expect(user.items.pets).to.eql({'Wolf-Base':5});
expect(user.items.eggs).to.eql({'Wolf':0});
expect(user.items.hatchingPotions).to.eql({'Base':0});
done();
});
});
});
});
});

View File

@@ -24,7 +24,8 @@ describe('Stats Service', function() {
"Deer-Golden" : 5,
"Deer-Red" : 5,
"Egg-Desert" : 5,
"MantisShrimp-Base" : 5
"MantisShrimp-Base" : 5,
"Wolf-Spooky": 5
}
var beastMasterDisplay = statCalc.beastMasterProgress(user.items.pets);
@@ -38,9 +39,10 @@ describe('Stats Service', function() {
"BearCub-CottonCandyBlue" : -1,
"Cactus-Zombie" : 5,
"Deer-Golden" : 5,
"Deer-Red" : 5,
"Deer-Red" : -1,
"Egg-Desert" : 5,
"MantisShrimp-Base" : 5
"MantisShrimp-Base" : 5,
"Wolf-Spooky": -1
}
var beastMasterDisplay = statCalc.beastMasterProgress(user.items.pets);
@@ -221,7 +223,8 @@ describe('Stats Service', function() {
"Wolf-Golden" : true,
"Owl-CottonCandyBlue" : true,
"Mammoth-Base" : true,
"Bunny-Skeleton" : true
"Bunny-Skeleton" : true,
"Tiger-Spooky": true
}
var mountMasterDisplay = statCalc.mountMasterProgress(user.items.mounts);
@@ -237,7 +240,8 @@ describe('Stats Service', function() {
"Wolf-Golden" : false,
"Owl-CottonCandyBlue" : true,
"Mammoth-Base" : true,
"Bunny-Skeleton" : true
"Bunny-Skeleton" : true,
"Tiger-Spooky": true
}
var mountMasterDisplay = statCalc.mountMasterProgress(user.items.mounts);

View File

@@ -232,10 +232,10 @@ var UserSchema = new Schema({
_.defaults(
// First transform to a 1D eggs/potions mapping
_.transform(shared.content.pets, function(m,v,k){ m[k] = Number; }),
// Then add quest pets
// Then add additional pets (quest, backer, contributor, premium)
_.transform(shared.content.questPets, function(m,v,k){ m[k] = Number; }),
// Then add additional pets (backer, contributor)
_.transform(shared.content.specialPets, function(m,v,k){ m[k] = Number; })
_.transform(shared.content.specialPets, function(m,v,k){ m[k] = Number; }),
_.transform(shared.content.premiumPets, function(m,v,k){ m[k] = Number; })
),
currentPet: String, // Cactus-Desert
@@ -265,9 +265,10 @@ var UserSchema = new Schema({
mounts: _.defaults(
// First transform to a 1D eggs/potions mapping
_.transform(shared.content.pets, function(m,v,k){ m[k] = Boolean; }),
// Then add quest pets
// Then add quest and premium pets
_.transform(shared.content.questPets, function(m,v,k){ m[k] = Boolean; }),
// Then add additional pets (backer, contributor)
_.transform(shared.content.premiumPets, function(m,v,k){ m[k] = Boolean; }),
// Then add additional mounts (backer, contributor)
_.transform(shared.content.specialMounts, function(m,v,k){ m[k] = Boolean; })
),
currentMount: String,

View File

@@ -10,7 +10,7 @@
p.muted(ng-show='eggCount < 1')=env.t('noEggs')
div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)')
button.customize-option(class='Pet_Egg_{{::egg}}',
ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0)}',
ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0) && (Content.dropEggs[egg] || !selectedPotion.premium)}',
popover='{{::Content.eggs[egg].notes()}}', popover-append-to-body='true',
popover-title!=env.t("egg", {eggType: "{{::Content.eggs[egg].text()}}"}),
popover-trigger='mouseenter', popover-placement='right',
@@ -18,12 +18,12 @@
.badge.badge-info.stack-count {{points}}
li.customize-menu
menu.hatchingPotion-menu(label=(env.t('hatchingPotions') + ' ({{potCount}})'))
menu.pets-menu(label=(env.t('hatchingPotions') + ' ({{potCount}})'))
p.muted(ng-show='potCount < 1')=env.t('noHatchingPotions')
div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)')
button.customize-option(class='Pet_HatchingPotion_{{::pot}}',
ng-class='{selectableInventory: selectedEgg && !(user.items.pets[selectedEgg.key+"-"+pot]>0)}',
popover='{{::Content.hatchingPotions[pot].notes()}}',
ng-class='{selectableInventory: selectedEgg && !(user.items.pets[selectedEgg.key+"-"+pot]>0) && (Content.dropEggs[selectedEgg.key] || !Content.hatchingPotions[pot].premium)}',
popover='{{::Content.hatchingPotions[pot].notes()}} {{::Content.hatchingPotions[pot].addlNotes()}}',
popover-title!=env.t("potion", {potionType: "{{::Content.hatchingPotions[pot].text()}}"}),
popover-trigger='mouseenter', popover-placement='right',
popover-append-to-body='true',
@@ -102,7 +102,7 @@
menu.pets-menu(label=env.t('eggs'))
p.muted!=env.t('dropsExplanation')
div(ng-repeat='egg in Content.eggs', ng-if='egg.canBuy')
div(ng-repeat='egg in Content.eggs', ng-if='egg.canBuy(user)')
button.customize-option(class='Pet_Egg_{{::egg.key}}',
popover='{{::egg.notes()}}', popover-append-to-body='true',
popover-title!=env.t("egg", {eggType: "{{::egg.text()}}"}),
@@ -110,31 +110,11 @@
ng-click='purchase("eggs", egg)')
p {{::egg.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
//- buyable quest eggs. TODO: Get this from a collection so we don't have to maintain this ridiculous comma-delimited list
//- The hard part will be the trex stuff, since it is allowed to exist with two quests
each egg,quest in {gryphon:'Gryphon',hedgehog:'Hedgehog',ghost_stag:'Deer',rat:'Rat',octopus:'Octopus',dilatory_derby:'Seahorse',harpy:'Parrot',rooster:'Rooster',spider:'Spider',owl:'Owl',penguin:'Penguin',rock:'Rock',bunny:'Bunny',slime:'Slime',sheep:'Sheep',kraken:'Cuttlefish',whale:'Whale',cheetah:'Cheetah',horse:'Horse'}
div(ng-show='user.achievements.quests.#{quest} > 0')
button.customize-option(class='Pet_Egg_#{egg}',
popover='{{::Content.eggs.#{egg}.notes()}}', popover-append-to-body='true',
popover-title!=env.t("egg", {eggType: "{{::Content.eggs.#{egg}.text()}}"}),
popover-trigger='mouseenter', popover-placement='top',
ng-click='purchase("eggs", Content.eggs.#{egg})')
p {{::Content.eggs.#{egg}.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
div(ng-show='(user.achievements.quests.trex + user.achievements.quests.trex_undead) > 0')
button.customize-option(class='Pet_Egg_TRex',
popover='{{::Content.eggs.TRex.notes()}}', popover-append-to-body='true',
popover-title!=env.t("egg", {eggType: "{{Content.eggs.TRex.text()}}"}),
popover-trigger='mouseenter', popover-placement='top',
ng-click='purchase("eggs", Content.eggs.TRex)')
p {{::Content.eggs.TRex.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
li.customize-menu
menu.pets-menu(label=env.t('hatchingPotions'))
p.muted!=env.t('dropsExplanation')
div(ng-repeat='pot in Content.hatchingPotions')
div(ng-repeat='pot in Content.hatchingPotions', ng-if='!pot.premium')
button.customize-option(class='Pet_HatchingPotion_{{::pot.key}}',
popover='{{::pot.notes()}}', popover-append-to-body='true',
popover-title!=env.t("potion", {potionType: "{{::pot.text()}}"}),
@@ -144,10 +124,23 @@
| {{::pot.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
li.customize-menu
menu.pets-menu!=env.t('magicHatchingPotions') + " - " + env.t('fallEventAvailability')
p.muted=env.t('premiumPotionNoDropExplanation')
div(ng-repeat='pot in Content.hatchingPotions', ng-if='pot.premium && pot.canBuy(user)')
button.customize-option(class='Pet_HatchingPotion_{{::pot.key}}',
popover='{{::pot.notes()}} {{::pot.addlNotes()}}', popover-append-to-body='true',
popover-title!=env.t("potion", {potionType: "{{::pot.text()}}"}),
popover-trigger='mouseenter', popover-placement='top',
ng-click='purchase("hatchingPotions", pot)')
p
| {{::pot.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
li.customize-menu
menu.pets-menu(label=env.t('food'))
p.muted!=env.t('dropsExplanation')
div(ng-repeat='food in Content.food', ng-if='food.canBuy')
div(ng-repeat='food in Content.food', ng-if='food.canBuy(user)')
button.customize-option(class='Pet_Food_{{::food.key}}',
popover='{{::food.notes()}}', popover-title='{{::food.text()}}',
popover-trigger='mouseenter', popover-placement='top',

View File

@@ -1,15 +1,14 @@
mixin mountList(source)
mixin mountList(eggSource, potionSource)
menu.pets(type='list')
each egg in source
each egg in eggSource
-if(!egg.noMount) {
li.customize-menu
menu
each potion in env.Content.hatchingPotions
each potion in potionSource
- mount = egg.key+"-"+potion.key
div(popover-trigger='mouseenter', popover=env.t('mountName', {potion: potion.text(env.language.code), mount: egg.mountText(env.language.code)}), popover-placement='bottom')
button(class="pet-button Mount_Head_#{mount}", ng-show='user.items.mounts["#{mount}"]', ng-class='{active: user.items.currentMount == "#{mount}"}', ng-click='chooseMount("#{egg.key}", "#{potion.key}")')
//div(class='Mount_Head_{{mount}}')
button(class="pet-button mount-not-owned", ng-hide='user.items.mounts["#{mount}"]')
button(class="pet-button mount-not-owned", ng-if='!user.items.mounts["#{mount}"] && (#{potion.canBuy()} || user.items.hatchingPotions["#{potion.key}"] || user.items.pets["#{mount}"])')
.PixelPaw
-}
@@ -24,10 +23,23 @@ mixin mountList(source)
p=env.t('mattShall', {name: "{{user.profile.name}}"})
h4 {{:: env.t('stableMountMasterProgress', { number: mountMasterProgress }) }}
.row: .col-md-12
+mountList(env.Content.dropEggs)
+mountList(env.Content.dropEggs,env.Content.dropHatchingPotions)
.row: .col-md-12
h4=env.t('magicMounts')
menu.pets(type='list')
each potion in env.Content.premiumHatchingPotions
li.customize-menu
menu
each egg in env.Content.dropEggs
- mount = egg.key+"-"+potion.key
div(popover-trigger='mouseenter', popover=env.t('mountName', {potion: potion.text(env.language.code), mount: egg.mountText(env.language.code)}), popover-placement='bottom')
button(class="pet-button Mount_Head_#{mount}", ng-show='user.items.mounts["#{mount}"]', ng-class='{active: user.items.currentMount == "#{mount}"}', ng-click='chooseMount("#{egg.key}", "#{potion.key}")')
button(class="pet-button mount-not-owned", ng-if='!user.items.mounts["#{mount}"] && (#{potion.canBuy()} || user.items.hatchingPotions["#{potion.key}"] || user.items.pets["#{mount}"])')
.PixelPaw
.row: .col-md-12
h4=env.t('questMounts')
+mountList(env.Content.questEggs)
+mountList(env.Content.questEggs,env.Content.dropHatchingPotions)
.row: .col-md-12
h4=env.t('rareMounts')
menu

View File

@@ -1,15 +1,15 @@
mixin petList(source)
mixin petList(eggSource, potionSource)
menu.pets(type='list')
each egg in source
each egg in eggSource
li.customize-menu
menu
each potion in env.Content.hatchingPotions
each potion in potionSource
- pet = egg.key+"-"+potion.key
div(popover-trigger='mouseenter', popover=env.t('petName', {potion: potion.text(env.language.code), egg: egg.text(env.language.code)}), popover-placement='bottom')
button(class="pet-button Pet-#{pet}", ng-if='user.items.pets["#{pet}"]>0', ng-class='{active: user.items.currentPet == "#{pet}", selectableInventory: #{!egg.noMount} && selectedFood && !user.items.mounts["#{pet}"]}', ng-click='choosePet("#{egg.key}", "#{potion.key}")')
.progress(ng-show='!user.items.mounts["#{pet}"]')
.progress-bar.progress-bar-success(ng-style='{width: user.items.pets["#{pet}"]/.5 + "%"}')
button(class="pet-button pet-not-owned", ng-if='!user.items.pets["#{pet}"]')
button(class="pet-button pet-not-owned", ng-if='!user.items.pets["#{pet}"] && (#{potion.canBuy()} || user.items.hatchingPotions["#{potion.key}"])')
.PixelPaw
button(class="pet-evolved pet-button Pet-#{pet}", ng-if='user.items.pets["#{pet}"]<0')
@@ -24,10 +24,25 @@ mixin petList(source)
p=env.t('mattBochText1')
h4 {{:: env.t('stableBeastMasterProgress', { number: beastMasterProgress }) }}
.row: .col-md-12
+petList(env.Content.dropEggs)
+petList(env.Content.dropEggs,env.Content.dropHatchingPotions)
.row: .col-md-12
h4=env.t('magicPets')
menu.pets(type='list')
each potion in env.Content.premiumHatchingPotions
li.customize-menu
menu
each egg in env.Content.dropEggs
- pet = egg.key+"-"+potion.key
div(popover-trigger='mouseenter', popover=env.t('petName', {potion: potion.text(env.language.code), egg: egg.text(env.language.code)}), popover-placement='bottom')
button(class="pet-button Pet-#{pet}", ng-if='user.items.pets["#{pet}"]>0', ng-class='{active: user.items.currentPet == "#{pet}", selectableInventory: #{!egg.noMount} && selectedFood && !user.items.mounts["#{pet}"]}', ng-click='choosePet("#{egg.key}", "#{potion.key}")')
.progress(ng-show='!user.items.mounts["#{pet}"]')
.progress-bar.progress-bar-success(ng-style='{width: user.items.pets["#{pet}"]/.5 + "%"}')
button(class="pet-button pet-not-owned", ng-if='!user.items.pets["#{pet}"] && (#{potion.canBuy()} || user.items.hatchingPotions["#{potion.key}"])')
.PixelPaw
button(class="pet-evolved pet-button Pet-#{pet}", ng-if='user.items.pets["#{pet}"]<0')
.row: .col-md-12
h4=env.t('questPets')
+petList(env.Content.questEggs)
+petList(env.Content.questEggs,env.Content.dropHatchingPotions)
.row: .col-md-12
h4=env.t('rarePets')

View File

@@ -19,7 +19,7 @@ include ../../shared/mixins
h3.equipment-title=env.t('questsForSale')
div(ng-repeat='type in Content.userCanOwnQuestCategories')
menu.pets-menu(label='{{env.t(type + "Quests")}}')
div(ng-repeat='quest in Content.questsByLevel', ng-if='quest.canBuy && quest.category === type')
div(ng-repeat='quest in Content.questsByLevel', ng-if='quest.canBuy(user) && quest.category === type')
button.customize-option(ng-class='lockQuest(quest) ? "inventory_quest_scroll_locked inventory_quest_scroll_{{::quest.key}}_locked locked" : "inventory_quest_scroll inventory_quest_scroll_{{::quest.key}}"',
data-popover-html="{{::lockQuest(quest,true) ? env.t('scrollsPre') : questPopover(quest) | markdown}}",
popover-title='{{::quest.text()}}', popover-append-to-body="true",

View File

@@ -33,7 +33,7 @@
span.Pet_Currency_Gem1x.inline-gems
menu.pets-menu(label=env.t('seasonalItems'))
div
button.customize-option(class='shop_spookDust',
button.customize-option(class='inventory_special_spookDust',
popover='{{::Content.spells.special.spookDust.notes()}}',
popover-title='{{::Content.spells.special.spookDust.text()}}',
popover-trigger='mouseenter', popover-placement='right',
@@ -41,6 +41,15 @@
ng-click='purchase("special", Content.spells.special.spookDust)')
p {{::Content.spells.special.spookDust.value}}
span(class='shop_gold')
div
button.customize-option(class='Pet_HatchingPotion_Spooky',
popover='{{::Content.hatchingPotions.Spooky.notes()}}',
popover-title!=env.t("potion", {potionType: "{{::Content.hatchingPotions.Spooky.text()}}"}),
popover-trigger='mouseenter', popover-placement='right',
popover-append-to-body='true',
ng-click='purchase("hatchingPotions", Content.hatchingPotions.Spooky)')
p {{::Content.hatchingPotions.Spooky.value}}&nbsp;
span.Pet_Currency_Gem1x.inline-gems
// div
button.customize-option(popover='{{::Content.spells.special.nye.notes()}}', popover-title='{{::Content.spells.special.nye.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='castStart(Content.spells.special.nye)', class='inventory_special_nye')

View File

@@ -1,35 +1,71 @@
h2 9/24/2015 - HAUNTED HAIR COLORS, SUPERNATURAL SKIN SET, AND WEREWOLF SUBSCRIBER ITEM! PLUS, THE FALL PLOT-LINE CONTINUES...
h2 10/1/2015 - SPOOKY HATCHING POTION; NEW ITEMS IN THE ENCHANTED ARMOIRE
hr
tr
td
h3 Haunted Hair Colors and Supernatural Skin Set
.promo_haunted_hair.pull-right
p The Seasonal Edition Haunted Hair Colors are now available for purchase in <a href='/#/options/profile/avatar'>the avatar customizations page</a>! Now you can dye your avatar's hair Pumpkin, Midnight, Candy Corn, Ghost White, Zombie, or Halloween. Get them before October 31st!
.Pet-Wolf-Spooky.pull-right
h3 Spooky Hatching Potions
p We've released a new feature: Magic Hatching Potions!
br
p The Supernatural Skin Set is also available until October 31st! Now your avatar can become an Ogre, Skeleton, Pumpkin, Candy Corn, Reptile, or Dread Shade.
p Between now and October 31st, you can buy Spooky Hatching Potions from <a href='/#/options/inventory/drops'>the Market</a> and use them to hatch any standard pet egg. (Magic Hatching Potions do not work on Quest Pet eggs.) You'll find it very easy to care for your new Spooky Pets: they're from the Flourishing Fields, so they love to eat every kind of food!
br
p Seasonal Edition items recur unchanged every year, but they are only available to purchase during a short period of time. Get them now, or you'll have to wait until next year!
p.small.muted by Lemoness, mariahm, and crystal phoenix
p Spooky Hatching Potions are a Seasonal Edition item, so they will only be available during the Fall Festival each year! Be sure to get them while you can.
p.small.muted by Lemoness and SabreCat
tr
td
h3 September Subscriber Items Revealed
.promo_mystery_201509.pull-right
p The September Subscriber Item has been revealed: the Werewolf Armor Set! All September subscribers will receive the Werewolf Mask and the Werewolf Costume. You still have six days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set! Thank you so much for your support - we really do rely on you to keep Habitica free to use and running smoothly.
p.small.muted by Lemoness
tr
td
h3 Fall Plot-Line Continues
p In general, we've all been enjoying the Flourishing Fields. Habiticans are posing in fun costumes, taking pictures of the orange-and-black wildlife, and casting Spooky Sparkles on each other.
.head_armoire_orangeCat.pull-right
h3 New Items in the Enchanted Armoire
p There is new equipment in Enchanted Armoire, a 100 GP Reward in the Rewards Column which unlocks after you've attained Ultimate Gear!
br
p Unfortunately, there does seem to be a serious problem with production for the first time in the history of the Fields. Deadlines are being missed. Shipments are not arriving. As you walk down the street, you overhear worried whispers among the citizens, speculating on the cause.
p Click on the Enchanted Armoire for a random chance at special Equipment, including the Black Cat Hat, Orange Cat Hat, Midnight Shield, and Bat Wing Wand! It may also give you random XP or food items. We'll be adding new equipment to it during the first week of each month, but even when you've exhausted the current supply, you can keep clicking for a chance at food and XP.
br
p Some blame the unseasonal heat wave that has begun in the past few days. Others point to the difficulty of the tasks, and their ever-increasing quantity. And a few people -- just a few -- murmur that some of the hardest-working citizens have been disappearing, one by one, leaving their obligations abandoned. But surely that is nothing more than rumor?
p Now go spend all that accumulated Gold! May the Random Number Generator smile upon you...
p.small.muted by Lemoness and SabreCat
p.small.muted art by Kiwibot and UncommonCriminal
if menuItem !== 'oldNews'
hr
a(href='/static/old-news', target='_blank') Read older news
mixin oldNews
h2 9/30/2015 - LAST CHANCE FOR WEREWOLF ARMOR SET; BACK-TO-SCHOOL CHALLENGE WINNERS ANNOUNCED
tr
td
h3 Last Chance for Werewolf Armor Set
.promo_mystery_201509.pull-right
p Reminder: this is the final day to <a href='/#/options/settings/subscription'>subscribe</a> and receive the Werewolf Armor Set! If you want the Werewolf Costume or the Werewolf Mask, now's the time! Thanks so much for your support.
tr
td
h3 Back-to-School Challenge Winners!
p The winners of the Back-to-School Challenge have been selected! Congratulations to: Randy, Jenn, AnnDeLune, Sh1n1 DeFier, Velinde, Nadine, citrusella, Thiago Coascci, Alicia Puck Vickery, and goblin.
br
p Thank you to everyone who shared your tips!
h2 9/24/2015 - HAUNTED HAIR COLORS, SUPERNATURAL SKIN SET, AND WEREWOLF SUBSCRIBER ITEM! PLUS, THE FALL PLOT-LINE CONTINUES...
tr
td
h3 Haunted Hair Colors and Supernatural Skin Set
.promo_haunted_hair.pull-right
p The Seasonal Edition Haunted Hair Colors are now available for purchase in <a href='/#/options/profile/avatar'>the avatar customizations page</a>! Now you can dye your avatar's hair Pumpkin, Midnight, Candy Corn, Ghost White, Zombie, or Halloween. Get them before October 31st!
br
p The Supernatural Skin Set is also available until October 31st! Now your avatar can become an Ogre, Skeleton, Pumpkin, Candy Corn, Reptile, or Dread Shade.
br
p Seasonal Edition items recur unchanged every year, but they are only available to purchase during a short period of time. Get them now, or you'll have to wait until next year!
p.small.muted by Lemoness, mariahm, and crystal phoenix
tr
td
h3 September Subscriber Items Revealed
.promo_mystery_201509.pull-right
p The September Subscriber Item has been revealed: the Werewolf Armor Set! All September subscribers will receive the Werewolf Mask and the Werewolf Costume. You still have six days to <a href='/#/options/settings/subscription'>subscribe</a> and receive the item set! Thank you so much for your support - we really do rely on you to keep Habitica free to use and running smoothly.
p.small.muted by Lemoness
tr
td
h3 Fall Plot-Line Continues
p In general, we've all been enjoying the Flourishing Fields. Habiticans are posing in fun costumes, taking pictures of the orange-and-black wildlife, and casting Spooky Sparkles on each other.
br
p Unfortunately, there does seem to be a serious problem with production for the first time in the history of the Fields. Deadlines are being missed. Shipments are not arriving. As you walk down the street, you overhear worried whispers among the citizens, speculating on the cause.
br
p Some blame the unseasonal heat wave that has begun in the past few days. Others point to the difficulty of the tasks, and their ever-increasing quantity. And a few people -- just a few -- murmur that some of the hardest-working citizens have been disappearing, one by one, leaving their obligations abandoned. But surely that is nothing more than rumor?
h2 9/21/2015 - FALL FESTIVAL! LIMITED-EDITION OUTFITS, SEASONAL SHOP, CANDY FOOD DROPS, AND NPC DRESS-UP
tr
td