Merge branch 'develop' into blade-Finish_weeklies_pr

This commit is contained in:
Blade Barringer
2015-06-05 17:35:16 -05:00
71 changed files with 9033 additions and 8423 deletions

View File

@@ -37,6 +37,12 @@
margin-right: 10px; margin-right: 10px;
} }
.multi-achievement {
margin: auto;
padding-left: 0.5em;
padding-right: 0.5em;
}
[class*="Mount_Head_"], [class*="Mount_Body_"]{ [class*="Mount_Head_"], [class*="Mount_Body_"]{
margin-top:18px; /* Sprite accommodates 105x123 box */ margin-top:18px; /* Sprite accommodates 105x123 box */
} }

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 304 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 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: 153 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 KiB

After

Width:  |  Height:  |  Size: 681 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 353 KiB

After

Width:  |  Height:  |  Size: 357 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 300 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: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 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: 19 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: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 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.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 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: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 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: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 825 B

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -95,5 +95,13 @@
"backgroundMountainLakeText": "Mountain Lake", "backgroundMountainLakeText": "Mountain Lake",
"backgroundMountainLakeNotes": "Dip your toes in a Mountain Lake.", "backgroundMountainLakeNotes": "Dip your toes in a Mountain Lake.",
"backgroundPagodasText": "Pagodas", "backgroundPagodasText": "Pagodas",
"backgroundPagodasNotes": "Climb to the top of Pagodas." "backgroundPagodasNotes": "Climb to the top of Pagodas.",
"backgrounds062015": "SET 13: Released June 2015",
"backgroundDriftingRaftText": "Drifting Raft",
"backgroundDriftingRaftNotes": "Paddle a Drifting Raft.",
"backgroundShimmeryBubblesText": "Shimmery Bubbles",
"backgroundShimmeryBubblesNotes": "Float through a sea of Shimmery Bubbles.",
"backgroundIslandWaterfallsText": "Island Waterfalls",
"backgroundIslandWaterfallsNotes": "Picnic near Island Waterfalls."
} }

View File

@@ -52,9 +52,11 @@
"costume": "Costume", "costume": "Costume",
"costumeText": "If you prefer the look of other gear to what you have equipped, check the \"Use Costume\" box to visually don a costume while wearing your battle gear underneath.", "costumeText": "If you prefer the look of other gear to what you have equipped, check the \"Use Costume\" box to visually don a costume while wearing your battle gear underneath.",
"useCostume": "Use Costume", "useCostume": "Use Costume",
"gearAchievement": "You have earned the \"Ultimate Gear\" Achievement for upgrading to the maximum gear set!", "gearAchievement": "You have earned the \"Ultimate Gear\" Achievement for upgrading to the maximum gear set for a class! You have attained the following complete sets:",
"moreGearAchievements": "To attain more Ultimate Gear badges, change classes on <a href='/#/options/profile/stats' target='_blank'>your stats page</a> and buy up your new class's gear!",
"armoireUnlocked": "You've also unlocked the <strong>Enchanted Armoire!</strong> Click on the Enchanted Armoire Reward for a random chance at special Equipment! It may also give you random XP or food items.",
"ultimGearName": "Ultimate Gear", "ultimGearName": "Ultimate Gear",
"ultimGearText": "Has upgraded to the maximum weapon and armor set", "ultimGearText": "Has upgraded to the maximum weapon and armor set for the following classes:",
"level": "Level", "level": "Level",
"levelUp": "Level Up!", "levelUp": "Level Up!",
"mana": "Mana", "mana": "Mana",

View File

@@ -2,6 +2,11 @@
"potionText": "Health Potion", "potionText": "Health Potion",
"potionNotes": "Recover 15 Health (Instant Use)", "potionNotes": "Recover 15 Health (Instant Use)",
"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 every month. Until then, keep clicking for Experience and Food!",
"dropEggWolfText": "Wolf", "dropEggWolfText": "Wolf",
"dropEggWolfAdjective": "loyal", "dropEggWolfAdjective": "loyal",

View File

@@ -138,6 +138,11 @@
"weaponMystery301404Text": "Steampunk Cane", "weaponMystery301404Text": "Steampunk Cane",
"weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.", "weaponMystery301404Notes": "Excellent for taking a turn about town. March 3015 Subscriber Item. Confers no benefit.",
"weaponArmoireBasicCrossbowText": "Basic Crossbow",
"weaponArmoireBasicCrossbowNotes": "This crossbow can pierce a task's armor from very far away! Increases Strength by <%= str %>, Perception by <%= per %>, and Constitution by <%= con %>. Enchanted Armoire: Independent Item.",
"weaponArmoireLunarSceptreText": "Soothing Lunar Sceptre",
"weaponArmoireLunarSceptreNotes": "The healing power of this wand waxes and wanes. Increases Constitution by <%= con %> and Intelligence by <%= int %>. Enchanted Armoire: Soothing Lunar Set (Item 3 of 3).",
"armor": "armor", "armor": "armor",
"armorBase0Text": "Plain Clothing", "armorBase0Text": "Plain Clothing",
@@ -282,6 +287,11 @@
"armorMystery301404Text": "Steampunk Suit", "armorMystery301404Text": "Steampunk Suit",
"armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.", "armorMystery301404Notes": "Dapper and dashing, wot! Confers no benefit. February 3015 Subscriber Item.",
"armorArmoireLunarArmorText": "Soothing Lunar Armor",
"armorArmoireLunarArmorNotes": "The light of the moon will make you strong and savvy. Increases Strength by <%= str %> and Intelligence by <%= int %>. Enchanted Armoire: Soothing Lunar Set (Item 2 of 3).",
"armorArmoireGladiatorArmorText": "Gladiator Armor",
"armorArmoireGladiatorArmorNotes": "To be a gladiator you must be not only cunning... but strong. Increases Perception by <%= per %> and Strength by <%= str %>. Enchanted Armoire: Gladiator Set (Item 2 of 3).",
"headgear": "headgear", "headgear": "headgear",
"headBase0Text": "No Helm", "headBase0Text": "No Helm",
@@ -422,6 +432,17 @@
"headMystery301405Text": "Basic Top Hat", "headMystery301405Text": "Basic Top Hat",
"headMystery301405Notes": "A basic top hat, just begging to be paired with some fancy head accessories. Confers no benefit. May 3015 Subscriber Item.", "headMystery301405Notes": "A basic top hat, just begging to be paired with some fancy head accessories. Confers no benefit. May 3015 Subscriber Item.",
"headArmoireLunarCrownText": "Soothing Lunar Crown",
"headArmoireLunarCrownNotes": "This crown strengthens health and sharpens senses, especially when the moon is full. Increases Constitution by <%= con %> and Perception by <%= per %>. Enchanted Armoire: Soothing Lunar Set (Item 1 of 3).",
"headArmoireRedHairbowText": "Red Hairbow",
"headArmoireRedHairbowNotes": "Become strong, tough and smart while wearing this beautiful Red Hairbow! Increases Strength by <%= str %>, Constitution by <%= con %>, and Intelligence by <%= int %>. Enchanted Armoire: Independent Item.",
"headArmoireVioletFloppyHatText": "Violet Floppy Hat",
"headArmoireVioletFloppyHatNotes": "Many spells have been sewn into this simple hat, giving it a pleasing purple color. Increases Perception by <%= per %>, Intelligence by <%= int %>, and Constitution by <%= con %>. Enchanted Armoire: Independent Item.",
"headArmoireGladiatorHelmText": "Gladiator Helm",
"headArmoireGladiatorHelmNotes": "To be a gladiator you must be not only strong.... but cunning. Increases Intelligence by <%= int %> and Perception by <%= per %>. Enchanted Armoire: Gladiator Set (Item 1 of 3).",
"headArmoireRancherHatText": "Rancher Hat",
"headArmoireRancherHatNotes": "Round up your pets and wrangle your mounts while wearing this magical Rancher Hat! Increases Strength by <%= str %>, Perception by <%= per %>, and Intelligence by <%= int %>. Enchanted Armoire: Independent Item.",
"offhand": "shield-hand item", "offhand": "shield-hand item",
"shieldBase0Text": "No Shield-Hand Equipment", "shieldBase0Text": "No Shield-Hand Equipment",
@@ -500,6 +521,9 @@
"shieldMystery301405Text": "Clock Shield", "shieldMystery301405Text": "Clock Shield",
"shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.", "shieldMystery301405Notes": "Time is on your side with this towering clock shield! Confers no benefit. June 3015 Subscriber Item.",
"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).",
"backBase0Text": "No Back Accessory", "backBase0Text": "No Back Accessory",
"backBase0Notes": "No Back Accessory.", "backBase0Notes": "No Back Accessory.",

