feat(Armoire): WIP
@@ -37,6 +37,12 @@
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.multi-achievement {
|
||||
margin: auto;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
|
||||
[class*="Mount_Head_"], [class*="Mount_Body_"]{
|
||||
margin-top:18px; /* Sprite accommodates 105x123 box */
|
||||
}
|
||||
|
||||
2
common/dist/sprites/habitrpg-shared.css
vendored
1352
common/dist/sprites/spritesmith0.css
vendored
BIN
common/dist/sprites/spritesmith0.png
vendored
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 287 KiB |
5416
common/dist/sprites/spritesmith1.css
vendored
BIN
common/dist/sprites/spritesmith1.png
vendored
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
5358
common/dist/sprites/spritesmith2.css
vendored
BIN
common/dist/sprites/spritesmith2.png
vendored
|
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 153 KiB |
1196
common/dist/sprites/spritesmith3.css
vendored
BIN
common/dist/sprites/spritesmith3.png
vendored
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 150 KiB |
896
common/dist/sprites/spritesmith4.css
vendored
BIN
common/dist/sprites/spritesmith4.png
vendored
|
Before Width: | Height: | Size: 673 KiB After Width: | Height: | Size: 678 KiB |
884
common/dist/sprites/spritesmith5.css
vendored
BIN
common/dist/sprites/spritesmith5.png
vendored
|
Before Width: | Height: | Size: 353 KiB After Width: | Height: | Size: 357 KiB |
1292
common/dist/sprites/spritesmith6.css
vendored
BIN
common/dist/sprites/spritesmith6.png
vendored
|
Before Width: | Height: | Size: 296 KiB After Width: | Height: | Size: 300 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
BIN
common/img/sprites/spritesmith/misc/shop_armoire.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
common/img/sprites/spritesmith/promo/promo_enchanted_armoire.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
@@ -52,9 +52,11 @@
|
||||
"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.",
|
||||
"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",
|
||||
"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",
|
||||
"levelUp": "Level Up!",
|
||||
"mana": "Mana",
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
"potionText": "Health Potion",
|
||||
"potionNotes": "Recover 15 Health (Instant Use)",
|
||||
|
||||
"armoireText": "Enchanted Armoire",
|
||||
"armoireNotesFull": "Open the Armoire to randomly receive special Equipment, Experience, or food!",
|
||||
"armoireNotesEmpty": "The Enchanted Armoire will have more Equipment soon. Keep clicking for Experience and Food!",
|
||||
|
||||
"dropEggWolfText": "Wolf",
|
||||
"dropEggWolfAdjective": "loyal",
|
||||
|
||||
|
||||
@@ -138,6 +138,11 @@
|
||||
"weaponMystery301404Text": "Steampunk Cane",
|
||||
"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",
|
||||
|
||||
"armorBase0Text": "Plain Clothing",
|
||||
@@ -282,6 +287,11 @@
|
||||
"armorMystery301404Text": "Steampunk Suit",
|
||||
"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",
|
||||
|
||||
"headBase0Text": "No Helm",
|
||||
@@ -422,6 +432,17 @@
|
||||
"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.",
|
||||
|
||||
"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",
|
||||
|
||||
"shieldBase0Text": "No Shield-Hand Equipment",
|
||||
@@ -500,6 +521,9 @@
|
||||
"shieldMystery301405Text": "Clock Shield",
|
||||
"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",
|
||||
"backBase0Notes": "No Back Accessory.",
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"close": "Close",
|
||||
"saveAndClose": "Save & Close",
|
||||
"cancel": "Cancel",
|
||||
"ok": "Ok",
|
||||
"ok": "OK",
|
||||
"add": "Add",
|
||||
"undo": "Undo",
|
||||
"continue": "Continue",
|
||||
|
||||
@@ -20,5 +20,8 @@
|
||||
"messageDropFood": "You've found <%= dropArticle %><%= dropText %>! <%= dropNotes %>",
|
||||
"messageDropEgg": "You've found a <%= dropText %> Egg! <%= 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": "You found a piece of rare Equipment in the Armoire: <%= dropText %>! Awesome!",
|
||||
"armoireFood": "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!"
|
||||
}
|
||||
|
||||
@@ -140,6 +140,9 @@ gear =
|
||||
201502: text: t('weaponMystery201502Text'), notes: t('weaponMystery201502Notes'), mystery:'201502', value: 0
|
||||
201505: text: t('weaponMystery201505Text'), notes: t('weaponMystery201505Notes'), mystery:'201505', 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
|
||||
lunarSceptre: text: t('weaponArmoireLunarSceptreText'), notes: t('weaponArmoireLunarSceptreNotes', {con: 7, int: 7}), value: 100, con: 7, int: 7, set: 'soothing'
|
||||
|
||||
armor:
|
||||
base:
|
||||
@@ -224,6 +227,9 @@ gear =
|
||||
201503: text: t('armorMystery201503Text'), notes: t('armorMystery201503Notes'), mystery:'201503', value: 0
|
||||
201504: text: t('armorMystery201504Text'), notes: t('armorMystery201504Notes'), mystery:'201504', 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'
|
||||
gladiatorArmor: text: t('armorArmoireGladiatorArmorText'), notes: t('armorArmoireGladiatorArmorNotes', {str: 7, per: 7}), value: 100, str: 7, per: 7, set: 'gladiator'
|
||||
|
||||
head:
|
||||
base:
|
||||
@@ -306,6 +312,12 @@ gear =
|
||||
201505: text: t('headMystery201505Text'), notes: t('headMystery201505Notes'), mystery:'201505', value: 0
|
||||
301404: text: t('headMystery301404Text'), notes: t('headMystery301404Notes'), mystery:'301404', 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'
|
||||
redHairbow: text: t('headArmoireRedHairbowText'), notes: t('headArmoireRedHairbowNotes', {str: 5, int: 5, con: 5}), value: 100, str: 5, int: 5, con: 5
|
||||
violetFloppyHat: text: t('headArmoireVioletFloppyHatText'), notes: t('headArmoireVioletFloppyHatNotes', {per: 5, int: 5, con: 5}), value: 100, per: 5, int: 5, con: 5
|
||||
gladiatorHelm: text: t('headArmoireGladiatorHelmText'), notes: t('headArmoireGladiatorHelmNotes', {per: 7, int: 7}), value: 100, per: 7, int: 7, set: 'gladiator'
|
||||
rancherHat: text: t('headArmoireRancherHatText'), notes: t('headArmoireRancherHatNotes', {str: 5, per: 5, int: 5}), value: 100, str: 5, per: 5, int: 5
|
||||
|
||||
shield:
|
||||
base:
|
||||
@@ -365,6 +377,8 @@ gear =
|
||||
spring2015Healer: event: events.spring2015, specialClass: 'healer', text: t('shieldSpecialSpring2015HealerText'), notes: t('shieldSpecialSpring2015HealerNotes', {con: 9}), value: 70, con: 9
|
||||
mystery:
|
||||
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'
|
||||
|
||||
back:
|
||||
base:
|
||||
@@ -442,7 +456,7 @@ api.gear =
|
||||
flat: {}
|
||||
|
||||
_.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
|
||||
_.each gear[type][klass], (item, 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.armoire = type: 'armoire', text: t('armoireText'), notes: t('armoireNotesFull'), value: 100, key: 'armoire', canOwn: ((u)-> _.contains(u.achievements.ultimateGearSets, true))
|
||||
|
||||
###
|
||||
---------------------------------------------------------------
|
||||
|
||||
@@ -166,6 +166,7 @@ api.updateStore = (user) ->
|
||||
changes = changes.concat _.filter content.gear.flat, (v) ->
|
||||
v.klass in ['special','mystery'] and !user.items.gear.owned[v.key] and v.canOwn?(user)
|
||||
changes.push content.potion
|
||||
if user.flags.armoireEnabled then changes.push content.armoire
|
||||
# Return sorted store (array)
|
||||
_.sortBy changes, (c)->sortOrder[c.type]
|
||||
|
||||
@@ -835,23 +836,40 @@ api.wrap = (user, main=true) ->
|
||||
buy: (req, cb) ->
|
||||
{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: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'
|
||||
user.stats.hp += 15
|
||||
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 < .7 or !user.flags.armoireOpened)
|
||||
drop = user.fns.randomVal(eligibleEquipment)
|
||||
user.items.gear.owned[drop.key] = true
|
||||
user.flags.armoireOpened = true
|
||||
message = i18n.t('armoireEquipment', {dropText: drop.text(req.language)}, req.language)
|
||||
else if (!_.isEmpty(eligibleEquipment) and armoireResult < .85) or armoireResult < .6
|
||||
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', {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
|
||||
user.items.gear.equipped[item.type] = item.key
|
||||
user.items.gear.owned[item.key] = true
|
||||
message = user.fns.handleTwoHanded(item, null, req)
|
||||
message ?= i18n.t('messageBought', {itemText: item.text(req.language)}, req.language)
|
||||
if not user.achievements.ultimateGear and item.last
|
||||
user.fns.ultimateGear()
|
||||
if item.last then user.fns.ultimateGear()
|
||||
user.stats.gp -= 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)->
|
||||
return cb?({code:401, message:"You don't have enough Mystic Hourglasses"}) unless user.purchased.plan.consecutive.trinkets>0
|
||||
@@ -1660,32 +1678,19 @@ api.wrap = (user, main=true) ->
|
||||
# ----------------------------------------------------------------------
|
||||
# Achievements
|
||||
# ----------------------------------------------------------------------
|
||||
ultimateGear: () ->
|
||||
# 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()
|
||||
ownedLastGear = _.chain(content.gear.flat)
|
||||
.pick(_.keys gear)
|
||||
.values()
|
||||
.filter (gear) -> gear.last
|
||||
|
||||
lastGearClassTypeMatrix = {}
|
||||
_.each content.classes, (klass) ->
|
||||
lastGearClassTypeMatrix[klass] = {}
|
||||
#_.each content.gearTypes, (type) ->
|
||||
_.each ['armor', 'weapon', 'shield', 'head'], (type) ->
|
||||
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
|
||||
ultimateGear: ->
|
||||
# on the server this is a Lodash transform, on the client its an object
|
||||
owned = if window? then user.items.gear.owned else user.items.gear.owned.toObject()
|
||||
user.achievements.ultimateGearSets ?= {healer: false, wizard: false, rogue: false, warrior: false}
|
||||
content.classes.forEach (klass) ->
|
||||
user.achievements.ultimateGearSets[klass] = _.reduce ['armor', 'shield', 'head', 'weapon'], (soFarGood, type) ->
|
||||
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)
|
||||
, true # start with true, else `and` will fail right away
|
||||
user.markModified? 'achievements.ultimateGearSets'
|
||||
if _.contains(user.achievements.ultimateGearSets, true) and user.flags.armoireEnabled != true
|
||||
user.flags.armoireEnabled = true
|
||||
user.markModified? 'flags'
|
||||
|
||||
nullify: ->
|
||||
user.ops = null
|
||||
|
||||
@@ -33,7 +33,8 @@ newUser = (addTasks=true)->
|
||||
todos: []
|
||||
rewards: []
|
||||
flags: {}
|
||||
achievements: {}
|
||||
achievements:
|
||||
ultimateGearSets: {}
|
||||
contributor:
|
||||
level: 2
|
||||
|
||||
@@ -432,7 +433,6 @@ describe 'User', ->
|
||||
expect(spell.lvl).to.be.above(0)
|
||||
expect(spell.cast).to.be.a('function')
|
||||
|
||||
|
||||
describe 'drop system', ->
|
||||
user = null
|
||||
|
||||
@@ -481,6 +481,81 @@ describe 'User', ->
|
||||
user.fns.randomVal.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 '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(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 [.8,.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 [.6,.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(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 [.6,.5])
|
||||
user.items.gear.owned = fullArmoire
|
||||
user.ops.buy({params: {key: 'armoire'}})
|
||||
expect(user.items.gear.owned).to.eql fullArmoire
|
||||
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', ->
|
||||
_.each shared.content.quests, (quest)->
|
||||
@@ -510,11 +585,11 @@ describe 'User', ->
|
||||
_.each [1..5], (i) ->
|
||||
user.ops.buy {params:'#{type}_#{klass}_#{i}'}
|
||||
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) ->
|
||||
user.ops.buy {params:'#{type}_#{klass}_6'}
|
||||
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', ->
|
||||
user = newUser()
|
||||
|
||||
@@ -101,10 +101,10 @@ habitrpg.controller('NotificationCtrl',
|
||||
}
|
||||
});
|
||||
|
||||
$rootScope.$watch('user.achievements.ultimateGear', function(after, before){
|
||||
if (after === before || after !== true) return;
|
||||
$rootScope.$watch('user.achievements.ultimateGearSets', function(after, before){
|
||||
if (_.isEqual(after,before) || !_.contains(User.user.achievements.ultimateGearSets, true)) return;
|
||||
$rootScope.openModal('achievements/ultimateGear');
|
||||
});
|
||||
}, true);
|
||||
|
||||
$rootScope.$watch('user.achievements.rebirths', function(after, before){
|
||||
if(after === before) return;
|
||||
|
||||
@@ -34,7 +34,8 @@ var UserSchema = new Schema({
|
||||
originalUser: Boolean,
|
||||
helpedHabit: Boolean, //TODO: Deprecate this. Superseded by habitSurveys
|
||||
habitSurveys: Number,
|
||||
ultimateGear: Boolean,
|
||||
ultimateGear: Boolean, //TODO: Deprecate this. Superseded by ultimateGearSets
|
||||
ultimateGearSets: Schema.Types.Mixed,
|
||||
beastMaster: Boolean,
|
||||
beastMasterCount: Number,
|
||||
mountMaster: Boolean,
|
||||
@@ -156,7 +157,9 @@ var UserSchema = new Schema({
|
||||
lastWeeklyRecap: {type: Date, 'default': Date.now},
|
||||
communityGuidelinesAccepted: {type: Boolean, 'default': false},
|
||||
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}
|
||||
},
|
||||
history: {
|
||||
exp: Array, // [{date: Date, value: Number}], // big peformance issues if these are defined
|
||||
|
||||
@@ -6,7 +6,7 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html')
|
||||
div
|
||||
button.btn.btn-default(type="button", ng-click='dequip("battleGear");') {{env.t("unequipBattleGear")}}
|
||||
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]')
|
||||
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
|
||||
|
||||
@@ -14,7 +14,27 @@ script(id='modals/achievements/ultimateGear.html', type='text/ng-template')
|
||||
h4=env.t('modalAchievement')
|
||||
.modal-body
|
||||
.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
|
||||
button.btn.btn-default(ng-click='$close()')=env.t('ok')
|
||||
|
||||
|
||||
@@ -78,18 +78,25 @@ div(ng-if='::profile.achievements.perfect || user._id == profile._id')
|
||||
small(ng-if='::profile.achievements.perfect == 1')=env.t('perfectSingularText')
|
||||
hr
|
||||
|
||||
//-div(ng-if='::profile.achievements.ultimateGear || user._id == profile._id')
|
||||
.achievement.achievement-armor(ng-if='::profile.achievements.ultimateGear')
|
||||
div(ng-class='::{muted: !profile.achievements.ultimateGear}')
|
||||
div(ng-if='::profile.achievements.ultimateGearSets || user._id == profile._id')
|
||||
.achievement.achievement-armor(ng-if='::profile.achievements.ultimateGearSets')
|
||||
div(ng-class='::{muted: !profile.achievements.ultimateGearSets}')
|
||||
h5=env.t('ultimGearName')
|
||||
small=env.t('ultimGearText')
|
||||
hr
|
||||
// Remove the following when ultimate gear is fixed (https://github.com/HabitRPG/habitrpg/issues/2232):
|
||||
div(ng-if='::user._id == profile._id')
|
||||
div.muted
|
||||
h5=env.t('ultimGearName')
|
||||
small
|
||||
+aLink('https://github.com/HabitRPG/habitrpg/issues/2232', 'Returning soon')
|
||||
table.multi-achievement
|
||||
tr
|
||||
td(ng-if='::profile.achievements.ultimateGearSets.healer').multi-achievement
|
||||
.achievement-ultimate-healer.multi-achievement
|
||||
=env.t('healer')
|
||||
td(ng-if='::profile.achievements.ultimateGearSets.wizard').multi-achievement
|
||||
.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
|
||||
|
||||
div(ng-if='::profile.achievements.beastMaster || user._id == profile._id')
|
||||
|
||||