Merge branch 'develop' into carolstone-chron-run-early-issue-4488

This commit is contained in:
Blade Barringer
2015-09-05 09:32:24 -05:00
979 changed files with 31917 additions and 22575 deletions
+329 -56
View File
@@ -51,11 +51,28 @@ api.mystery =
201504: {start:'2015-04-24',end:'2015-05-02', text:'Busy Bee Set'}
201505: {start:'2015-05-25',end:'2015-06-02', text:'Green Knight Set'}
201506: {start:'2015-06-25',end:'2015-07-02', text:'Neon Snorkeler Set'}
201507: {start:'2015-07-24',end:'2015-08-02', text:'Rad Surfer Set'}
201508: {start:'2015-08-23',end:'2015-09-02', text:'Cheetah Costume Set'}
301404: {start:'3014-03-24',end:'3014-04-02', text:'Steampunk Standard Set'}
301405: {start:'3014-04-24',end:'3014-05-02', text:'Steampunk Accessories Set'}
wondercon: {start:'2014-03-24',end:'2014-04-01'} # not really, but the mechanic works
_.each api.mystery, (v,k)->v.key = k
api.itemList =
'weapon': {localeKey: 'weapon', isEquipment: true}
'armor' : {localeKey: 'armor', isEquipment: true}
'head' : {localeKey: 'headgear', isEquipment: true}
'shield' : {localeKey: 'offhand', isEquipment: true}
'back' : {localeKey: 'back', isEquipment: true}
'body' : {localeKey: 'body', isEquipment: true}
'headAccessory' : {localeKey: 'headAccessory', isEquipment: true}
'eyewear' : {localeKey: 'eyewear', isEquipment: true}
'hatchingPotions' : {localeKey: 'hatchingPotion', isEquipment: false}
'eggs' : {localeKey: 'eggSingular', isEquipment: false}
'quests' : {localeKey: 'quest', isEquipment: false}
'food' : {localeKey: 'foodText', isEquipment: false}
'Saddle' : {localeKey: 'foodSaddleText', isEquipment: false}
gear =
weapon:
base:
@@ -70,14 +87,6 @@ gear =
5: text: t('weaponWarrior5Text'), notes: t('weaponWarrior5Notes', {str: 15}), str: 15, value:90
6: text: t('weaponWarrior6Text'), notes: t('weaponWarrior6Notes', {str: 18}), str: 18, value:120, last: true
rogue:
#Not using bows at the moment, but they would be easy to add back in to an advanced Armory feature, as Quest drops, etc.
#0: twoHanded: true, text: "Practice Bow", notes:'Training weapon. Confers no benefit.', value:0
#1: twoHanded: true, text: "Short Bow", notes:'Simple bow best at close ranges. Increases STR by 2.', str: 2, value:20
#2: twoHanded: true, text: "Long Bow", notes:'Bow with a strong draw for extra distance. Increases STR by 5.', str: 5, value:50
#3: twoHanded: true, text: "Recurve Bow", notes:'Built with advanced techniques. Increases STR by 8.', str: 8, value:80
#4: twoHanded: true, text: "Icicle Bow", notes:'Fires arrows of piercing cold. Increases STR by 12.', str: 12, value:120
#5: twoHanded: true, text: "Meteor Bow", notes:'Rains flame upon your foes. Increases STR by 16.', str: 16, value:160
#6: twoHanded: true, text: "Golden Bow", notes:'As swift as sunlight and as sharp as lightning. Increases STR by 20.', str: 20, value:200, last: true
0: text: t('weaponRogue0Text'), notes: t('weaponRogue0Notes'), str: 0, value: 0
1: text: t('weaponRogue1Text'), notes: t('weaponRogue1Notes', {str: 2}), str: 2, value: 20
2: text: t('weaponRogue2Text'), notes: t('weaponRogue2Notes', {str: 3}), str: 3, value: 35
@@ -102,27 +111,29 @@ gear =
5: text: t('weaponHealer5Text'), notes: t('weaponHealer5Notes', {int: 9}), int: 9, value:90
6: text: t('weaponHealer6Text'), notes: t('weaponHealer6Notes', {int: 11}), int: 11, value:120, last: true
special:
# Backer, Contributor, and Quest Rewards
0: text: t('weaponSpecial0Text'), notes: t('weaponSpecial0Notes', {str: 20}), str: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 70)
1: text: t('weaponSpecial1Text'), notes: t('weaponSpecial1Notes', {attrs: 6}), str: 6, per: 6, con: 6, int: 6, value:170, canOwn: ((u)-> +u.contributor?.level >= 4)
2: text: t('weaponSpecial2Text'), notes: t('weaponSpecial2Notes', {attrs: 25}), str: 25, per: 25, value:200, canOwn: ((u)-> (+u.backer?.tier >= 300) or u.items.gear.owned.weapon_special_2?)
3: text: t('weaponSpecial3Text'), notes: t('weaponSpecial3Notes', {attrs: 17}), str: 17, int: 17, con: 17, value:200, canOwn: ((u)-> (+u.backer?.tier >= 300) or u.items.gear.owned.weapon_special_3?)
critical: text: t('weaponSpecialCriticalText'), notes: t('weaponSpecialCriticalNotes', {attrs: 40}), str: 40, per: 40, value:200, canOwn: ((u)-> !!u.contributor?.critical)
# Winter event gear
tridentOfCrashingTides: text: t('weaponSpecialTridentOfCrashingTidesText'), notes: t('weaponSpecialTridentOfCrashingTidesNotes', {int: 15}), int: 15, value: 130, canOwn: ((u)-> u.items.gear.owned.weapon_special_tridentOfCrashingTides?)
# Winter Wonderland
yeti: event: events.winter, specialClass: 'warrior', text: t('weaponSpecialYetiText'), notes: t('weaponSpecialYetiNotes', {str: 15}), str: 15, value:90
ski: event: events.winter, specialClass: 'rogue', text: t('weaponSpecialSkiText'), notes: t('weaponSpecialSkiNotes', {str: 8}), str: 8, value: 90
candycane: event: events.winter, specialClass: 'wizard', twoHanded: true, text: t('weaponSpecialCandycaneText'), notes: t('weaponSpecialCandycaneNotes', {int: 15, per: 7}), int: 15, per: 7, value:160
snowflake: event: events.winter, specialClass: 'healer', text: t('weaponSpecialSnowflakeText'), notes: t('weaponSpecialSnowflakeNotes', {int: 9}), int: 9, value:90
#Spring Fling
# Spring Fling
springRogue: event: events.spring, specialClass: 'rogue', text: t('weaponSpecialSpringRogueText'), notes: t('weaponSpecialSpringRogueNotes', {str: 8}), value: 80, str: 8
springWarrior: event: events.spring, specialClass: 'warrior', text: t('weaponSpecialSpringWarriorText'), notes: t('weaponSpecialSpringWarriorNotes', {str: 15}), value: 90, str: 15
springMage: event: events.spring, specialClass: 'wizard', twoHanded:true, text: t('weaponSpecialSpringMageText'), notes: t('weaponSpecialSpringMageNotes', {int: 15, per: 7}), value: 160, int:15, per:7
springHealer: event: events.spring, specialClass: 'healer', text: t('weaponSpecialSpringHealerText'), notes: t('weaponSpecialSpringHealerNotes', {int: 9}), value: 90, int: 9
#Summer
# Summer Splash
summerRogue: event: events.summer, specialClass: 'rogue', text: t('weaponSpecialSummerRogueText'), notes: t('weaponSpecialSummerRogueNotes', {str: 8}), value: 80, str: 8
summerWarrior: event: events.summer, specialClass: 'warrior', text: t('weaponSpecialSummerWarriorText'), notes: t('weaponSpecialSummerWarriorNotes', {str: 15}), value: 90, str: 15
summerMage: event: events.summer, specialClass: 'wizard', twoHanded:true, text: t('weaponSpecialSummerMageText'), notes: t('weaponSpecialSummerMageNotes', {int: 15, per: 7}), value: 160, int:15, per:7
summerHealer: event: events.summer, specialClass: 'healer', text: t('weaponSpecialSummerHealerText'), notes: t('weaponSpecialSummerHealerNotes', {int: 9}), value: 90, int: 9
#Fall
# Fall Festival
fallRogue: event: events.fall, specialClass: 'rogue', text: t('weaponSpecialFallRogueText'), notes: t('weaponSpecialFallRogueNotes', {str: 8}), value: 80, str: 8
fallWarrior: event: events.fall, specialClass: 'warrior', text: t('weaponSpecialFallWarriorText'), notes: t('weaponSpecialFallWarriorNotes', {str: 15}), value: 90, str: 15
fallMage: event: events.fall, specialClass: 'wizard', twoHanded:true, text: t('weaponSpecialFallMageText'), notes: t('weaponSpecialFallMageNotes', {int: 15, per: 7}), value: 160, int:15, per:7
@@ -150,43 +161,45 @@ gear =
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?)
rancherLasso: twoHanded:true, text: t('weaponArmoireRancherLassoText'), notes: t('weaponArmoireRancherLassoNotes', {str: 5, per: 5, int: 5}), value: 100, str: 5, per: 5, int: 5, set: 'rancher', canOwn: ((u)-> u.items.gear.owned.weapon_armoire_rancherLasso?)
mythmakerSword: text: t('weaponArmoireMythmakerSwordText'), notes: t('weaponArmoireMythmakerSwordNotes', {attrs: 6}), value: 100, str: 6, per: 6, set: 'goldenToga', canOwn: ((u)-> u.items.gear.owned.weapon_armoire_mythmakerSword?)
ironCrook: text: t('weaponArmoireIronCrookText'), notes: t('weaponArmoireIronCrookNotes', {attrs: 7}), value: 100, str: 7, per: 7, set: 'hornedIron', canOwn: ((u)-> u.items.gear.owned.weapon_armoire_ironCrook?)
goldWingStaff: text: t('weaponArmoireGoldWingStaffText'), notes: t('weaponArmoireGoldWingStaffNotes', {attrs:4}), value: 100, con: 4, int: 4, per: 4, str: 4, canOwn: ((u)-> u.items.gear.owned.weapon_armoire_goldWingStaff?)
armor:
base:
0: text: t('armorBase0Text'), notes: t('armorBase0Notes'), value:0
warrior:
#0: text: "Plain Clothing", notes:'Ordinary clothing. Confers no benefit.', value:0
1: text: t('armorWarrior1Text'), notes: t('armorWarrior1Notes', {con: 3}), con: 3, value:30
2: text: t('armorWarrior2Text'), notes: t('armorWarrior2Notes', {con: 5}), con: 5, value:45
3: text: t('armorWarrior3Text'), notes: t('armorWarrior3Notes', {con: 7}), con: 7, value:65
4: text: t('armorWarrior4Text'), notes: t('armorWarrior4Notes', {con: 9}), con: 9, value:90
5: text: t('armorWarrior5Text'), notes: t('armorWarrior5Notes', {con: 11}), con: 11, value:120, last: true
rogue:
#0: text: "Plain Clothing", notes:'Ordinary clothing. Confers no benefit.', value:0
1: text: t('armorRogue1Text'), notes: t('armorRogue1Notes', {per: 6}), per: 6, value:30
2: text: t('armorRogue2Text'), notes: t('armorRogue2Notes', {per: 9}), per: 9, value:45
3: text: t('armorRogue3Text'), notes: t('armorRogue3Notes', {per: 12}), per: 12, value:65
4: text: t('armorRogue4Text'), notes: t('armorRogue4Notes', {per: 15}), per: 15, value:90
5: text: t('armorRogue5Text'), notes: t('armorRogue5Notes', {per: 18}), per: 18, value:120, last: true
wizard:
#0: text: "Apprentice Garb", notes:'For students of magic. Confers no benefit.', value:0
1: text: t('armorWizard1Text'), notes: t('armorWizard1Notes', {int: 2}), int: 2, value:30
2: text: t('armorWizard2Text'), notes: t('armorWizard2Notes', {int: 4}), int: 4, value:45
3: text: t('armorWizard3Text'), notes: t('armorWizard3Notes', {int: 6}), int: 6, value:65
4: text: t('armorWizard4Text'), notes: t('armorWizard4Notes', {int: 9}), int: 9, value:90
5: text: t('armorWizard5Text'), notes: t('armorWizard5Notes', {int: 12}), int: 12, value:120, last: true
healer:
#0: text: "Novice Robe", notes:'For healers in training. Confers no benefit.', value:0
1: text: t('armorHealer1Text'), notes: t('armorHealer1Notes', {con: 6}), con: 6, value:30
2: text: t('armorHealer2Text'), notes: t('armorHealer2Notes', {con: 9}), con: 9, value:45
3: text: t('armorHealer3Text'), notes: t('armorHealer3Notes', {con: 12}), con: 12, value:65
4: text: t('armorHealer4Text'), notes: t('armorHealer4Notes', {con: 15}), con: 15, value:90
5: text: t('armorHealer5Text'), notes: t('armorHealer5Notes', {con: 18}), con: 18, value:120, last: true
special:
# Backer, Contributor, and Quest Rewards
0: text: t('armorSpecial0Text'), notes: t('armorSpecial0Notes', {con: 20}), con: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 45)
1: text: t('armorSpecial1Text'), notes: t('armorSpecial1Notes', {attrs: 6}), con: 6, str: 6, per: 6, int: 6, value:170, canOwn: ((u)-> +u.contributor?.level >= 2)
2: text: t('armorSpecial2Text'), notes: t('armorSpecial2Notes', {attrs: 25}), int: 25, con: 25, value:200, canOwn: ((u)-> +u.backer?.tier >= 300 or u.items.gear.owned.armor_special_2?)
#Winter event
finnedOceanicArmor: text: t('armorSpecialFinnedOceanicArmorText'), notes: t('armorSpecialFinnedOceanicArmorNotes', {str: 15}), str: 15, value: 130, canOwn: ((u)-> u.items.gear.owned.armor_special_finnedOceanicArmor?)
# Winter Wonderland
yeti: event: events.winter, specialClass: 'warrior', text: t('armorSpecialYetiText'), notes: t('armorSpecialYetiNotes', {con: 9}), con: 9, value:90
ski: event: events.winter, specialClass: 'rogue', text: t('armorSpecialSkiText'), notes: t('armorSpecialSkiNotes', {per: 15}), per: 15, value:90
candycane: event: events.winter, specialClass: 'wizard', text: t('armorSpecialCandycaneText'), notes: t('armorSpecialCandycaneNotes', {int: 9}), int: 9, value:90
@@ -197,12 +210,12 @@ gear =
springWarrior: event: events.spring, specialClass: 'warrior', text: t('armorSpecialSpringWarriorText'), notes: t('armorSpecialSpringWarriorNotes', {con: 9}), value: 90, con: 9
springMage: event: events.spring, specialClass: 'wizard', text: t('armorSpecialSpringMageText'), notes: t('armorSpecialSpringMageNotes', {int: 9}), value: 90, int: 9
springHealer: event: events.spring, specialClass: 'healer', text: t('armorSpecialSpringHealerText'), notes: t('armorSpecialSpringHealerNotes', {con: 15}), value: 90, con: 15
# Summer
# Summer Splash
summerRogue: event: events.summer, specialClass: 'rogue', text: t('armorSpecialSummerRogueText'), notes: t('armorSpecialSummerRogueNotes', {per: 15}), value: 90, per: 15
summerWarrior: event: events.summer, specialClass: 'warrior', text: t('armorSpecialSummerWarriorText'), notes: t('armorSpecialSummerWarriorNotes', {con: 9}), value: 90, con: 9
summerMage: event: events.summer, specialClass: 'wizard', text: t('armorSpecialSummerMageText'), notes: t('armorSpecialSummerMageNotes', {int: 9}), value: 90, int: 9
summerHealer: event: events.summer, specialClass: 'healer', text: t('armorSpecialSummerHealerText'), notes: t('armorSpecialSummerHealerNotes', {con: 15}), value: 90, con: 15
# Fall
# Fall Festival
fallRogue: event: events.fall, specialClass: 'rogue', text: t('armorSpecialFallRogueText'), notes: t('armorSpecialFallRogueNotes', {per: 15}), value: 90, per: 15
fallWarrior: event: events.fall, specialClass: 'warrior', text: t('armorSpecialFallWarriorText'), notes: t('armorSpecialFallWarriorNotes', {con: 9}), value: 90, con: 9
fallMage: event: events.fall, specialClass: 'wizard', text: t('armorSpecialFallMageText'), notes: t('armorSpecialFallMageNotes', {int: 9}), value: 90, int: 9
@@ -239,47 +252,50 @@ gear =
201503: text: t('armorMystery201503Text'), notes: t('armorMystery201503Notes'), mystery:'201503', value: 0
201504: text: t('armorMystery201504Text'), notes: t('armorMystery201504Notes'), mystery:'201504', value: 0
201506: text: t('armorMystery201506Text'), notes: t('armorMystery201506Notes'), mystery:'201506', value: 0
201508: text: t('armorMystery201508Text'), notes: t('armorMystery201508Notes'), mystery:'201508', value: 0, int: 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?)
rancherRobes: text: t('armorArmoireRancherRobesText'), notes: t('armorArmoireRancherRobesNotes', {str: 5, per: 5, int: 5}), value: 100, str: 5, per: 5, int: 5, set: 'rancher', canOwn: ((u)-> u.items.gear.owned.armor_armoire_rancherRobes?)
goldenToga: text: t('armorArmoireGoldenTogaText'), notes: t('armorArmoireGoldenTogaNotes', {attrs: 8}), value: 100, str: 8, con: 8, set: 'goldenToga', canOwn: ((u)-> u.items.gear.owned.armor_armoire_goldenToga?)
hornedIronArmor: text: t('armorArmoireHornedIronArmorText'), notes: t('armorArmoireHornedIronArmorNotes', {con: 9, per: 7}), value: 100, con: 9, per: 7, set: 'hornedIron', canOwn: ((u)-> u.items.gear.owned.armor_armoire_hornedIronArmor?)
plagueDoctorOvercoat: text: t('armorArmoirePlagueDoctorOvercoatText'), notes: t('armorArmoirePlagueDoctorOvercoatNotes', {int: 6, str: 5, con: 6}), int: 6, str: 5, con: 6, set: 'plagueDoctor', canOwn: ((u)-> u.items.gear.owned.armor_armoire_plagueDoctorOvercoat?)
head:
base:
0: text: t('headBase0Text'), notes: t('headBase0Notes'), value:0
warrior:
#0: text: "No Helm", notes:'No headgear.', value:0
1: text: t('headWarrior1Text'), notes: t('headWarrior1Notes', {str: 2}), str: 2, value:15
2: text: t('headWarrior2Text'), notes: t('headWarrior2Notes', {str: 4}), str: 4, value:25
3: text: t('headWarrior3Text'), notes: t('headWarrior3Notes', {str: 6}), str: 6, value:40
4: text: t('headWarrior4Text'), notes: t('headWarrior4Notes', {str: 9}), str: 9, value:60
5: text: t('headWarrior5Text'), notes: t('headWarrior5Notes', {str: 12}), str: 12, value:80, last: true
rogue:
#0: text: "No Hood", notes:'No headgear.', value:0
1: text: t('headRogue1Text'), notes: t('headRogue1Notes', {per: 2}), per: 2, value:15
2: text: t('headRogue2Text'), notes: t('headRogue2Notes', {per: 4}), per: 4, value:25
3: text: t('headRogue3Text'), notes: t('headRogue3Notes', {per: 6}), per: 6, value:40
4: text: t('headRogue4Text'), notes: t('headRogue4Notes', {per: 9}), per: 9, value:60
5: text: t('headRogue5Text'), notes: t('headRogue5Notes', {per: 12}), per: 12, value:80, last: true
wizard:
#0: text: "No Hat", notes:'No headgear.', value:0
1: text: t('headWizard1Text'), notes: t('headWizard1Notes', {per: 2}), per: 2, value:15
2: text: t('headWizard2Text'), notes: t('headWizard2Notes', {per: 3}), per: 3, value:25
3: text: t('headWizard3Text'), notes: t('headWizard3Notes', {per: 5}), per: 5, value:40
4: text: t('headWizard4Text'), notes: t('headWizard4Notes', {per: 7}), per: 7, value:60
5: text: t('headWizard5Text'), notes: t('headWizard5Notes', {per: 10}), per: 10, value:80, last: true
healer:
#0: text: "No Circlet", notes:'No headgear.', value:0
1: text: t('headHealer1Text'), notes: t('headHealer1Notes', {int: 2}), int: 2, value:15
2: text: t('headHealer2Text'), notes: t('headHealer2Notes', {int: 3}), int: 3, value:25
3: text: t('headHealer3Text'), notes: t('headHealer3Notes', {int: 5}), int: 5, value:40
4: text: t('headHealer4Text'), notes: t('headHealer4Notes', {int: 7}), int: 7, value:60
5: text: t('headHealer5Text'), notes: t('headHealer5Notes', {int: 9}), int: 9, value:80, last: true
special:
# Backer, Contributor, and Quest Rewards
0: text: t('headSpecial0Text'), notes: t('headSpecial0Notes', {int: 20}), int: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 45)
1: text: t('headSpecial1Text'), notes: t('headSpecial1Notes', {attrs: 6}), con: 6, str: 6, per: 6, int: 6, value:170, canOwn: ((u)-> +u.contributor?.level >= 3)
2: text: t('headSpecial2Text'), notes: t('headSpecial2Notes', {attrs: 25}), int: 25, str: 25, value:200, canOwn: ((u)-> (+u.backer?.tier >= 300) or u.items.gear.owned.head_special_2?)
#Winter event
fireCoralCirclet: text: t('headSpecialFireCoralCircletText'), notes: t('headSpecialFireCoralCircletNotes', {per: 15}), per: 15, value: 130, canOwn: ((u)-> u.items.gear.owned.head_special_fireCoralCirclet?)
# Winter Wonderland
nye: event: events.winter, text: t('headSpecialNyeText'), notes: t('headSpecialNyeNotes'), value: 0
yeti: event: events.winter, specialClass: 'warrior', text: t('headSpecialYetiText'), notes: t('headSpecialYetiNotes', {str: 9}), str: 9, value:60
ski: event: events.winter, specialClass: 'rogue', text: t('headSpecialSkiText'), notes: t('headSpecialSkiNotes', {per: 9}), per: 9, value:60
@@ -290,12 +306,12 @@ gear =
springWarrior: event: events.spring, specialClass: 'warrior', text: t('headSpecialSpringWarriorText'), notes: t('headSpecialSpringWarriorNotes', {str: 9}),value: 60,str: 9
springMage: event: events.spring, specialClass: 'wizard', text: t('headSpecialSpringMageText'), notes: t('headSpecialSpringMageNotes', {per: 7}),value: 60,per: 7
springHealer: event: events.spring, specialClass: 'healer', text: t('headSpecialSpringHealerText'), notes: t('headSpecialSpringHealerNotes', {int: 7}), value: 60, int: 7
# Summer
# Summer Splash
summerRogue: event: events.summer, specialClass: 'rogue', text: t('headSpecialSummerRogueText'), notes: t('headSpecialSummerRogueNotes', {per: 9}),value: 60,per: 9
summerWarrior: event: events.summer, specialClass: 'warrior', text: t('headSpecialSummerWarriorText'), notes: t('headSpecialSummerWarriorNotes', {str: 9}),value: 60,str: 9
summerMage: event: events.summer, specialClass: 'wizard', text: t('headSpecialSummerMageText'), notes: t('headSpecialSummerMageNotes', {per: 7}),value: 60,per: 7
summerHealer: event: events.summer, specialClass: 'healer', text: t('headSpecialSummerHealerText'), notes: t('headSpecialSummerHealerNotes', {int: 7}), value: 60, int: 7
# Fall
# Fall Festival
fallRogue: event: events.fall, specialClass: 'rogue', text: t('headSpecialFallRogueText'), notes: t('headSpecialFallRogueNotes', {per: 9}),value: 60,per: 9
fallWarrior: event: events.fall, specialClass: 'warrior', text: t('headSpecialFallWarriorText'), notes: t('headSpecialFallWarriorNotes', {str: 9}),value: 60,str: 9
fallMage: event: events.fall, specialClass: 'wizard', text: t('headSpecialFallMageText'), notes: t('headSpecialFallMageNotes', {per: 7}),value: 60,per: 7
@@ -328,6 +344,7 @@ gear =
201412: text: t('headMystery201412Text'), notes: t('headMystery201412Notes'), mystery:'201412', value: 0
201501: text: t('headMystery201501Text'), notes: t('headMystery201501Notes'), mystery:'201501', value: 0
201505: text: t('headMystery201505Text'), notes: t('headMystery201505Notes'), mystery:'201505', value: 0
201508: text: t('headMystery201508Text'), notes: t('headMystery201508Notes'), mystery:'201508', value: 0, int: 0
301404: text: t('headMystery301404Text'), notes: t('headMystery301404Notes'), mystery:'301404', value: 0
301405: text: t('headMystery301405Text'), notes: t('headMystery301405Notes'), mystery:'301405', value: 0
armoire:
@@ -335,7 +352,14 @@ gear =
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?)
rancherHat: text: t('headArmoireRancherHatText'), notes: t('headArmoireRancherHatNotes', {str: 5, per: 5, int: 5}), value: 100, str: 5, per: 5, int: 5, set: 'rancher', canOwn: ((u)-> u.items.gear.owned.head_armoire_rancherHat?)
royalCrown: text: t('headArmoireRoyalCrownText'), notes: t('headArmoireRoyalCrownNotes', {str: 10}), value: 100, str: 10, canOwn: ((u)-> u.items.gear.owned.head_armoire_royalCrown?)
blueHairbow: text: t('headArmoireBlueHairbowText'), notes: t('headArmoireBlueHairbowNotes', {per: 5, int: 5, con: 5}), value: 100, per: 5, int: 5, con: 5, canOwn: ((u)-> u.items.gear.owned.head_armoire_blueHairbow?)
goldenLaurels: text: t('headArmoireGoldenLaurelsText'), notes: t('headArmoireGoldenLaurelsNotes', {attrs: 8}), value: 100, per: 8, con: 8, set: 'goldenToga', canOwn: ((u)-> u.items.gear.owned.head_armoire_goldenLaurels?)
hornedIronHelm: text: t('headArmoireHornedIronHelmText'), notes: t('headArmoireHornedIronHelmNotes', {con: 9, str: 7}), value: 100, con: 9, str:7, set: 'hornedIron', canOwn: ((u)-> u.items.gear.owned.head_armoire_hornedIronHelm?)
yellowHairbow: text: t('headArmoireYellowHairbowText'), notes: t('headArmoireYellowHairbowNotes', {attrs: 5}), int: 5, per: 5, str: 5, canOwn: ((u)-> u.items.gear.owned.head_armoire_yellowHairbow?)
redFloppyHat: text: t('headArmoireRedFloppyHatText'), notes: t('headArmoireRedFloppyHatNotes', {attrs: 6}), con: 6, int: 6, per: 6, canOwn: ((u)-> u.items.gear.owned.head_armoire_redFloppyHat?)
plagueDoctorHat: text: t('headArmoirePlagueDoctorHatText'), notes: t('headArmoirePlagueDoctorHatNotes', {int: 5, str: 6, con: 5}), int: 5, str: 6, con: 5, set: 'plagueDoctor', canOwn: ((u)-> u.items.gear.owned.head_armoire_plagueDoctorHat?)
shield:
base:
@@ -366,22 +390,24 @@ gear =
4: text: t('shieldHealer4Text'), notes: t('shieldHealer4Notes', {con: 9}), con: 9, value:70
5: text: t('shieldHealer5Text'), notes: t('shieldHealer5Notes', {con: 12}), con: 12, value:90, last: true
special:
# Backer, Contributor, and Quest Rewards
0: text: t('shieldSpecial0Text'), notes: t('shieldSpecial0Notes', {per: 20}), per: 20, value:150, canOwn: ((u)-> +u.backer?.tier >= 45)
1: text: t('shieldSpecial1Text'), notes: t('shieldSpecial1Notes', {attrs: 6}), con: 6, str: 6, per: 6, int:6, value:170, canOwn: ((u)-> +u.contributor?.level >= 5)
goldenknight: text: t('shieldSpecialGoldenknightText'), notes: t('shieldSpecialGoldenknightNotes', {attrs: 25}), con: 25, per: 25, value:200, canOwn: ((u)-> u.items.gear.owned.shield_special_goldenknight?)
#Winter event
moonpearlShield: text: t('shieldSpecialMoonpearlShieldText'), notes: t('shieldSpecialMoonpearlShieldNotes', {con: 15}), con: 15, value: 130, canOwn: ((u)-> u.items.gear.owned.shield_special_moonpearlShield?)
# Winter Wonderland
yeti: event: events.winter, specialClass: 'warrior', text: t('shieldSpecialYetiText'), notes: t('shieldSpecialYetiNotes', {con: 7}), con: 7, value: 70
ski: event: events.winter, specialClass: 'rogue', text: t('weaponSpecialSkiText'), notes: t('weaponSpecialSkiNotes', {str: 8}), str: 8, value: 90
snowflake: event: events.winter, specialClass: 'healer', text: t('shieldSpecialSnowflakeText'), notes: t('shieldSpecialSnowflakeNotes', {con: 9}), con: 9, value: 70
#Spring Fling
# Spring Fling
springRogue: event: events.spring, specialClass: 'rogue', text: t('shieldSpecialSpringRogueText'), notes: t('shieldSpecialSpringRogueNotes', {str: 8}), value: 80, str: 8
springWarrior: event: events.spring, specialClass: 'warrior', text: t('shieldSpecialSpringWarriorText'), notes: t('shieldSpecialSpringWarriorNotes', {con: 7}), value: 70, con: 7
springHealer: event: events.spring, specialClass: 'healer', text: t('shieldSpecialSpringHealerText'), notes: t('shieldSpecialSpringHealerNotes', {con: 9}), value: 70, con: 9
#Summer
# Summer Splash
summerRogue: event: events.summer, specialClass: 'rogue', text: t('shieldSpecialSummerRogueText'), notes: t('shieldSpecialSummerRogueNotes', {str: 8}), value: 80, str: 8
summerWarrior: event: events.summer, specialClass: 'warrior', text: t('shieldSpecialSummerWarriorText'), notes: t('shieldSpecialSummerWarriorNotes', {con: 7}), value: 70, con: 7
summerHealer: event: events.summer, specialClass: 'healer', text: t('shieldSpecialSummerHealerText'), notes: t('shieldSpecialSummerHealerNotes', {con: 9}), value: 70, con: 9
#Fall
# Fall Festival
fallRogue: event: events.fall, specialClass: 'rogue', text: t('shieldSpecialFallRogueText'), notes: t('shieldSpecialFallRogueNotes', {str: 8}), value: 80, str: 8
fallWarrior: event: events.fall, specialClass: 'warrior', text: t('shieldSpecialFallWarriorText'), notes: t('shieldSpecialFallWarriorNotes', {con: 7}), value: 70, con: 7
fallHealer: event: events.fall, specialClass: 'healer', text: t('shieldSpecialFallHealerText'), notes: t('shieldSpecialFallHealerNotes', {con: 9}), value: 70, con: 9
@@ -410,6 +436,7 @@ gear =
201404: text: t('backMystery201404Text'), notes: t('backMystery201404Notes'), mystery:'201404', value: 0
201410: text: t('backMystery201410Text'), notes: t('backMystery201410Notes'), mystery:'201410', value: 0
201504: text: t('backMystery201504Text'), notes: t('backMystery201504Notes'), mystery:'201504', value: 0
201507: text: t('backMystery201507Text'), notes: t('backMystery201507Notes'), mystery:'201507', value: 0
special:
wondercon_red: text: t('backSpecialWonderconRedText'), notes: t('backSpecialWonderconRedNotes'), value: 0, mystery:'wondercon'
wondercon_black: text: t('backSpecialWonderconBlackText'), notes: t('backSpecialWonderconBlackNotes'), value: 0, mystery:'wondercon'
@@ -472,12 +499,15 @@ gear =
mystery:
201503: text: t('eyewearMystery201503Text'), notes: t('eyewearMystery201503Notes'), mystery:'201503', value: 0
201506: text: t('eyewearMystery201506Text'), notes: t('eyewearMystery201506Notes'), mystery:'201506', value: 0
201507: text: t('eyewearMystery201507Text'), notes: t('eyewearMystery201507Notes'), mystery:'201507', value: 0
301404: text: t('eyewearMystery301404Text'), notes: t('eyewearMystery301404Notes'), mystery:'301404', value: 0
301405: text: t('eyewearMystery301405Text'), notes: t('eyewearMystery301405Notes'), mystery:'301405', value: 0
armoire:
plagueDoctorMask: text: t('eyewearArmoirePlagueDoctorMaskText'), notes: t('eyewearArmoirePlagueDoctorMaskNotes'), set: 'plagueDoctor', canOwn: ((u)-> u.items.gear.owned.eyewear_armoire_plagueDoctorMask?)
###
The gear is exported as a tree (defined above), and a flat list (eg, {weapon_healer_1: .., shield_special_0: ...}) since
they are needed in different froms at different points in the app
they are needed in different forms at different points in the app
###
api.gear =
tree: gear
@@ -521,8 +551,23 @@ api.timeTravelerStore = (owned) ->
---------------------------------------------------------------
###
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))
api.potion =
type: 'potion',
text: t('potionText'),
notes: t('potionNotes'),
value: 25,
key: 'potion'
api.armoire =
type: 'armoire',
text: t('armoireText'),
notes: ((user, count)->
return t('armoireNotesEmpty')() if (user.flags.armoireEmpty)
return t('armoireNotesFull')() + count
),
value: 100,
key: 'armoire',
canOwn: ((u)-> _.contains(u.achievements.ultimateGearSets, true))
###
---------------------------------------------------------------
@@ -900,6 +945,7 @@ api.spells =
if !target.items.special.nyeReceived
target.items.special.nyeReceived = []
target.items.special.nyeReceived.push user.profile.name
target.flags.cardReceived = true
target.markModified? 'items.special.nyeReceived'
user.stats.gp -= 10
@@ -923,10 +969,75 @@ api.spells =
if !target.items.special.valentineReceived
target.items.special.valentineReceived = []
target.items.special.valentineReceived.push user.profile.name
target.flags.cardReceived = true
target.markModified? 'items.special.valentineReceived'
user.stats.gp -= 10
greeting:
text: t('greetingCard')
mana: 0
value: 10
immediateUse: true
silent: true
target: 'user'
notes: t('greetingCardNotes')
cast: (user, target) ->
if user == target
user.achievements.greeting ?= 0
user.achievements.greeting++
else
_.each [user,target], (t)->
t.achievements.greeting ?= 0
t.achievements.greeting++
if !target.items.special.greetingReceived
target.items.special.greetingReceived = []
target.items.special.greetingReceived.push user.profile.name
target.flags.cardReceived = true
target.markModified? 'items.special.greetingReceived'
user.stats.gp -= 10
thankyou:
text: t('thankyouCard')
mana: 0
value: 10
immediateUse: true
silent: true
target: 'user'
notes: t('thankyouCardNotes')
cast: (user, target) ->
if user == target
user.achievements.thankyou ?= 0
user.achievements.thankyou++
else
_.each [user,target], (t)->
t.achievements.thankyou ?= 0
t.achievements.thankyou++
if !target.items.special.thankyouReceived
target.items.special.thankyouReceived = []
target.items.special.thankyouReceived.push user.profile.name
target.flags.cardReceived = true
target.markModified? 'items.special.thankyouReceived'
user.stats.gp -= 10
api.cardTypes =
greeting:
key: 'greeting'
messageOptions: 4
yearRound: true
nye:
key: 'nye'
messageOptions: 5
thankyou:
key: 'thankyou'
messageOptions: 4
yearRound: true
valentine:
key: 'valentine'
messageOptions: 4
# Intercept all spells to reduce user.stats.mp after casting the spell
_.each api.spells, (spellClass) ->
_.each spellClass, (spell, key) ->
@@ -956,6 +1067,7 @@ api.dropEggs =
Dragon: text: t('dropEggDragonText'), adjective: t('dropEggDragonAdjective')
Cactus: text: t('dropEggCactusText'), adjective: t('dropEggCactusAdjective')
BearCub: text: t('dropEggBearCubText'), mountText: t('dropEggBearCubMountText'), adjective: t('dropEggBearCubAdjective')
_.each api.dropEggs, (egg,key) ->
_.defaults egg,
canBuy:true
@@ -984,6 +1096,8 @@ api.questEggs =
Slime: text: t('questEggSlimeText'), adjective: t('questEggSlimeAdjective'), canBuy: false
Sheep: text: t('questEggSheepText'), adjective: t('questEggSheepAdjective'), canBuy: false
Cuttlefish: text: t('questEggCuttlefishText'), adjective: t('questEggCuttlefishAdjective'), canBuy: false
Whale: text: t('questEggWhaleText'), adjective: t('questEggWhaleAdjective'), canBuy: false
Cheetah: text: t('questEggCheetahText'), adjective: t('questEggCheetahAdjective'), canBuy: false
_.each api.questEggs, (egg,key) ->
_.defaults egg,
@@ -1005,13 +1119,16 @@ api.specialPets =
'MantisShrimp-Base': 'mantisShrimp'
'JackOLantern-Base': 'jackolantern'
'Mammoth-Base': 'mammoth'
'Tiger-Veteran': 'veteranTiger'
api.specialMounts =
'BearCub-Polar': 'polarBear'
'LionCub-Ethereal': 'etherealLion'
'MantisShrimp-Base': 'mantisShrimp'
'Turkey-Base': 'turkey'
'Mammoth-Base': 'mammoth'
'BearCub-Polar': 'polarBear'
'LionCub-Ethereal': 'etherealLion'
'MantisShrimp-Base': 'mantisShrimp'
'Turkey-Base': 'turkey'
'Mammoth-Base': 'mammoth'
'Orca-Base': 'orca'
'Gryphon-RoyalPurple': 'royalPurpleGryphon'
api.hatchingPotions =
Base: value: 2, text: t('hatchingPotionBase')
@@ -1094,6 +1211,7 @@ api.quests =
completion: t("questDilatoryCompletion")
value: 0
canBuy: false
category: 'world'
boss:
name: t("questDilatoryBoss")
# We ran an average of progress{up,down} on users over 5 days: {up:805025,down:1324423}. /5*30 (we want the
@@ -1136,6 +1254,7 @@ api.quests =
completionChat: t("questStressbeastCompletionChat")
value: 0
canBuy: false
category: 'world'
boss:
name: t("questStressbeastBoss")
hp: 2750000
@@ -1179,7 +1298,7 @@ api.quests =
notes: t('questEvilSantaNotes')
completion: t('questEvilSantaCompletion')
value: 4 # Gem cost to buy, GP sell-back
#mechanic: enum['perfectDailies', ...]
category: 'pet'
boss:
name: t('questEvilSantaBoss') # name of the boss himself (eg, Vice)
hp: 300
@@ -1198,6 +1317,7 @@ api.quests =
completion: t('questEvilSanta2Completion')
value: 4
previous: 'evilsanta'
category: 'pet'
collect:
tracks: text: t('questEvilSanta2CollectTracks'), count: 20
branches: text: t('questEvilSanta2CollectBranches'), count: 10
@@ -1213,6 +1333,7 @@ api.quests =
notes: t('questGryphonNotes')
completion: t('questGryphonCompletion')
value: 4 # Gem cost to buy, GP sell-back
category: 'pet'
boss:
name: t('questGryphonBoss') # name of the boss himself (eg, Vice)
hp: 300
@@ -1232,6 +1353,7 @@ api.quests =
notes: t('questHedgehogNotes')
completion: t('questHedgehogCompletion')
value: 4 # Gem cost to buy, GP sell-back
category: 'pet'
boss:
name: t('questHedgehogBoss') # name of the boss himself (eg, Vice)
hp: 400
@@ -1251,6 +1373,7 @@ api.quests =
notes: t('questGhostStagNotes')
completion: t('questGhostStagCompletion')
value: 4
category: 'pet'
boss:
name: t('questGhostStagBoss')
hp: 1200
@@ -1270,6 +1393,7 @@ api.quests =
notes: t('questVice1Notes')
value: 4
lvl: 30
category: 'unlockable'
boss:
name: t('questVice1Boss')
hp: 750
@@ -1285,7 +1409,8 @@ api.quests =
text: t('questVice2Text')
notes: t('questVice2Notes')
value: 4
lvl: 35
lvl: 30
category: 'unlockable'
previous: 'vice1'
collect:
lightCrystal: text: t('questVice2CollectLightCrystal'), count: 45
@@ -1302,7 +1427,8 @@ api.quests =
completion: t('questVice3Completion')
previous: 'vice2'
value: 4
lvl: 40
lvl: 30
category: 'unlockable'
boss:
name: t('questVice3Boss')
hp: 1500
@@ -1324,6 +1450,7 @@ api.quests =
completion: t('questEggHuntCompletion')
value: 1
canBuy: false
category: 'pet'
collect:
plainEgg: text: t('questEggHuntCollectPlainEgg'), count: 100
drop:
@@ -1347,6 +1474,7 @@ api.quests =
notes: t('questRatNotes')
completion: t('questRatCompletion')
value: 4
category: 'pet'
boss:
name: t('questRatBoss')
hp: 1200
@@ -1366,6 +1494,7 @@ api.quests =
notes: t('questOctopusNotes')
completion: t('questOctopusCompletion')
value: 4
category: 'pet'
boss:
name: t('questOctopusBoss')
hp: 1200
@@ -1385,6 +1514,7 @@ api.quests =
notes: t('questSeahorseNotes')
completion: t('questSeahorseCompletion')
value: 4
category: 'pet'
boss:
name: t('questSeahorseBoss')
hp: 300
@@ -1404,6 +1534,7 @@ api.quests =
notes: t('questAtom1Notes')
value: 4
lvl: 15
category: 'unlockable'
collect:
soapBars: text: t('questAtom1CollectSoapBars'), count: 20
drop:
@@ -1418,6 +1549,7 @@ api.quests =
previous: 'atom1'
value: 4
lvl: 15
category: 'unlockable'
boss:
name: t('questAtom2Boss')
hp: 300
@@ -1435,6 +1567,7 @@ api.quests =
completion: t('questAtom3Completion')
value: 4
lvl: 15
category: 'unlockable'
boss:
name: t('questAtom3Boss')
hp: 800
@@ -1453,6 +1586,7 @@ api.quests =
notes: t('questHarpyNotes')
completion: t('questHarpyCompletion')
value: 4
category: 'pet'
boss:
name: t('questHarpyBoss')
hp: 600
@@ -1472,6 +1606,7 @@ api.quests =
notes: t('questRoosterNotes')
completion: t('questRoosterCompletion')
value: 4
category: 'pet'
boss:
name: t('questRoosterBoss')
hp: 300
@@ -1491,6 +1626,7 @@ api.quests =
notes: t('questSpiderNotes')
completion: t('questSpiderCompletion')
value: 4
category: 'pet'
boss:
name: t('questSpiderBoss')
hp: 400
@@ -1510,6 +1646,7 @@ api.quests =
notes: t('questMoonstone1Notes')
value: 4
lvl: 60
category: 'unlockable'
collect:
moonstone: text: t('questMoonstone1CollectMoonstone'), count: 500
drop:
@@ -1522,8 +1659,9 @@ api.quests =
text: t('questMoonstone2Text')
notes: t('questMoonstone2Notes')
value: 4
lvl: 65
lvl: 60
previous: 'moonstone1'
category: 'unlockable'
boss:
name: t('questMoonstone2Boss')
hp: 1500
@@ -1540,7 +1678,8 @@ api.quests =
completion: t('questMoonstone3Completion')
previous: 'moonstone2'
value: 4
lvl: 70
lvl: 60
category: 'unlockable'
boss:
name: t('questMoonstone3Boss')
hp: 2000
@@ -1565,6 +1704,7 @@ api.quests =
notes: t('questGoldenknight1Notes')
value: 4
lvl: 40
category: 'unlockable'
collect:
testimony: text: t('questGoldenknight1CollectTestimony'), count: 300
drop:
@@ -1578,7 +1718,8 @@ api.quests =
notes: t('questGoldenknight2Notes')
value: 4
previous: 'goldenknight1'
lvl: 45
lvl: 40
category: 'unlockable'
boss:
name: t('questGoldenknight2Boss')
hp: 1000
@@ -1595,7 +1736,8 @@ api.quests =
completion: t('questGoldenknight3Completion')
previous: 'goldenknight2'
value: 4
lvl: 50
lvl: 40
category: 'unlockable'
boss:
name: t('questGoldenknight3Boss')
hp: 1700
@@ -1616,8 +1758,11 @@ api.quests =
text: t('questBasilistText')
notes: t('questBasilistNotes')
completion: t('questBasilistCompletion')
canBuy: false
value: 4
category: 'unlockable'
unlockCondition:
condition: 'party invite'
text: t('inviteFriends')
boss:
name: t('questBasilistBoss')
hp: 100
@@ -1631,6 +1776,7 @@ api.quests =
notes: t('questOwlNotes')
completion: t('questOwlCompletion')
value: 4
category: 'pet'
boss:
name: t('questOwlBoss')
hp: 500
@@ -1650,6 +1796,7 @@ api.quests =
notes: t('questPenguinNotes')
completion: t('questPenguinCompletion')
value: 4
category: 'pet'
boss:
name: t('questPenguinBoss')
hp: 400
@@ -1669,6 +1816,7 @@ api.quests =
notes: t('questTRexNotes')
completion: t('questTRexCompletion')
value: 4
category: 'pet'
boss:
name: t('questTRexBoss')
hp: 800
@@ -1688,6 +1836,7 @@ api.quests =
notes: t('questTRexUndeadNotes')
completion: t('questTRexUndeadCompletion')
value: 4
category: 'pet'
boss:
name: t('questTRexUndeadBoss')
hp: 500
@@ -1713,6 +1862,7 @@ api.quests =
notes: t('questRockNotes')
completion: t('questRockCompletion')
value: 4
category: 'pet'
boss:
name: t('questRockBoss')
hp: 400
@@ -1732,6 +1882,7 @@ api.quests =
notes: t('questBunnyNotes')
completion: t('questBunnyCompletion')
value: 4
category: 'pet'
boss:
name: t('questBunnyBoss')
hp: 300
@@ -1751,6 +1902,7 @@ api.quests =
notes: t('questSlimeNotes')
completion: t('questSlimeCompletion')
value: 4
category: 'pet'
boss:
name: t('questSlimeBoss')
hp: 400
@@ -1770,6 +1922,7 @@ api.quests =
notes: t('questSheepNotes')
completion: t('questSheepCompletion')
value: 4
category: 'pet'
boss:
name: t('questSheepBoss')
hp: 300
@@ -1789,6 +1942,7 @@ api.quests =
notes: t('questKrakenNotes')
completion: t('questKrakenCompletion')
value: 4
category: 'pet'
boss:
name: t('questKrakenBoss')
hp: 800
@@ -1803,6 +1957,110 @@ api.quests =
exp: 500
unlock: t('questKrakenUnlockText')
whale:
text: t('questWhaleText')
notes: t('questWhaleNotes')
completion: t('questWhaleCompletion')
value: 4
category: 'pet'
boss:
name: t('questWhaleBoss')
hp: 500
str: 1.5
drop:
items: [
{type: 'eggs', key: 'Whale', text: t('questWhaleDropWhaleEgg')}
{type: 'eggs', key: 'Whale', text: t('questWhaleDropWhaleEgg')}
{type: 'eggs', key: 'Whale', text: t('questWhaleDropWhaleEgg')}
]
gp: 37
exp: 275
unlock: t('questWhaleUnlockText')
dilatoryDistress1:
text: t('questDilatoryDistress1Text')
notes: t('questDilatoryDistress1Notes')
completion: t('questDilatoryDistress1Completion')
value: 4
goldValue: 200
category: 'gold'
collect:
fireCoral: text: t('questDilatoryDistress1CollectFireCoral'), count: 25
blueFins: text: t('questDilatoryDistress1CollectBlueFins'), count: 25
drop:
items: [
{type: 'gear', key: "armor_special_finnedOceanicArmor", text: t('questDilatoryDistress1DropArmor')}
]
gp: 0
exp: 75
dilatoryDistress2:
text: t('questDilatoryDistress2Text')
notes: t('questDilatoryDistress2Notes')
completion: t('questDilatoryDistress2Completion')
previous: 'dilatoryDistress1'
value: 4
goldValue: 300
category: 'gold'
boss:
name: t('questDilatoryDistress2Boss')
hp: 500
rage:
title: t("questDilatoryDistress2RageTitle")
description: t("questDilatoryDistress2RageDescription")
value: 50
healing: .3
effect:t('questDilatoryDistress2RageEffect')
drop:
items: [
{type: 'hatchingPotions', key: 'Skeleton', text: t('questDilatoryDistress2DropSkeletonPotion')}
{type: 'hatchingPotions', key: 'CottonCandyBlue', text: t('questDilatoryDistress2DropCottonCandyBluePotion')}
{type: 'gear', key: "head_special_fireCoralCirclet", text: t('questDilatoryDistress2DropHeadgear')}
]
gp: 0
exp: 500
dilatoryDistress3:
text: t('questDilatoryDistress3Text')
notes: t('questDilatoryDistress3Notes')
completion: t('questDilatoryDistress3Completion')
previous: 'dilatoryDistress2'
value: 4
goldValue: 400
category: 'gold'
boss:
name: t('questDilatoryDistress3Boss')
hp: 1000
str: 2
drop:
items: [
{type: 'food', key: 'Fish', text: t('questDilatoryDistress3DropFish')}
{type: 'food', key: 'Fish', text: t('questDilatoryDistress3DropFish')}
{type: 'food', key: 'Fish', text: t('questDilatoryDistress3DropFish')}
{type: 'gear', key: "weapon_special_tridentOfCrashingTides", text: t('questDilatoryDistress3DropWeapon')}
{type: 'gear', key: "shield_special_moonpearlShield", text: t('questDilatoryDistress3DropShield')}
]
gp: 0
exp: 650
cheetah:
text: t('questCheetahText')
notes: t('questCheetahNotes')
completion: t('questCheetahCompletion')
value: 4
category: 'pet'
boss:
name: t('questCheetahBoss')
hp: 600
str: 1.5
drop:
items: [
{type: 'eggs', key: 'Cheetah', text: t('questCheetahDropCheetahEgg')}
{type: 'eggs', key: 'Cheetah', text: t('questCheetahDropCheetahEgg')}
{type: 'eggs', key: 'Cheetah', text: t('questCheetahDropCheetahEgg')}
]
gp: 43
exp: 350
unlock: t('questCheetahUnlockText')
_.each api.quests, (v,key) ->
_.defaults v, {key,canBuy:true}
b = v.boss
@@ -1811,6 +2069,10 @@ _.each api.quests, (v,key) ->
if b.rage
_.defaults b.rage, {title:t('bossRageTitle'),description:t('bossRageDescription')}
api.questsByLevel =
_.sortBy api.quests, (quest) ->
quest.lvl || 0
api.backgrounds =
backgrounds062014:
beach:
@@ -1952,6 +2214,26 @@ api.backgrounds =
sunken_ship:
text: t('backgroundSunkenShipText')
notes: t('backgroundSunkenShipNotes')
backgrounds082015:
pyramids:
text: t('backgroundPyramidsText')
notes: t('backgroundPyramidsNotes')
sunset_savannah:
text: t('backgroundSunsetSavannahText')
notes: t('backgroundSunsetSavannahNotes')
twinkly_party_lights:
text: t('backgroundTwinklyPartyLightsText')
notes: t('backgroundTwinklyPartyLightsNotes')
backgrounds092015:
market:
text: t('backgroundMarketText')
notes: t('backgroundMarketNotes')
stable:
text: t('backgroundStableText')
notes: t('backgroundStableNotes')
tavern:
text: t('backgroundTavernText')
notes: t('backgroundTavernNotes')
api.subscriptionBlocks =
basic_earned: months:1, price:5
@@ -1970,23 +2252,14 @@ api.userDefaults =
]
dailys: [
# {type: 'daily', text: t('defaultDaily1Text'), notes: t('defaultDaily1Notes'), value: 0, completed: false, repeat: repeat, attribute: 'per' }
# {type: 'daily', text: t('defaultDaily2Text'), notes: t('defaultDaily2Notes'), value: 3, completed: false, repeat: repeat, attribute: 'con' }
# {type: 'daily', text: t('defaultDaily3Text'), notes: t('defaultDaily3Notes'), value: -10, completed: false, repeat: repeat, attribute: 'int' }
# {type: 'daily', text: t('defaultDaily4Text'), notes: t('defaultDaily4Notes'), checklist: [{completed: true, text: t('defaultDaily4Checklist1') }, {completed: false, text: t('defaultDaily4Checklist2')}, {completed: false, text: t('defaultDaily4Checklist3')}], completed: false, repeat: repeat, attribute: 'str' }
]
todos: [
{type: 'todo', text: t('defaultTodo1Text'), notes: t('defaultTodoNotes'), completed: false, attribute: 'int' }
# {type: 'todo', text: t('defaultTodo2Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo2Checklist1') }, {completed: false, text: t('defaultTodo2Checklist2')}, {completed: false, text: t('defaultTodo2Checklist3')}], completed: false, attribute: 'per' }
# {type: 'todo', text: t('defaultTodo3Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo3Checklist1') }, {completed: false, text: t('defaultTodo3Checklist2')}, {completed: false, text: t('defaultTodo3Checklist3')}], completed: false, attribute: 'per' }
# {type: 'todo', text: t('defaultTodo4Text'), notes: t('defaultTodoNotes'), checklist: [{completed: false, text: t('defaultTodo4Checklist1') }, {completed: false, text: t('defaultTodo4Checklist2')}, {completed: false, text: t('defaultTodo4Checklist3')}], completed: false, attribute: 'per' }
# {type: 'todo', text: t('defaultTodo5Text'), notes: t('defaultTodoNotes'), completed: false, attribute: 'per' }
]
rewards: [
{type: 'reward', text: t('defaultReward1Text'), value: 10 }
# {type: 'reward', text: t('defaultReward2Text'), notes: t('defaultReward2Notes'), value: 10 }
]
tags: [
+59
View File
@@ -0,0 +1,59 @@
'use strict';
require('coffee-script');
var _ = require('lodash');
var content = require('./content.coffee');
var DROP_ANIMALS = _.keys(content.pets);
function beastMasterProgress(pets) {
var count = 0;
_(DROP_ANIMALS).each(function(animal) {
if(pets[animal] > 0 || pets[animal] == -1)
count++
});
return count;
}
function dropPetsCurrentlyOwned(pets) {
var count = 0;
_(DROP_ANIMALS).each(function(animal) {
if(pets[animal] > 0)
count++
});
return count;
}
function mountMasterProgress(mounts) {
var count = 0;
_(DROP_ANIMALS).each(function(animal) {
if (mounts[animal])
count++
});
return count;
}
function remainingGearInSet(userGear, set) {
var gear = _.filter(content.gear.flat, function(item) {
var setMatches = item.klass === set;
var hasItem = userGear[item.key];
return setMatches && !hasItem;
});
var count = _.size(gear);
return count;
}
module.exports = {
beastMasterProgress: beastMasterProgress,
dropPetsCurrentlyOwned: dropPetsCurrentlyOwned,
mountMasterProgress: mountMasterProgress,
remainingGearInSet: remainingGearInSet
};
+272 -139
View File
@@ -90,13 +90,12 @@ api.shouldDo = (day, dailyTask, options = {}) ->
o = sanitizeOptions options
startOfDayWithCDSTime = api.startOfDay(_.defaults {now:day}, o) # a moment()
# If the Daily does not have a Start Date (old tasks predating the Start Date feature), we assume it starts on the day being checked. This does not cause a bug with the Every X Days option because if the user had edited the task to select X days, then a start date would have been saved to the task at the same time.
taskStartDate = dailyTask.startDate || day
taskStartDate = moment(dailyTask.startDate).zone(o.timezoneOffset)
# The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it.
# Therefore, we must also ignore the time portion of the user's day start (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times.
# NB: The user's day start date has already been converted to the PREVIOUS day's date if the time portion was before CDS.
taskStartDate = moment(taskStartDate).startOf('day');
taskStartDate = moment(taskStartDate).startOf('day')
if taskStartDate > startOfDayWithCDSTime.startOf('day')
return false # Daily starts in the future
@@ -128,6 +127,14 @@ api.maxLevel = 100
api.capByLevel = (lvl) ->
if lvl > api.maxLevel then api.maxLevel else lvl
###
------------------------------------------------------
Health cap
------------------------------------------------------
###
api.maxHealth = 50
###
------------------------------------------------------
Scoring
@@ -190,7 +197,6 @@ preenHistory = (history) ->
Update the in-browser store with new gear. FIXME this was in user.fns, but it was causing strange issues there
###
sortOrder = _.reduce content.gearTypes,((m,v,k)->m[v]=k;m), {}
#sortOrder.potion = _.size(sortOrder) #potion goes last #actually, _.sortBy puts anything else last, so this is unecessary
api.updateStore = (user) ->
return unless user
changes= []
@@ -202,8 +208,6 @@ api.updateStore = (user) ->
# Add special items (contrib gear, backer gear, etc)
changes = changes.concat _.filter content.gear.flat, (v) ->
v.klass in ['special','mystery','armoire'] 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]
@@ -390,32 +394,10 @@ api.appliedTags = (userTags, taskTags) ->
arr.push(t.name) if taskTags?[t.id]
arr.join(', ')
api.countPets = (originalCount, pets) ->
count = if originalCount? then originalCount else _.size(pets)
for pet of content.questPets
count-- if pets[pet]
for pet of content.specialPets
count-- if pets[pet]
count
api.countMounts = (originalCount, mounts) ->
count2 = if originalCount? then originalCount else _.size(mounts)
for mount of content.questPets
count2-- if mounts[mount]
for mount of content.specialMounts
count2-- if mounts[mount]
count2
api.countTriad = (pets) ->
count3 = 0
for egg of content.dropEggs
for potion of content.hatchingPotions
if pets[egg + "-" + potion] > 0 then count3++
count3
api.countArmoire = (gear) ->
count = _.size(_.filter(content.gear.flat, ((i)->i.klass is 'armoire' and !gear[i.key])))
count
###
Various counting functions
###
api.count = require('./count')
###
------------------------------------------------------
@@ -476,7 +458,7 @@ api.wrap = (user, main=true) ->
user.preferences.sleep = !user.preferences.sleep
cb? null, {}
revive: (req, cb) ->
revive: (req, cb, analytics) ->
return cb?({code:400, message: "Cannot revive if not dead"}) unless user.stats.hp <= 0
# Reset stats after death
@@ -505,7 +487,15 @@ api.wrap = (user, main=true) ->
user.items.gear.equipped[item.type] = "#{item.type}_base_0" if user.items.gear.equipped[item.type] is lostItem
user.items.gear.costume[item.type] = "#{item.type}_base_0" if user.items.gear.costume[item.type] is lostItem
user.markModified? 'items.gear'
mixpanel?.track('Death',{'lostItem':lostItem})
analyticsData = {
uuid: user._id,
lostItem: lostItem,
gaLabel: lostItem,
category: 'behavior'
}
analytics?.track('Death', analyticsData)
cb? (if item then {code:200,message: i18n.t('messageLostItem', {itemText: item.text(req.language)}, req.language)} else null), user
reset: (req, cb) ->
@@ -524,14 +514,14 @@ api.wrap = (user, main=true) ->
gear[type].weapon = 'weapon_base_0'
gear[type].head = 'head_base_0'
gear[type].shield = 'shield_base_0'
gear.owned = {} if typeof gear.owned == 'undefined';
gear.owned = {} if typeof gear.owned == 'undefined'
_.each gear.owned, (v, k)-> gear.owned[k]=false if gear.owned[k];true
gear.owned.weapon_warrior_0 = true
user.markModified? 'items.gear.owned'
user.preferences.costume = false
cb? null, user
reroll: (req, cb, ga) ->
reroll: (req, cb, analytics) ->
if user.balance < 1
return cb? {code:401,message: i18n.t('notEnoughGems', req.language)}
user.balance--
@@ -539,19 +529,37 @@ api.wrap = (user, main=true) ->
unless task.type is 'reward'
task.value = 0
user.stats.hp = 50
cb? null, user
mixpanel?.track("Acquire Item",{'itemName':'Fortify','acquireMethod':'Gems','gemCost':4})
ga?.event('behavior', 'gems', 'reroll').send()
rebirth: (req, cb, ga) ->
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
}
analytics?.track('Fortify Potion', analyticsData)
cb? null, user
rebirth: (req, cb, analytics) ->
# Cost is 8 Gems ($2)
if (user.balance < 2 && user.stats.lvl < api.maxLevel)
return cb? {code:401,message: i18n.t('notEnoughGems', req.language)}
analyticsData = {
uuid: user._id,
category: 'behavior'
}
# only charge people if they are under the max level - ryan
if user.stats.lvl < api.maxLevel
user.balance -= 2
mixpanel?.track("Acquire Item",{'itemName':'Rebirth','acquireMethod':'Gems','gemCost':8})
ga?.event('behavior', 'gems', 'rebirth').send()
analyticsData.acquireMethod = 'Gems'
analyticsData.gemCost = 8
else
analyticsData.gemCost = 0
analyticsData.acquireMethod = '> 100'
analytics?.track('Rebirth', analyticsData)
# Save off user's level, for calculating achievement eligibility later
lvl = api.capByLevel(user.stats.lvl)
# Turn tasks yellow, zero out streaks
@@ -578,8 +586,8 @@ api.wrap = (user, main=true) ->
gear[type].shield = 'shield_base_0'
if user.items.currentPet then user.ops.equip({params:{type: 'pet', key: user.items.currentPet}})
if user.items.currentMount then user.ops.equip({params:{type: 'mount', key: user.items.currentMount}})
# Strip owned gear down to the training sword, but preserve purchase history so user can re-purchase limited edition equipment
_.each gear.owned, (v, k) -> if gear.owned[k] then gear.owned[k] = false; true
# Strip owned gear down to the training sword and free items (zero gold value), but preserve purchase history so user can re-purchase limited edition equipment
_.each gear.owned, (v, k) -> if gear.owned[k] and content.gear.flat[k].value then gear.owned[k] = false; true
gear.owned.weapon_warrior_0 = true
user.markModified? 'items.gear.owned'
user.preferences.costume = false
@@ -719,7 +727,7 @@ api.wrap = (user, main=true) ->
addPushDevice: (req, cb) ->
user.pushDevices = [] unless user.pushDevices
pd = user.pushDevices
item = {regId:req.body.regId, type:req.body.type};
item = {regId:req.body.regId, type:req.body.type}
i = _.findIndex pd, {regId: item.regId}
pd.push(item) unless i != -1
@@ -786,7 +794,7 @@ api.wrap = (user, main=true) ->
if userPets[pet] >= 50 and !user.items.mounts[pet]
evolve()
user.items.food[food.key]--
cb? {code:200, message}, userPets[pet]
cb? {code:200, message}, {value: userPets[pet]}
buySpecialSpell: (req,cb) ->
{key} = req.params
@@ -800,7 +808,7 @@ api.wrap = (user, main=true) ->
cb? {code:200,message}, _.pick(user,$w 'items stats')
# buy is for using Gold, purchase is for Gems (I know, I know...)
purchase: (req, cb, ga) ->
purchase: (req, cb, analytics) ->
{type,key} = req.params
if type is 'gems' and key is 'gem'
@@ -812,7 +820,16 @@ api.wrap = (user, main=true) ->
user.balance += .25
user.purchased.plan.gemsBought++
user.stats.gp -= convRate
mixpanel?.track("Acquire Item",{'itemName':key,'acquireMethod':'Gold','goldCost':convRate})
analyticsData = {
uuid: user._id,
itemKey: key,
acquireMethod: 'Gold',
goldCost: convRate,
category: 'behavior'
}
analytics?.track('purchase gems', analyticsData)
return cb? {code:200,message:"+1 Gem"}, _.pick(user,$w 'stats balance')
return cb?({code:404,message:":type must be in [eggs,hatchingPotions,food,quests,gear]"},req) unless type in ['eggs','hatchingPotions','food','quests','gear']
@@ -830,11 +847,20 @@ api.wrap = (user, main=true) ->
else
user.items[type][key] = 0 unless user.items[type][key] > 0
user.items[type][key]++
mixpanel?.track("Acquire Item",{'itemName':key,'acquireMethod':'Gems','gemCost':item.value})
cb? null, _.pick(user,$w 'items balance')
ga?.event('behavior', 'gems', key).send()
releasePets: (req, cb) ->
analyticsData = {
uuid: user._id,
itemKey: key,
itemType: 'Market',
acquireMethod: 'Gems',
gemCost: item.value,
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
cb? null, _.pick(user,$w 'items balance')
releasePets: (req, cb, analytics) ->
if user.balance < 1
return cb? {code:401,message: i18n.t('notEnoughGems', req.language)}
else
@@ -845,10 +871,17 @@ api.wrap = (user, main=true) ->
user.achievements.beastMasterCount = 0
user.achievements.beastMasterCount++
user.items.currentPet = ""
cb? null, user
mixpanel?.track("Acquire Item",{'itemName':'Kennel Key','acquireMethod':'Gems','gemCost':4})
releaseMounts: (req, cb) ->
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
}
analytics?.track('release pets', analyticsData)
cb? null, user
releaseMounts: (req, cb, analytics) ->
if user.balance < 1
return cb? {code:401,message: i18n.t('notEnoughGems', req.language)}
else
@@ -859,8 +892,16 @@ api.wrap = (user, main=true) ->
if not user.achievements.mountMasterCount
user.achievements.mountMasterCount = 0
user.achievements.mountMasterCount++
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 4,
category: 'behavior'
}
analytics?.track('release mounts', analyticsData)
cb? null, user
mixpanel?.track("Acquire Item",{'itemName':'Kennel Key','acquireMethod':'Gems','gemCost':4})
releaseBoth: (req, cb) ->
if user.balance < 1.5 and not user.achievements.triadBingo
@@ -868,7 +909,13 @@ api.wrap = (user, main=true) ->
else
giveTriadBingo = true
if not user.achievements.triadBingo
mixpanel?.track("Acquire Item",{'itemName':'Kennel Key','acquireMethod':'Gems','gemCost':6})
analyticsData = {
uuid: user._id,
acquireMethod: 'Gems',
gemCost: 6,
category: 'behavior'
}
analytics?.track('release pets & mounts', analyticsData)
user.balance -= 1.5
user.items.currentMount = ""
user.items.currentPet = ""
@@ -889,15 +936,16 @@ api.wrap = (user, main=true) ->
cb? null, user
# buy is for gear, purchase is for gem-purchaseables (i know, I know...)
buy: (req, cb) ->
buy: (req, cb, analytics) ->
{key} = req.params
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/blob/develop/common/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 buy this item"}) if item.canOwn? and !item.canOwn(user)
armoireResp = undefined
if item.key is 'potion'
user.stats.hp += 15
user.stats.hp = 50 if user.stats.hp > 50
@@ -914,15 +962,19 @@ api.wrap = (user, main=true) ->
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
if api.count.remainingGearInSet(user.items.gear.owned, 'armoire') is 0 then user.flags.armoireEmpty = true
armoireResp = {type: "gear", dropKey: drop.key, dropText: drop.text(req.language)}
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)
armoireResp = {type: "food", dropKey: drop.key, dropArticle: drop.article, dropText: drop.text(req.language)}
else
user.stats.exp += Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10)
armoireExp = Math.floor(user.fns.predictableRandom(user.stats.exp) * 40 + 10)
user.stats.exp += armoireExp
message = i18n.t('armoireExp', req.language)
armoireResp = {"type": "experience", "value": armoireExp}
else
user.items.gear.equipped[item.type] = item.key
user.items.gear.owned[item.key] = true
@@ -930,10 +982,42 @@ api.wrap = (user, main=true) ->
message ?= i18n.t('messageBought', {itemText: item.text(req.language)}, req.language)
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 flags')
buyMysterySet: (req, cb)->
analyticsData = {
uuid: user._id,
itemKey: key,
acquireMethod: 'Gold',
goldCost: item.value,
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
buyResp = _.pick(user,$w 'items achievements stats flags')
buyResp["armoire"] = armoireResp if armoireResp
cb? {code:200, message}, buyResp
buyQuest: (req, cb, analytics) ->
{key} = req.params
item = content.quests[key]
return cb?({code:404, message:"Quest '#{key} not found (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content.coffee)"}) unless item
return cb?({code:404, message:"Quest '#{key} is not a Gold-purchasable quest (see https://github.com/HabitRPG/habitrpg/blob/develop/common/script/content.coffee)"}) unless item.category is 'gold' and item.goldValue
return cb?({code:401, message: i18n.t('messageNotEnoughGold', req.language)}) if user.stats.gp < item.goldValue
message = i18n.t('messageBought', {itemText: item.text(req.language)}, req.language)
user.items.quests[item.key] ?= 0
user.items.quests[item.key] += 1
user.stats.gp -= item.goldValue
analyticsData = {
uuid: user._id,
itemKey: item.key,
itemType: 'Market',
goldCost: item.goldValue,
acquireMethod: 'Gold',
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
cb? {code:200, message}, user.items.quests
buyMysterySet: (req, cb, analytics)->
return cb?({code:401, message:"You don't have enough Mystic Hourglasses"}) unless user.purchased.plan.consecutive.trinkets>0
mysterySet = content.timeTravelerStore(user.items.gear.owned)?[req.params.key]
if window?.confirm?
@@ -941,7 +1025,15 @@ api.wrap = (user, main=true) ->
return cb?({code:404, message:"Mystery set not found, or set already owned"}) unless mysterySet
_.each mysterySet.items, (i)->
user.items.gear.owned[i.key]=true
mixpanel?.track("Acquire Item",{'itemName':i.key,'acquireMethod':'Hourglass'})
analyticsData = {
uuid: user._id,
itemKey: i.key,
itemType: 'Subscriber Gear',
acquireMethod: 'Hourglass',
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
user.purchased.plan.consecutive.trinkets--
cb? null, _.pick(user,$w 'items purchased.plan.consecutive')
@@ -985,7 +1077,7 @@ api.wrap = (user, main=true) ->
user.items.hatchingPotions[hatchingPotion]--
cb? {code:200, message:i18n.t('messageHatched', req.language)}, user.items
unlock: (req, cb, ga) ->
unlock: (req, cb, analytics) ->
{path} = req.query
fullSet = ~path.indexOf(",")
cost =
@@ -1012,17 +1104,36 @@ api.wrap = (user, main=true) ->
user.fns.dotSet "purchased." + path, true
user.balance -= cost
if ~path.indexOf('gear.') then user.markModified? 'gear.owned' else user.markModified? 'purchased'
analyticsData = {
uuid: user._id,
itemKey: path,
itemType: 'customization',
acquireMethod: 'Gems',
gemCost: (cost/.25),
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
cb? null, _.pick(user,$w 'purchased preferences items')
mixpanel?.track("Acquire Item",{'itemName':'Customizations','acquireMethod':'Gems','gemCost':(cost / .25)})
ga?.event('behavior', 'gems', path).send()
# ------
# Classes
# ------
changeClass: (req, cb, ga) ->
changeClass: (req, cb, analytics) ->
klass = req.query?.class
if klass in ['warrior','rogue','wizard','healer']
analyticsData = {
uuid: user._id,
class: klass,
acquireMethod: 'Gems',
gemCost: 3,
category: 'behavior'
}
analytics?.track('change class', analyticsData)
user.stats.class = klass
user.flags.classSelected = true
# Clear their gear and equip their new class's gear (can still equip old gear from inventory)
@@ -1053,8 +1164,6 @@ api.wrap = (user, main=true) ->
user.balance -= .75
_.merge user.stats, {str: 0, con: 0, per: 0, int: 0, points: api.capByLevel(user.stats.lvl)}
user.flags.classSelected = false
mixpanel?.track("Acquire Item",{'itemName':klass,'acquireMethod':'Gems','gemCost':3})
ga?.event('behavior', 'gems', 'changeClass').send()
#'stats.points': this is handled on the server
cb? null, _.pick(user,$w 'stats flags items preferences')
@@ -1075,27 +1184,31 @@ api.wrap = (user, main=true) ->
user.stats.mp++ if stat is 'int' #increase their MP along with their max MP
cb? null, _.pick(user,$w 'stats')
readValentine: (req,cb) ->
user.items.special.valentineReceived.shift()
user.markModified? 'items.special.valentineReceived'
cb? null, 'items.special'
readCard: (req, cb) ->
{cardType} = req.params
user.items.special["#{cardType}Received"].shift()
user.markModified? "items.special.#{cardType}Received"
user.flags.cardReceived = false
cb? null, 'items.special flags.cardReceived'
openMysteryItem: (req,cb,ga) ->
openMysteryItem: (req,cb,analytics) ->
item = user.purchased.plan?.mysteryItems?.shift()
return cb?(code:400,message:"Empty") unless item
item = content.gear.flat[item]
user.items.gear.owned[item.key] = true
user.markModified? 'purchased.plan.mysteryItems'
# Could show {code:200} message, but it's yellow with no icon. This is round-about, but prettier. FIXME
(user._tmp?={}).drop = {type: 'gear', dialog: "#{item.text(req.language)} inside!"} if typeof window != 'undefined'
#cb? {code:200, message:"#{item.text} inside!"}, user.items.gear.owned
item.notificationType = 'Mystery' # needed for website/public/js/controllers/notificationCtrl.js line 59 approx.
analyticsData = {
uuid: user._id,
itemKey: item,
itemType: 'Subscriber Gear',
acquireMethod: 'Subscriber',
category: 'behavior'
}
analytics?.track('open mystery item', analyticsData)
(user._tmp?={}).drop = item if typeof window != 'undefined'
cb? null, user.items.gear.owned
readNYE: (req,cb) ->
user.items.special.nyeReceived.shift()
user.markModified? 'items.special.nyeReceived'
cb? null, 'items.special'
# ------
# Score
# ------
@@ -1186,7 +1299,7 @@ api.wrap = (user, main=true) ->
unless task.type is 'reward'
if (user.preferences.automaticAllocation is true and user.preferences.allocationMode is 'taskbased' and !(task.type is 'todo' and direction is 'down')) then user.stats.training[task.attribute] += nextDelta
if direction is 'up' # Make progress on quest based on STR
user.party.quest.progress.up = user.party.quest.progress.up || 0;
user.party.quest.progress.up = user.party.quest.progress.up || 0
user.party.quest.progress.up += (nextDelta * (1 + (user._statsComputed.str / 200))) if task.type in ['daily','todo']
user.party.quest.progress.up += (nextDelta * (0.5 + (user._statsComputed.str / 400))) if task.type is 'habit'
task.value += nextDelta
@@ -1352,10 +1465,10 @@ api.wrap = (user, main=true) ->
returns random property (the value)
###
randomVal: (obj, options) ->
array = if options?.key then _.keys(obj) else _.values(obj)
rand = user.fns.predictableRandom(options?.seed)
array.sort()
array[Math.floor(rand * array.length)]
array = if options?.key then _.keys(obj) else _.values(obj)
rand = user.fns.predictableRandom(options?.seed)
array.sort()
array[Math.floor(rand * array.length)]
###
This allows you to set object properties by dot-path. Eg, you can run pathSet('stats.hp',50,user) which is the same as
@@ -1483,7 +1596,7 @@ api.wrap = (user, main=true) ->
else "str" # if all else fails, dump into STR
)()]++
updateStats: (stats, req) ->
updateStats: (stats, req, analytics) ->
# Game Over (death)
return user.stats.hp=0 if stats.hp <= 0
@@ -1510,7 +1623,7 @@ api.wrap = (user, main=true) ->
user.fns.autoAllocate()
else
# add new allocatable points. We could do user.stats.points++, but this does a fail-safe just in case
user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int);
user.stats.points = user.stats.lvl - (user.stats.con + user.stats.str + user.stats.per + user.stats.int)
if user.stats.points < 0
user.stats.points = 0
# This happens after dropping level with Fix Character Values and perhaps from other causes.
@@ -1523,9 +1636,7 @@ api.wrap = (user, main=true) ->
user.flags.customizationsNotification = true
if !user.flags.itemsEnabled and (user.stats.exp > 10 or user.stats.lvl > 1)
user.flags.itemsEnabled = true
if !user.flags.partyEnabled and user.stats.lvl >= 3
user.flags.partyEnabled = true
if !user.flags.dropsEnabled and user.stats.lvl >= 4
if !user.flags.dropsEnabled and user.stats.lvl >= 3
user.flags.dropsEnabled = true
if user.items.eggs["Wolf"] > 0 then user.items.eggs["Wolf"]++ else user.items.eggs["Wolf"] = 1
if !user.flags.classSelected and user.stats.lvl >= 10
@@ -1537,10 +1648,14 @@ api.wrap = (user, main=true) ->
user.items.quests[k]++
(user.flags.levelDrops ?= {})[k] = true
user.markModified? 'flags.levelDrops'
mixpanel?.track("Acquire Item",{'itemName':k,'acquireMethod':'Drop'})
user._tmp.drop = _.defaults content.quests[k],
type: 'Quest'
dialog: i18n.t('messageFoundQuest', {questText: content.quests[k].text(req.language)}, req.language)
analyticsData = {
uuid: user._id,
itemKey: k,
acquireMethod: 'Level Drop',
category: 'behavior'
}
analytics?.track('acquire item', analyticsData)
user._tmp.drop = {type: 'Quest', key: k}
if !user.flags.rebirthEnabled and (user.stats.lvl >= 50 or user.achievements.beastMaster)
user.flags.rebirthEnabled = true
@@ -1620,56 +1735,66 @@ api.wrap = (user, main=true) ->
daily.completed = false
return
multiDaysCountAsOneDay = true
# If the user does not log in for two or more days, cron (mostly) acts as if it were only one day.
# When site-wide difficulty settings are introduced, this can be a user preference option.
# Tally each task
todoTally = 0
user.todos.forEach (task) -> # make uncompleted todos redder
return unless task
{id, completed} = task
delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(multiDaysCountAsOneDay ? 1 : daysMissed), cron:true}})
absVal = if (completed) then Math.abs(task.value) else task.value
todoTally += absVal
dailyChecked = 0 # how many dailies were checked?
dailyDueUnchecked = 0 # how many dailies were due but not checked?
user.party.quest.progress.down ?= 0
user.todos.concat(user.dailys).forEach (task) ->
user.dailys.forEach (task) ->
return unless task
{id, completed} = task
{id, type, completed, repeat} = task
# Deduct points for missed Daily tasks, but not for Todos (just increase todo's value)
# Deduct points for missed Daily tasks
EvadeTask = 0
scheduleMisses = daysMissed
if completed
if type is 'daily'
dailyChecked += 1
dailyChecked += 1
else
# for dailys which have repeat dates, need to calculate how many they've missed according to their own schedule
if (type is 'daily') and repeat
scheduleMisses = 0
_.times daysMissed, (n) ->
thatDay = moment(now).subtract({days: n + 1})
if api.shouldDo(thatDay.toDate(), task, user.preferences)
scheduleMisses++
if user.stats.buffs.stealth
user.stats.buffs.stealth--
EvadeTask++
if scheduleMisses > EvadeTask
if type is 'daily'
perfect = false
if task.checklist?.length > 0 # Partially completed checklists dock fewer mana points
fractionChecked = _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0) / task.checklist.length
dailyDueUnchecked += (1 - fractionChecked)
dailyChecked += fractionChecked
else
dailyDueUnchecked += 1
delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(scheduleMisses-EvadeTask), cron:true}}); # this line occurs for todos or dailys
user.party.quest.progress.down += delta if type is 'daily'
# dailys repeat, so need to calculate how many they've missed according to their own schedule
scheduleMisses = 0
for n in [0...daysMissed]
thatDay = moment(now).subtract({days: n + 1})
if api.shouldDo(thatDay.toDate(), task, user.preferences)
scheduleMisses++
if user.stats.buffs.stealth
user.stats.buffs.stealth--
EvadeTask++
if multiDaysCountAsOneDay
break
switch type
when 'daily'
# This occurs whether or not the task is completed
(task.history ?= []).push({ date: +new Date, value: task.value })
task.completed = false
if completed || (scheduleMisses > 0)
_.each task.checklist, ((i)->i.completed=false;true) # this should not happen for grey tasks unless they are completed
when 'todo'
#get updated value
absVal = if (completed) then Math.abs(task.value) else task.value
todoTally += absVal
if scheduleMisses > EvadeTask
perfect = false
if task.checklist?.length > 0 # Partially completed checklists dock fewer mana points
fractionChecked = _.reduce(task.checklist,((m,i)->m+(if i.completed then 1 else 0)),0) / task.checklist.length
dailyDueUnchecked += (1 - fractionChecked)
dailyChecked += fractionChecked
else
dailyDueUnchecked += 1
delta = user.ops.score({params:{id:task.id, direction:'down'}, query:{times:(multiDaysCountAsOneDay ? 1 : (scheduleMisses-EvadeTask)), cron:true}})
# Apply damage from a boss, less damage for Trivial priority (difficulty)
user.party.quest.progress.down += delta * (if task.priority < 1 then task.priority else 1)
# NB: Medium and Hard priorities do not increase damage from boss. This was by accident
# initially, and when we realised, we could not fix it because users are used to
# their Medium and Hard Dailies doing an Easy amount of damage from boss.
# Easy is task.priority = 1. Anything < 1 will be Trivial (0.1) or any future
# setting between Trivial and Easy.
(task.history ?= []).push({ date: +new Date, value: task.value })
task.completed = false
if completed || (scheduleMisses > 0)
_.each task.checklist, ((i)->i.completed=false;true) # this should not happen for grey tasks unless they are completed
user.habits.forEach (task) -> # slowly reset 'onlies' value to 0
if task.up is false or task.down is false
@@ -1678,7 +1803,6 @@ api.wrap = (user, main=true) ->
else
task.value = task.value / 2
# Finished tallying
((user.history ?= {}).todos ?= []).push { date: now, value: todoTally }
# tally experience
@@ -1713,8 +1837,17 @@ api.wrap = (user, main=true) ->
# Analytics
user.flags.cronCount?=0
user.flags.cronCount++
options.mixpanel?.track('Cron',{'distinct_id':user._id,'resting':user.preferences.sleep})
options.ga?.event('behavior', 'cron', 'cron', user.flags.cronCount).send(); #TODO userId for cohort
analyticsData = {
category: 'behavior',
gaLabel: 'Cron Count',
gaValue: user.flags.cronCount,
uuid: user._id,
user: user,
resting: user.preferences.sleep,
cronCount: user.flags.cronCount
}
options.analytics?.track('Cron', analyticsData)
# After all is said and done, progress up user's effect on quest, return those values & reset the user's
progress = user.party.quest.progress; _progress = _.cloneDeep progress
+3 -3
View File
@@ -124,7 +124,7 @@
if(removeWatch)
{
doRemoveWatch();
doRemoveWatch();
}
};
@@ -144,9 +144,9 @@
habitrpg.filter('markdown', function() {
return function(input){
var html = md.toHtml(input);
html = html.replace(' href',' target="_self" href');
return html;
};
});
+5
View File
@@ -194,6 +194,11 @@ angular.module('habitrpg')
return this.settings.auth.apiId !== "";
},
getBalanceInGems: function() {
var balance = user.balance || 0;
return balance * 4;
},
log: function (action, cb) {
//push by one buy one if an array passed in.
if (_.isArray(action)) {