View File

@@ -39,7 +39,7 @@
"close": "Close", "close": "Close",
"saveAndClose": "Save & Close", "saveAndClose": "Save & Close",
"cancel": "Cancel", "cancel": "Cancel",
"ok": "Ok", "ok": "OK",
"add": "Add", "add": "Add",
"undo": "Undo", "undo": "Undo",
"continue": "Continue", "continue": "Continue",

View File

@@ -20,5 +20,8 @@
"messageDropFood": "You've found <%= dropArticle %><%= dropText %>! <%= dropNotes %>", "messageDropFood": "You've found <%= dropArticle %><%= dropText %>! <%= dropNotes %>",
"messageDropEgg": "You've found a <%= dropText %> Egg! <%= dropNotes %>", "messageDropEgg": "You've found a <%= dropText %> Egg! <%= dropNotes %>",
"messageDropPotion": "You've found a <%= dropText %> Hatching Potion! <%= dropNotes %>", "messageDropPotion": "You've found a <%= dropText %> Hatching Potion! <%= dropNotes %>",
"messageFoundQuest": "You've found the quest \"<%= questText %>\"!" "messageFoundQuest": "You've found the quest \"<%= questText %>\"!",
"armoireEquipment": "<%= image %> You found a piece of rare Equipment in the Armoire: <%= dropText %>! Awesome!",
"armoireFood": "<%= image %> You rummage in the Armoire and find <%= dropArticle %><%= dropText %>. What's that doing in here?",
"armoireExp": "You wrestle with the Armoire and gain Experience. Take that!"
} }

View File

@@ -140,6 +140,9 @@ gear =
201502: text: t('weaponMystery201502Text'), notes: t('weaponMystery201502Notes'), mystery:'201502', value: 0 201502: text: t('weaponMystery201502Text'), notes: t('weaponMystery201502Notes'), mystery:'201502', value: 0
201505: text: t('weaponMystery201505Text'), notes: t('weaponMystery201505Notes'), mystery:'201505', value: 0 201505: text: t('weaponMystery201505Text'), notes: t('weaponMystery201505Notes'), mystery:'201505', value: 0
301404: text: t('weaponMystery301404Text'), notes: t('weaponMystery301404Notes'), mystery:'301404', value: 0 301404: text: t('weaponMystery301404Text'), notes: t('weaponMystery301404Notes'), mystery:'301404', value: 0
armoire:
basicCrossbow: text: t('weaponArmoireBasicCrossbowText'), notes: t('weaponArmoireBasicCrossbowNotes', {str: 5, per: 5, con: 5}), value: 100, str: 5, per: 5, con: 5, canOwn: ((u)-> u.items.gear.owned.weapon_armoire_basicCrossbow?)
lunarSceptre: text: t('weaponArmoireLunarSceptreText'), notes: t('weaponArmoireLunarSceptreNotes', {con: 7, int: 7}), value: 100, con: 7, int: 7, set: 'soothing', canOwn: ((u)-> u.items.gear.owned.weapon_armoire_lunarSceptre?)
armor: armor:
base: base:
@@ -224,6 +227,9 @@ gear =
201503: text: t('armorMystery201503Text'), notes: t('armorMystery201503Notes'), mystery:'201503', value: 0 201503: text: t('armorMystery201503Text'), notes: t('armorMystery201503Notes'), mystery:'201503', value: 0
201504: text: t('armorMystery201504Text'), notes: t('armorMystery201504Notes'), mystery:'201504', value: 0 201504: text: t('armorMystery201504Text'), notes: t('armorMystery201504Notes'), mystery:'201504', value: 0
301404: text: t('armorMystery301404Text'), notes: t('armorMystery301404Notes'), mystery:'301404', value: 0 301404: text: t('armorMystery301404Text'), notes: t('armorMystery301404Notes'), mystery:'301404', value: 0
armoire:
lunarArmor: text: t('armorArmoireLunarArmorText'), notes: t('armorArmoireLunarArmorNotes', {str: 7, int: 7}), value: 100, str: 7, int: 7, set: 'soothing', canOwn: ((u)-> u.items.gear.owned.armor_armoire_lunarArmor?)
gladiatorArmor: text: t('armorArmoireGladiatorArmorText'), notes: t('armorArmoireGladiatorArmorNotes', {str: 7, per: 7}), value: 100, str: 7, per: 7, set: 'gladiator', canOwn: ((u)-> u.items.gear.owned.armor_armoire_gladiatorArmor?)
head: head:
base: base:
@@ -306,6 +312,12 @@ gear =
201505: text: t('headMystery201505Text'), notes: t('headMystery201505Notes'), mystery:'201505', value: 0 201505: text: t('headMystery201505Text'), notes: t('headMystery201505Notes'), mystery:'201505', value: 0
301404: text: t('headMystery301404Text'), notes: t('headMystery301404Notes'), mystery:'301404', value: 0 301404: text: t('headMystery301404Text'), notes: t('headMystery301404Notes'), mystery:'301404', value: 0
301405: text: t('headMystery301405Text'), notes: t('headMystery301405Notes'), mystery:'301405', value: 0 301405: text: t('headMystery301405Text'), notes: t('headMystery301405Notes'), mystery:'301405', value: 0
armoire:
lunarCrown: text: t('headArmoireLunarCrownText'), notes: t('headArmoireLunarCrownNotes', {con: 7, per: 7}), value: 100, con: 7, per: 7, set: 'soothing', canOwn: ((u)-> u.items.gear.owned.head_armoire_lunarCrown?)
redHairbow: text: t('headArmoireRedHairbowText'), notes: t('headArmoireRedHairbowNotes', {str: 5, int: 5, con: 5}), value: 100, str: 5, int: 5, con: 5, canOwn: ((u)-> u.items.gear.owned.head_armoire_redHairbow?)
violetFloppyHat: text: t('headArmoireVioletFloppyHatText'), notes: t('headArmoireVioletFloppyHatNotes', {per: 5, int: 5, con: 5}), value: 100, per: 5, int: 5, con: 5, canOwn: ((u)-> u.items.gear.owned.head_armoire_violetFloppyHat?)
gladiatorHelm: text: t('headArmoireGladiatorHelmText'), notes: t('headArmoireGladiatorHelmNotes', {per: 7, int: 7}), value: 100, per: 7, int: 7, set: 'gladiator', canOwn: ((u)-> u.items.gear.owned.head_armoire_gladiatorHelm?)
rancherHat: text: t('headArmoireRancherHatText'), notes: t('headArmoireRancherHatNotes', {str: 5, per: 5, int: 5}), value: 100, str: 5, per: 5, int: 5, canOwn: ((u)-> u.items.gear.owned.head_armoire_rancherHat?)
shield: shield:
base: base:
@@ -365,6 +377,8 @@ gear =
spring2015Healer: event: events.spring2015, specialClass: 'healer', text: t('shieldSpecialSpring2015HealerText'), notes: t('shieldSpecialSpring2015HealerNotes', {con: 9}), value: 70, con: 9 spring2015Healer: event: events.spring2015, specialClass: 'healer', text: t('shieldSpecialSpring2015HealerText'), notes: t('shieldSpecialSpring2015HealerNotes', {con: 9}), value: 70, con: 9
mystery: mystery:
301405: text: t('shieldMystery301405Text'), notes: t('shieldMystery301405Notes'), mystery:'301405', value: 0 301405: text: t('shieldMystery301405Text'), notes: t('shieldMystery301405Notes'), mystery:'301405', value: 0
armoire:
gladiatorShield: text: t('shieldArmoireGladiatorShieldText'), notes: t('shieldArmoireGladiatorShieldNotes', {con: 5, str: 5}), value: 100, con: 5, str: 5, set: 'gladiator', canOwn: ((u)-> u.items.gear.owned.shield_armoire_gladiatorShield?)
back: back:
base: base:
@@ -442,7 +456,7 @@ api.gear =
flat: {} flat: {}
_.each gearTypes, (type) -> _.each gearTypes, (type) ->
_.each classes.concat(['base', 'special', 'mystery']), (klass) -> _.each classes.concat(['base', 'special', 'mystery', 'armoire']), (klass) ->
# add "type" to each item, so we can reference that as "weapon" or "armor" in the html # add "type" to each item, so we can reference that as "weapon" or "armor" in the html
_.each gear[type][klass], (item, i) -> _.each gear[type][klass], (item, i) ->
key = "#{type}_#{klass}_#{i}" key = "#{type}_#{klass}_#{i}"
@@ -475,11 +489,12 @@ api.timeTravelerStore = (owned) ->
### ###
--------------------------------------------------------------- ---------------------------------------------------------------
Potion Unique Rewards: Potion and Armoire
--------------------------------------------------------------- ---------------------------------------------------------------
### ###
api.potion = type: 'potion', text: t('potionText'), notes: t('potionNotes'), value: 25, key: 'potion' api.potion = type: 'potion', text: t('potionText'), notes: t('potionNotes'), value: 25, key: 'potion'
api.armoire = type: 'armoire', text: t('armoireText'), notes: t('armoireNotesEmpty'), value: 100, key: 'armoire', canOwn: ((u)-> _.contains(u.achievements.ultimateGearSets, true))
### ###
--------------------------------------------------------------- ---------------------------------------------------------------
@@ -1840,6 +1855,16 @@ api.backgrounds =
pagodas: pagodas:
text: t('backgroundPagodasText') text: t('backgroundPagodasText')
notes: t('backgroundPagodasNotes') notes: t('backgroundPagodasNotes')
backgrounds062015:
drifting_raft:
text: t('backgroundDriftingRaftText')
notes: t('backgroundDriftingRaftNotes')
shimmery_bubbles:
text: t('backgroundShimmeryBubblesText')
notes: t('backgroundShimmeryBubblesNotes')
island_waterfalls:
text: t('backgroundIslandWaterfallsText')
notes: t('backgroundIslandWaterfallsNotes')
api.subscriptionBlocks = api.subscriptionBlocks =
basic_earned: months:1, price:5 basic_earned: months:1, price:5

View File

@@ -188,8 +188,9 @@ api.updateStore = (user) ->
true true
# Add special items (contrib gear, backer gear, etc) # Add special items (contrib gear, backer gear, etc)
changes = changes.concat _.filter content.gear.flat, (v) -> changes = changes.concat _.filter content.gear.flat, (v) ->
v.klass in ['special','mystery'] and !user.items.gear.owned[v.key] and v.canOwn?(user) v.klass in ['special','mystery','armoire'] and !user.items.gear.owned[v.key] and v.canOwn?(user)
changes.push content.potion changes.push content.potion
if user.flags.armoireEnabled then changes.push content.armoire
# Return sorted store (array) # Return sorted store (array)
_.sortBy changes, (c)->sortOrder[c.type] _.sortBy changes, (c)->sortOrder[c.type]
@@ -385,6 +386,10 @@ api.countTriad = (pets) ->
if pets[egg + "-" + potion] > 0 then count3++ if pets[egg + "-" + potion] > 0 then count3++
count3 count3
api.countArmoire = (gear) ->
count = _.size(_.filter(content.gear.flat, ((i)->i.klass is 'armoire' and !gear[i.key])))
count
### ###
------------------------------------------------------ ------------------------------------------------------
User (prototype wrapper to give it ops, helper funcs, and virtuals User (prototype wrapper to give it ops, helper funcs, and virtuals
@@ -465,7 +470,7 @@ api.wrap = (user, main=true) ->
if v if v
itm = content.gear.flat[''+k] itm = content.gear.flat[''+k]
if itm if itm
if (itm.value > 0 || k == 'weapon_warrior_0') && ( itm.klass == cl || ( itm.klass == 'special' && (! itm.specialClass || itm.specialClass == cl) ) ) if (itm.value > 0 || k == 'weapon_warrior_0') && ( itm.klass == cl || ( itm.klass == 'special' && (! itm.specialClass || itm.specialClass == cl) ) || itm.klass == 'armoire' )
losableItems[''+k]=''+k losableItems[''+k]=''+k
lostItem = user.fns.randomVal losableItems lostItem = user.fns.randomVal losableItems
if item = content.gear.flat[lostItem] if item = content.gear.flat[lostItem]
@@ -859,23 +864,41 @@ api.wrap = (user, main=true) ->
buy: (req, cb) -> buy: (req, cb) ->
{key} = req.params {key} = req.params
item = if key is 'potion' then content.potion else content.gear.flat[key] item = if key is 'potion' then content.potion
else if key is 'armoire' then content.armoire
else content.gear.flat[key]
return cb?({code:404, message:"Item '#{key} not found (see https://github.com/HabitRPG/habitrpg-shared/blob/develop/script/content.coffee)"}) unless item return cb?({code:404, message:"Item '#{key} not found (see https://github.com/HabitRPG/habitrpg-shared/blob/develop/script/content.coffee)"}) unless item
return cb?({code:401, message: i18n.t('messageNotEnoughGold', req.language)}) if user.stats.gp < item.value return cb?({code:401, message: i18n.t('messageNotEnoughGold', req.language)}) if user.stats.gp < item.value
return cb?({code:401, message: "You can't own this item"}) if item.canOwn? and !item.canOwn(user) return cb?({code:401, message: "You can't buy this item"}) if item.canOwn? and !item.canOwn(user)
if item.key is 'potion' if item.key is 'potion'
user.stats.hp += 15 user.stats.hp += 15
user.stats.hp = 50 if user.stats.hp > 50 user.stats.hp = 50 if user.stats.hp > 50
else if item.key is 'armoire'
armoireResult = user.fns.predictableRandom()
eligibleEquipment = _.filter(content.gear.flat, ((i)->i.klass is 'armoire' and !user.items.gear.owned[i.key]))
if !_.isEmpty(eligibleEquipment) and (armoireResult < .6 or !user.flags.armoireOpened)
drop = user.fns.randomVal(eligibleEquipment)
user.items.gear.owned[drop.key] = true
user.flags.armoireOpened = true
message = i18n.t('armoireEquipment', {image: '<span class="shop_'+drop.key+' pull-left"></span>', dropText: drop.text(req.language)}, req.language)
if api.countArmoire(user.items.gear.owned) is 0 then user.flags.armoireEmpty = true
else if (!_.isEmpty(eligibleEquipment) and armoireResult < .8) or armoireResult < .5
drop = user.fns.randomVal _.where(content.food, {canDrop:true})
user.items.food[drop.key] ?= 0
user.items.food[drop.key] += 1
message = i18n.t('armoireFood', {image: '<span class="Pet_Food_'+drop.key+' pull-left"></span>', dropArticle: drop.article, dropText: drop.text(req.language)}, req.language)
else
user.stats.exp += Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10)
message = i18n.t('armoireExp', req.language)
else else
user.items.gear.equipped[item.type] = item.key user.items.gear.equipped[item.type] = item.key
user.items.gear.owned[item.key] = true user.items.gear.owned[item.key] = true
message = user.fns.handleTwoHanded(item, null, req) message = user.fns.handleTwoHanded(item, null, req)
message ?= i18n.t('messageBought', {itemText: item.text(req.language)}, req.language) message ?= i18n.t('messageBought', {itemText: item.text(req.language)}, req.language)
if not user.achievements.ultimateGear and item.last if item.last then user.fns.ultimateGear()
user.fns.ultimateGear()
user.stats.gp -= item.value user.stats.gp -= item.value
mixpanel?.track("Acquire Item",{'itemName':key,'acquireMethod':'Gold','goldCost':item.value}) mixpanel?.track("Acquire Item",{'itemName':key,'acquireMethod':'Gold','goldCost':item.value})
cb? {code:200, message}, _.pick(user,$w 'items achievements stats') cb? {code:200, message}, _.pick(user,$w 'items achievements stats flags')
buyMysterySet: (req, cb)-> buyMysterySet: (req, cb)->
return cb?({code:401, message:"You don't have enough Mystic Hourglasses"}) unless user.purchased.plan.consecutive.trinkets>0 return cb?({code:401, message:"You don't have enough Mystic Hourglasses"}) unless user.purchased.plan.consecutive.trinkets>0
@@ -1684,32 +1707,19 @@ api.wrap = (user, main=true) ->
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
# Achievements # Achievements
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
ultimateGear: () -> ultimateGear: ->
# on the server this is a LoDash transform, on the client its an object # on the server this is a Lodash transform, on the client its an object
gear = if window? then user.items.gear.owned else user.items.gear.owned.toObject() owned = if window? then user.items.gear.owned else user.items.gear.owned.toObject()
ownedLastGear = _.chain(content.gear.flat) user.achievements.ultimateGearSets ?= {healer: false, wizard: false, rogue: false, warrior: false}
.pick(_.keys gear) content.classes.forEach (klass) ->
.values() user.achievements.ultimateGearSets[klass] = _.reduce ['armor', 'shield', 'head', 'weapon'], (soFarGood, type) ->
.filter (gear) -> gear.last found = _.find content.gear.tree[type][klass], {last:true}
soFarGood and (!found or owned[found.key]==true) #!found only true when weapon is two-handed (mages)
lastGearClassTypeMatrix = {} , true # start with true, else `and` will fail right away
_.each content.classes, (klass) -> user.markModified? 'achievements.ultimateGearSets'
lastGearClassTypeMatrix[klass] = {} if _.contains(user.achievements.ultimateGearSets, true) and user.flags.armoireEnabled != true
#_.each content.gearTypes, (type) -> user.flags.armoireEnabled = true
_.each ['armor', 'weapon', 'shield', 'head'], (type) -> user.markModified? 'flags'
lastGearClassTypeMatrix[klass][type] = false
return true # false exits the each loop early
ownedLastGear.each (gear) ->
lastGearClassTypeMatrix[gear.klass]["shield"] = true if gear.twoHanded
lastGearClassTypeMatrix[gear.klass][gear.type] = true
shouldGrant = _(lastGearClassTypeMatrix)
.values()
.reduce(((ans, klass) -> ans or _(klass).values().reduce(((ans, gearType) -> ans and gearType), true)), false)
.valueOf()
user.achievements.ultimateGear = shouldGrant
nullify: -> nullify: ->
user.ops = null user.ops = null

View File

@@ -0,0 +1,117 @@
/**
* Created by Sabe on 6/3/2015.
*/
var migrationName = '20150604_ultimateGearSets';
var authorName = process.env.AUTHOR_NAME || 'Sabe'; // in case script author needs to know when their ...
var authorUuid = process.env.AUTHOR_UUID || '7f14ed62-5408-4e1b-be83-ada62d504931'; //... own data is done
/*
* grant the new ultimateGearSets achievement for existing users' collected equipment
*/
var dbserver = process.env.DB_SERVER || 'localhost:27017'; // CHANGE THIS FOR PRODUCTION DATABASE
var dbname = process.env.DB_NAME || 'habitrpg';
var mongo = require('mongoskin');
var _ = require('lodash');
var dbUsers = mongo.db(dbserver + '/' + dbname + '?auto_reconnect').collection('users');
var fields = {
'achievements.ultimateGearSets':1,
'items.gear.owned':1
};
var query = {
// 'auth.timestamps.loggedin':{$lte:new Date('2015-05-22')},
$or: [
{'items.gear.owned.weapon_wizard_6': {$exists: true}},
{'items.gear.owned.armor_wizard_5': {$exists: true}},
{'items.gear.owned.head_wizard_5': {$exists: true}},
{'items.gear.owned.weapon_warrior_6': {$exists: true}},
{'items.gear.owned.armor_warrior_5': {$exists: true}},
{'items.gear.owned.head_warrior_5': {$exists: true}},
{'items.gear.owned.shield_warrior_5': {$exists: true}},
{'items.gear.owned.weapon_healer_6': {$exists: true}},
{'items.gear.owned.armor_healer_5': {$exists: true}},
{'items.gear.owned.head_healer_5': {$exists: true}},
{'items.gear.owned.shield_healer_5': {$exists: true}},
{'items.gear.owned.weapon_rogue_6': {$exists: true}},
{'items.gear.owned.armor_rogue_5': {$exists: true}},
{'items.gear.owned.head_rogue_5': {$exists: true}},
{'items.gear.owned.shield_rogue_6': {$exists: true}}
]
};
console.warn('Updating users...');
var progressCount = 1000;
var count = 0;
dbUsers.findEach(query, fields, {batchSize:250}, function(err, user) {
if (err) { return exiting(1, 'ERROR! ' + err); }
if (!user) {
console.warn('All appropriate users found and modified.');
return displayData();
}
count++;
var achievements = {};
var changeUser = false;
if ( (typeof user.items.gear.owned.weapon_wizard_6 !== 'undefined')
&& (typeof user.items.gear.owned.armor_wizard_5 !== 'undefined')
&& (typeof user.items.gear.owned.head_wizard_5 !== 'undefined')
) {
achievements['wizard'] = true;
changeUser = true;
}
if ( (typeof user.items.gear.owned.weapon_warrior_6 !== 'undefined')
&& (typeof user.items.gear.owned.armor_warrior_5 !== 'undefined')
&& (typeof user.items.gear.owned.head_warrior_5 !== 'undefined')
&& (typeof user.items.gear.owned.shield_warrior_5 !== 'undefined')
) {
achievements['warrior'] = true;
changeUser = true;
}
if ( (typeof user.items.gear.owned.weapon_healer_6 !== 'undefined')
&& (typeof user.items.gear.owned.armor_healer_5 !== 'undefined')
&& (typeof user.items.gear.owned.head_healer_5 !== 'undefined')
&& (typeof user.items.gear.owned.shield_healer_5 !== 'undefined')
) {
achievements['healer'] = true;
changeUser = true;
}
if ( (typeof user.items.gear.owned.weapon_rogue_6 !== 'undefined')
&& (typeof user.items.gear.owned.armor_rogue_5 !== 'undefined')
&& (typeof user.items.gear.owned.head_rogue_5 !== 'undefined')
&& (typeof user.items.gear.owned.shield_rogue_6 !== 'undefined')
) {
achievements['rogue'] = true;
changeUser = true;
}
if (changeUser) {
var set = {'migration':migrationName, 'achievements.ultimateGearSets':achievements, 'flags.armoireEnabled':true};
dbUsers.update({_id:user._id}, {$set:set});
}
if (count%progressCount == 0) console.warn(count + ' ' + user._id);
if (user._id == authorUuid) console.warn(authorName + ' processed');
if (user._id == '9' ) console.warn('lefnire' + ' processed');
});
function displayData() {
console.warn('\n' + count + ' users processed\n');
return exiting(0);
}
function exiting(code, msg) {
code = code || 0; // 0 = success
if (code && !msg) { msg = 'ERROR!'; }
if (msg) {
if (code) { console.error(msg); }
else { console.log( msg); }
}
process.exit(code);
}

View File

@@ -33,7 +33,8 @@ newUser = (addTasks=true)->
todos: [] todos: []
rewards: [] rewards: []
flags: {} flags: {}
achievements: {} achievements:
ultimateGearSets: {}
contributor: contributor:
level: 2 level: 2
@@ -432,7 +433,6 @@ describe 'User', ->
expect(spell.lvl).to.be.above(0) expect(spell.lvl).to.be.above(0)
expect(spell.cast).to.be.a('function') expect(spell.cast).to.be.a('function')
describe 'drop system', -> describe 'drop system', ->
user = null user = null
@@ -481,6 +481,88 @@ describe 'User', ->
user.fns.randomVal.restore() user.fns.randomVal.restore()
user.fns.predictableRandom.restore() user.fns.predictableRandom.restore()
describe 'Enchanted Armoire', ->
user = newUser()
fullArmoire = {'weapon_warrior_0': true, 'armor_armoire_gladiatorArmor':true,'armor_armoire_lunarArmor':true,'head_armoire_gladiatorHelm':true,'head_armoire_lunarCrown':true,'head_armoire_rancherHat':true,'head_armoire_redHairbow':true,'head_armoire_violetFloppyHat':true,'shield_armoire_gladiatorShield':true,'weapon_armoire_basicCrossbow':true,'weapon_armoire_lunarSceptre':true}
beforeEach ->
# too many predictableRandom calls to stub, let's return the last element
sinon.stub(user.fns, 'randomVal', (obj)->
result = undefined
for key, val of obj
result = val
result
)
it 'counts all available equipment before any are claimed', ->
sinon.stub(user.fns, 'predictableRandom').returns 0
expect(shared.countArmoire(user.items.gear.owned)).to.eql (_.size(fullArmoire) - 1)
it 'does not open without paying', ->
sinon.stub(user.fns, 'predictableRandom').returns 0
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true}
expect(user.items.food).to.eql {}
expect(user.stats.exp).to.eql 0
it 'does not open without Ultimate Gear achievement', ->
sinon.stub(user.fns, 'predictableRandom').returns 0
user.stats.gp = 500
user.ops.buy({params: {key: 'armoire'}})
user.achievements.ultimateGearSets = {'healer':false,'wizard':false,'rogue':false,'warrior':false}
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true}
expect(user.items.food).to.eql {}
expect(user.stats.exp).to.eql 0
it 'always drops equipment the first time', ->
sinon.stub(user.fns, 'predictableRandom', cycle [.9,.5])
user.achievements.ultimateGearSets = {'healer':false,'wizard':false,'rogue':true,'warrior':false}
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true, 'shield_armoire_gladiatorShield':true}
expect(shared.countArmoire(user.items.gear.owned)).to.eql (_.size(fullArmoire) - 2)
expect(user.items.food).to.eql {}
expect(user.stats.exp).to.eql 0
expect(user.stats.gp).to.eql 400
it 'gives Experience', ->
sinon.stub(user.fns, 'predictableRandom', cycle [.9,.5])
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true, 'shield_armoire_gladiatorShield':true}
expect(user.items.food).to.eql {}
expect(user.stats.exp).to.eql 30
expect(user.stats.gp).to.eql 300
it 'gives food', ->
sinon.stub(user.fns, 'predictableRandom', cycle [.7,.5])
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true, 'shield_armoire_gladiatorShield':true}
expect(user.items.food).to.eql {'Honey': 1}
expect(user.stats.exp).to.eql 30
expect(user.stats.gp).to.eql 200
it 'gives more equipment', ->
sinon.stub(user.fns, 'predictableRandom', cycle [.5,.5])
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql {'weapon_warrior_0': true, 'shield_armoire_gladiatorShield':true,'head_armoire_rancherHat':true}
expect(shared.countArmoire(user.items.gear.owned)).to.eql (_.size(fullArmoire) - 3)
expect(user.items.food).to.eql {'Honey': 1}
expect(user.stats.exp).to.eql 30
expect(user.stats.gp).to.eql 100
it 'does not give equipment if all equipment has been found', ->
sinon.stub(user.fns, 'predictableRandom', cycle [.5,.5])
user.items.gear.owned = fullArmoire
user.ops.buy({params: {key: 'armoire'}})
expect(user.items.gear.owned).to.eql fullArmoire
expect(shared.countArmoire(user.items.gear.owned)).to.eql 0
expect(user.items.food).to.eql {'Honey': 1}
expect(user.stats.exp).to.eql 60
expect(user.stats.gp).to.eql 0
afterEach ->
user.fns.randomVal.restore()
user.fns.predictableRandom.restore()
describe 'Quests', -> describe 'Quests', ->
_.each shared.content.quests, (quest)-> _.each shared.content.quests, (quest)->
@@ -510,11 +592,11 @@ describe 'User', ->
_.each [1..5], (i) -> _.each [1..5], (i) ->
user.ops.buy {params:'#{type}_#{klass}_#{i}'} user.ops.buy {params:'#{type}_#{klass}_#{i}'}
it 'does not get ultimateGear ' + klass, -> it 'does not get ultimateGear ' + klass, ->
expect(user.achievements.ultimateGear).to.not.be.ok() expect(user.achievements.ultimateGearSets[klass]).to.not.be.ok()
_.each shared.content.gearTypes, (type) -> _.each shared.content.gearTypes, (type) ->
user.ops.buy {params:'#{type}_#{klass}_6'} user.ops.buy {params:'#{type}_#{klass}_6'}
xit 'gets ultimateGear ' + klass, -> xit 'gets ultimateGear ' + klass, ->
expect(user.achievements.ultimateGear).to.be.ok() expect(user.achievements.ultimateGearSets[klass]).to.be.ok()
it 'does not get beastMaster if user has less than 90 drop pets', -> it 'does not get beastMaster if user has less than 90 drop pets', ->
user = newUser() user = newUser()

View File

@@ -0,0 +1,74 @@
'use strict'
TEST_DB = process.env.DB_NAME = 'habitrpg_migration_test'
process.env.NODE_DB_URI = 'mongodb://localhost/' + TEST_DB
app = require('../../website/src/server')
sh = require('shelljs')
runMigration = ->
sh.exec 'node ./migrations/20150604_ultimateGearSets.js'
describe 'Backfill for granting ultimate gear sets achievement', ->
before (done) ->
sh.exec "mongo \"#{TEST_DB}\" --eval \"db.dropDatabase()\""
done()
context 'User without any purchased equipment', ->
before (done) ->
registerNewUser done, true
it 'does not update user', (done)->
user_gear = user.items.gear.owned
expect(user_gear.weapon_wizard_6).to.not.exist
expect(user.achievements.ultimateGearSets).to.not.exist
runMigration()
User.findById user._id, (err, _user) ->
user = _user
expect(user.achievements.ultimateGearSets).to.not.exist
done()
context 'User with all but one needed piece of equipment', ->
before (done) ->
registerNewUser ->
items = {
weapon_wizard_6: true
armor_wizard_5: true
}
User.findByIdAndUpdate user._id, {'items.gear.owned': items}, (err, _user) ->
user = _user
done()
, true
it 'does not update user', (done)->
runMigration()
User.findById user._id, (err, _user) ->
user = _user
expect(user.achievements.ultimateGearSets).to.not.exist
done()
context 'User with all necessary equipment', ->
before (done) ->
registerNewUser ->
items = {
weapon_wizard_6: true
armor_wizard_5: true
head_wizard_5: true
}
User.findByIdAndUpdate user._id, {'items.gear.owned': items}, (err, _user) ->
user = _user
done()
, true
it 'grants user ultimate gear', (done)->
runMigration()
User.findById user._id, (err, _user) ->
user = _user
expect(user.achievements.ultimateGearSets.wizard).to.exist
done()

View File

@@ -10,8 +10,12 @@ describe('Inventory Controller', function() {
inject(function($rootScope, $controller, Shared){ inject(function($rootScope, $controller, Shared){
user = specHelper.newUser(); user = specHelper.newUser();
user.balance = 4, user.balance = 4;
user.items = {eggs: {Cactus: 1}, hatchingPotions: {Base: 1}, food: {Meat: 1}, pets: {}, mounts: {}}; user.items.eggs = {Cactus: 1};
user.items.hatchingPotions = {Base: 1};
user.items.food = {Meat: 1};
user.items.pets = {}
user.items.mounts = {};
Shared.wrap(user); Shared.wrap(user);
var mockWindow = { var mockWindow = {
confirm: function(msg){ confirm: function(msg){

View File

@@ -13,7 +13,7 @@ specHelper = {
food: {}, food: {},
pets: {}, pets: {},
mounts: {}, mounts: {},
gear: {equipped: {}, costume: {}}, gear: {equipped: {}, costume: {}, owned: {}},
}, },
party: { party: {
quest: { quest: {

View File

@@ -101,9 +101,14 @@ habitrpg.controller('NotificationCtrl',
} }
}); });
$rootScope.$watch('user.achievements.ultimateGear', function(after, before){ $rootScope.$watch('user.achievements.ultimateGearSets', function(after, before){
if (after === before || after !== true) return; if (_.isEqual(after,before) || !_.contains(User.user.achievements.ultimateGearSets, true)) return;
$rootScope.openModal('achievements/ultimateGear'); $rootScope.openModal('achievements/ultimateGear');
}, true);
$rootScope.$watch('user.flags.armoireEmpty', function(after,before){
if (before == undefined || after == before || after == false) return;
$rootScope.openModal('armoireEmpty');
}); });
$rootScope.$watch('user.achievements.rebirths', function(after, before){ $rootScope.$watch('user.achievements.rebirths', function(after, before){

View File

@@ -4,6 +4,11 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide) { function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide) {
$scope.obj = User.user; // used for task-lists $scope.obj = User.user; // used for task-lists
$scope.user = User.user; $scope.user = User.user;
$scope.armoireCount = function(gear) {
return Shared.countArmoire(gear);
};
$scope.score = function(task, direction) { $scope.score = function(task, direction) {
switch (task.type) { switch (task.type) {
case 'reward': case 'reward':
@@ -213,7 +218,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
------------------------ ------------------------
*/ */
$scope.$watch('user.items.gear.equipped', function(){ $scope.$watchGroup(['user.items.gear.owned', 'user.flags.armoireEnabled'], function(){
$scope.itemStore = Shared.updateStore(User.user); $scope.itemStore = Shared.updateStore(User.user);
},true); },true);

View File

@@ -34,7 +34,8 @@ var UserSchema = new Schema({
originalUser: Boolean, originalUser: Boolean,
helpedHabit: Boolean, //TODO: Deprecate this. Superseded by habitSurveys helpedHabit: Boolean, //TODO: Deprecate this. Superseded by habitSurveys
habitSurveys: Number, habitSurveys: Number,
ultimateGear: Boolean, ultimateGear: Boolean, //TODO: Deprecate this. Superseded by ultimateGearSets
ultimateGearSets: Schema.Types.Mixed,
beastMaster: Boolean, beastMaster: Boolean,
beastMasterCount: Number, beastMasterCount: Number,
mountMaster: Boolean, mountMaster: Boolean,
@@ -156,7 +157,10 @@ var UserSchema = new Schema({
lastWeeklyRecap: {type: Date, 'default': Date.now}, lastWeeklyRecap: {type: Date, 'default': Date.now},
communityGuidelinesAccepted: {type: Boolean, 'default': false}, communityGuidelinesAccepted: {type: Boolean, 'default': false},
cronCount: {type:Number, 'default':0}, cronCount: {type:Number, 'default':0},
welcomed: {type: Boolean, 'default': false} welcomed: {type: Boolean, 'default': false},
armoireEnabled: {type: Boolean, 'default': false},
armoireOpened: {type: Boolean, 'default': false},
armoireEmpty: {type: Boolean, 'default': false}
}, },
history: { history: {
exp: Array, // [{date: Date, value: Number}], // big peformance issues if these are defined exp: Array, // [{date: Date, value: Number}], // big peformance issues if these are defined

View File

@@ -6,7 +6,7 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html')
div div
button.btn.btn-default(type="button", ng-click='dequip("battleGear");') {{env.t("unequipBattleGear")}} button.btn.btn-default(type="button", ng-click='dequip("battleGear");') {{env.t("unequipBattleGear")}}
li.customize-menu.inventory-gear li.customize-menu.inventory-gear
menu.pets-menu(label='{{::label}}', ng-repeat='(klass,label) in {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer"), special:env.t("special"), mystery:env.t("mystery")}', ng-show='gear[klass]') menu.pets-menu(label='{{::label}}', ng-repeat='(klass,label) in {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer"), special:env.t("special"), mystery:env.t("mystery"), armoire:env.t("armoireText")}', ng-show='gear[klass]')
div(ng-repeat='item in gear[klass]') div(ng-repeat='item in gear[klass]')
button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='user.ops.equip({params:{key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}') button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='user.ops.equip({params:{key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}')
.col-md-6 .col-md-6
@@ -20,7 +20,7 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html')
button.btn.btn-default(type="button", ng-click='dequip("costume");') {{env.t("unequipCostume")}} button.btn.btn-default(type="button", ng-click='dequip("costume");') {{env.t("unequipCostume")}}
button.btn.btn-default(type="button", ng-click='dequip("petMountBackground");') {{env.t("unequipPetMountBackground")}} button.btn.btn-default(type="button", ng-click='dequip("petMountBackground");') {{env.t("unequipPetMountBackground")}}
li.customize-menu(ng-if='user.preferences.costume') li.customize-menu(ng-if='user.preferences.costume')
menu.pets-menu(label='{{::label}}', ng-repeat='(klass,label) in {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer"), special:env.t("special"), mystery:env.t("mystery")}', ng-show='gear[klass]') menu.pets-menu(label='{{::label}}', ng-repeat='(klass,label) in {warrior:env.t("warrior"), wizard:env.t("mage"), rogue:env.t("rogue"), healer:env.t("healer"), special:env.t("special"), mystery:env.t("mystery"), armoire:env.t("armoireText")}', ng-show='gear[klass]')
div(ng-repeat='item in gear[klass]') div(ng-repeat='item in gear[klass]')
button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') button.customize-option(popover='{{::item.notes()}}', popover-title='{{::item.text()}}', popover-trigger='mouseenter', popover-placement='right', popover-append-to-body='true', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{::item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}')

View File

@@ -14,7 +14,27 @@ script(id='modals/achievements/ultimateGear.html', type='text/ng-template')
h4=env.t('modalAchievement') h4=env.t('modalAchievement')
.modal-body .modal-body
.achievement.achievement-armor .achievement.achievement-armor
=env.t('gearAchievement') p=env.t('gearAchievement')
br
table.multi-achievement
tr
td(ng-if='::user.achievements.ultimateGearSets.healer').multi-achievement
.achievement-ultimate-healer.multi-achievement
=env.t('healer')
td(ng-if='::user.achievements.ultimateGearSets.wizard').multi-achievement
.achievement-ultimate-mage.multi-achievement
=env.t('mage')
td(ng-if='::user.achievements.ultimateGearSets.rogue').multi-achievement
.achievement-ultimate-rogue.multi-achievement
=env.t('rogue')
td(ng-if='::user.achievements.ultimateGearSets.warrior').multi-achievement
.achievement-ultimate-warrior.multi-achievement
=env.t('warrior')
br
p(ng-if='!(user.achievements.ultimateGearSets.healer && user.achievements.ultimateGearSets.wizard && user.achievements.ultimateGearSets.rogue && user.achievements.ultimateGearSets.warrior)')!=env.t('moreGearAchievements')
br
.shop_armoire.pull-right
p!=env.t("armoireUnlocked")
.modal-footer .modal-footer
button.btn.btn-default(ng-click='$close()')=env.t('ok') button.btn.btn-default(ng-click='$close()')=env.t('ok')

View File

@@ -51,3 +51,14 @@ script(type='text/ng-template', id='modals/pet-key.html')
span(ng-if='!user.achievements.triadBingo') span(ng-if='!user.achievements.triadBingo')
| : 6&nbsp; | : 6&nbsp;
span.Pet_Currency_Gem1x.inline-gems span.Pet_Currency_Gem1x.inline-gems
script(type='text/ng-template', id='modals/armoireEmpty.html')
.modal-header
.shop_armoire.pull-right
h4=env.t('armoireText')
.modal-body
p=env.t('armoireLastItem')
br
p=env.t('armoireNotesEmpty')
.modal-footer
button.btn.btn-default(ng-click='$close()')=env.t('close')

View File

@@ -1,10 +1,22 @@
h5 6/1/2015 - JUNE MYSTERY ITEM AND NEW MOUNT POSITIONING! h5 6/1/2015 - NEW EQUIPMENT: THE ENCHANTED ARMOIRE, JUNE BACKGROUNDS, AND NEW MOUNT POSITIONING!
hr hr
tr tr
td td
.inventory_present_06.pull-right .promo_enchanted_armoire.pull-right
h5 June Mystery Item! h5 New Equipment: The Enchanted Armoire!
p Ooh, how mysterious! All Habiticans who are <a href='/#/options/settings/subscription' target='_blank'>subscribed</a> during the month of June will receive the June Mystery Item Set, as well as the ability to buy Gems with Gold! The June Item Set will be revealed on the 25th, so keep your eyes peeled. Thanks for supporting the site <3 p Now after you achieve Ultimate Gear, you'll unlock a new Reward: THE ENCHANTED ARMOIRE!
br
p Click on the Enchanted Armoire, a 100 GP Reward in the Rewards Column, for a random chance at special Equipment! It may also give you random XP or food items. We'll be adding new equipment to it every month, but even when you've exhausted the current supply, you can keep clicking for a chance at food and XP.
br
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, Starsystemic, UncommonCriminal, Zoebeagle, and Andrews38
tr
td
.background_island_waterfalls.pull-right
h5 June Backgrounds Revealed
p There are three new avatar backgrounds in the <a href='/#/options/profile/backgrounds' target='_blank'>Background Shop</a>! Now your avatar can paddle a Drifting Raft, float through a sea of Shimmery Bubbles, or picnic near Island Waterfalls!
p.small.muted by (in order): Teto is Great, beffymaroo, and UncommonCriminal
tr tr
td td
h5 New Mount Positioning! h5 New Mount Positioning!
@@ -16,6 +28,12 @@ a(href='/static/old-news', target='_blank') Read older news
mixin oldNews mixin oldNews
h5 6/1/2015 - JUNE MYSTERY ITEM!
tr
td
.inventory_present_06.pull-right
h5 June Mystery Item!
p Ooh, how mysterious! All Habiticans who are <a href='/#/options/settings/subscription' target='_blank'>subscribed</a> during the month of June will receive the June Mystery Item Set, as well as the ability to buy Gems with Gold! The June Item Set will be revealed on the 25th, so keep your eyes peeled. Thanks for supporting the site <3
h5 5/31/2015 - PUSH NOTIFICATIONS FOR ANDROID, AND LAST CHANCE FOR GREEN KNIGHT SUBSCRIBER ITEMS! h5 5/31/2015 - PUSH NOTIFICATIONS FOR ANDROID, AND LAST CHANCE FOR GREEN KNIGHT SUBSCRIBER ITEMS!
tr tr
td td

View File

@@ -78,18 +78,25 @@ div(ng-if='::profile.achievements.perfect || user._id == profile._id')
small(ng-if='::profile.achievements.perfect == 1')=env.t('perfectSingularText') small(ng-if='::profile.achievements.perfect == 1')=env.t('perfectSingularText')
hr hr
//-div(ng-if='::profile.achievements.ultimateGear || user._id == profile._id') div(ng-if='::profile.achievements.ultimateGearSets || user._id == profile._id')
.achievement.achievement-armor(ng-if='::profile.achievements.ultimateGear') .achievement.achievement-armor(ng-if='::profile.achievements.ultimateGearSets')
div(ng-class='::{muted: !profile.achievements.ultimateGear}') div(ng-class='::{muted: !profile.achievements.ultimateGearSets}')
h5=env.t('ultimGearName') h5=env.t('ultimGearName')
small=env.t('ultimGearText') small=env.t('ultimGearText')
hr table.multi-achievement
// Remove the following when ultimate gear is fixed (https://github.com/HabitRPG/habitrpg/issues/2232): tr
div(ng-if='::user._id == profile._id') td(ng-if='::profile.achievements.ultimateGearSets.healer').multi-achievement
div.muted .achievement-ultimate-healer.multi-achievement
h5=env.t('ultimGearName') =env.t('healer')
small td(ng-if='::profile.achievements.ultimateGearSets.wizard').multi-achievement
+aLink('https://github.com/HabitRPG/habitrpg/issues/2232', 'Returning soon') .achievement-ultimate-mage.multi-achievement
=env.t('mage')
td(ng-if='::profile.achievements.ultimateGearSets.rogue').multi-achievement
.achievement-ultimate-rogue.multi-achievement
=env.t('rogue')
td(ng-if='::profile.achievements.ultimateGearSets.warrior').multi-achievement
.achievement-ultimate-warrior.multi-achievement
=env.t('warrior')
hr hr
div(ng-if='::profile.achievements.beastMaster || user._id == profile._id') div(ng-if='::profile.achievements.beastMaster || user._id == profile._id')

View File

@@ -126,7 +126,7 @@ script(id='templates/habitrpg-tasks.html', type="text/ng-template")
// Static Rewards // Static Rewards
ul.items.rewards(ng-if='main && list.type=="reward"') ul.items.rewards(ng-if='main && list.type=="reward"')
li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.notes()}}') li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.key == "armoire" && !user.flags.armoireEmpty ? env.t("armoireNotesFull") + armoireCount(user.items.gear.owned) : item.notes()}}')
// right-hand side control buttons // right-hand side control buttons
.task-meta-controls .task-meta-controls
span.task-notes span.task-notes