Last active
October 11, 2015 01:08
-
-
Save nzifnab/3779098 to your computer and use it in GitHub Desktop.
Coffeescript for stat calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
I don't advise trying to edit this file directly. This was coded using coffeescript. | |
Coffeescript source here: https://gist.github.com/3779098 | |
### | |
`function onOpen(){}` | |
`function populateProfile(){}` | |
`function clearUpgradeFields(){}` | |
`function clearAllFields(){}` | |
`function spreadsheetName(){}` | |
`function heroSelectClickHandler(){}` | |
flattenArray = (arr) -> | |
return [] if arr.length == 0 | |
arr.reduce (sum, val) -> sum.concat(val) | |
padArray = (arr, size, value) -> | |
length = Math.abs(size) - arr.length | |
newArr = [].concat(arr) | |
return newArr if (length <= 0) | |
i = 0 | |
while i < length | |
if size < 0 then newArr.unshift(value) else newArr.push(value) | |
i++ | |
newArr | |
class CellRanges | |
### | |
If you moved things around, you'll need to update the | |
cell ranges here. Standard cell range notation. | |
### | |
@SHEET_VERSION: "O5" | |
@THIS_SHEET: "O7" | |
@MAIN_SHEET: "N5" | |
@ADVANCED_SHEET: "N7" | |
@BATTLE_TAG: "X4" | |
@HERO_ID: "X5" | |
@CLASS_ABBREV: "X6" | |
@LEVEL: "X7" | |
@PARAGON_LEVEL: "X9" | |
@LOCALIZATION: "X10" | |
@LOGGER: "Z1" | |
@HEAD: "B3:B13" | |
@SHOULDERS: "F3:F13" | |
@HANDS: "B16:B26" | |
@BRACERS: "F16:F26" | |
@TORSO: "J16:J26" | |
@LEGS: "B29:B39" | |
@FEET: "F29:F39" | |
@WAIST: "J29:J39" | |
@NECK: "B42:B54" | |
@RIGHTFINGER: "F42:F54" | |
@LEFTFINGER: "J42:J54" | |
@MAINHAND: "B57:B73" | |
@OFFHAND: "F57:F73" | |
@SET: "J3:J13" | |
@HEAD_UPGRADE: "C3:C13" | |
@SHOULDERS_UPGRADE: "G3:G13" | |
@HANDS_UPGRADE: "C16:C26" | |
@BRACERS_UPGRADE: "G16:G26" | |
@TORSO_UPGRADE: "K16:K26" | |
@LEGS_UPGRADE: "C29:C39" | |
@FEET_UPGRADE: "G29:G39" | |
@WAIST_UPGRADE: "K29:K39" | |
@NECK_UPGRADE: "C42:C54" | |
@RIGHTFINGER_UPGRADE: "G42:G54" | |
@LEFTFINGER_UPGRADE: "K42:K54" | |
@MAINHAND_UPGRADE: "C57:C73" | |
@OFFHAND_UPGRADE: "G57:G73" | |
@SET_UPGRADE: "K3:K13" | |
@HEAD_ADVANCED: "B3:B12" | |
@SHOULDERS_ADVANCED: "F3:F12" | |
@HANDS_ADVANCED: "B15:B24" | |
@BRACERS_ADVANCED: "F15:F24" | |
@TORSO_ADVANCED: "J15:J24" | |
@LEGS_ADVANCED: "B27:B36" | |
@FEET_ADVANCED: "F27:F36" | |
@WAIST_ADVANCED: "J27:J36" | |
@NECK_ADVANCED: "B39:B48" | |
@RIGHTFINGER_ADVANCED: "F39:F48" | |
@LEFTFINGER_ADVANCED: "J39:J48" | |
@MAINHAND_ADVANCED: "B51:B60" | |
@OFFHAND_ADVANCED: "F51:F62" | |
@SET_ADVANCED: "J3:J12" | |
@HEAD_ADVANCED_UPGRADE: "C3:C12" | |
@SHOULDERS_ADVANCED_UPGRADE: "G3:G12" | |
@HANDS_ADVANCED_UPGRADE: "C15:C24" | |
@BRACERS_ADVANCED_UPGRADE: "G15:G24" | |
@TORSO_ADVANCED_UPGRADE: "K15:K24" | |
@LEGS_ADVANCED_UPGRADE: "C27:C36" | |
@FEET_ADVANCED_UPGRADE: "G27:G36" | |
@WAIST_ADVANCED_UPGRADE: "K27:K36" | |
@NECK_ADVANCED_UPGRADE: "C39:C48" | |
@RIGHTFINGER_ADVANCED_UPGRADE: "G39:G48" | |
@LEFTFINGER_ADVANCED_UPGRADE: "K39:K48" | |
@MAINHAND_ADVANCED_UPGRADE: "C51:C60" | |
@OFFHAND_ADVANCED_UPGRADE: "G51:G62" | |
@SET_ADVANCED_UPGRADE: "K3:K12" | |
@FOLLOWER_BUFFS: "X12:X16" | |
@PLAYER_BUFFS: "X17:X41" | |
@verticalSize: (range) -> | |
matches = @[range].match /^[A-Z]+(\d+)\:[A-Z]+(\d+)$/ | |
minRange = parseInt(matches[1]) | |
maxRange = parseInt(matches[2]) | |
totalCells = maxRange - minRange + 1 | |
onOpen = -> | |
SpreadsheetApp.getActiveSpreadsheet().addMenu( | |
'Diablo', [ | |
{ name: 'Populate Profile', functionName: 'populateProfile' }, | |
{ name: 'Clear Upgrade Fields', functionName: 'clearUpgradeFields' }, | |
{ name: 'Clear All', functionName: 'clearAllFields' } | |
] | |
) | |
spreadsheetName = (dummy) -> | |
SpreadsheetApp.getActiveSheet().getName() | |
populateProfile = -> | |
try | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()) | |
profile = new Profile(sheets) | |
return D3Logger.log(sheets.main, "") and false if profile.battleTag == 'cancel' | |
profile.populateSheet() | |
D3Logger.log(sheets.main, "") | |
catch error | |
Browser.msgBox("An error occurred when attempting to perform that action. Error: #{error}") unless error == "IGNOREME" | |
clearUpgradeFields = -> | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()) | |
for slot in ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"] | |
sheets.main.getRange(CellRanges["#{slot.toUpperCase()}_UPGRADE"]).setValue('') | |
sheets.advanced.getRange(CellRanges["#{slot.toUpperCase()}_ADVANCED_UPGRADE"]).setValue('') | |
clearAllFields = -> | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()) | |
for slot in ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"] | |
try | |
sheets.main.getRange(CellRanges[slot.toUpperCase()]).setValue('') | |
sheets.main.getRange(CellRanges["#{slot.toUpperCase()}_UPGRADE"]).setValue('') | |
sheets.advanced.getRange(CellRanges["#{slot.toUpperCase()}_ADVANCED"]).setValue('') | |
sheets.advanced.getRange(CellRanges["#{slot.toUpperCase()}_ADVANCED_UPGRADE"]).setValue('') | |
catch error | |
Browser.msgBox("an error occurred when clearing out the #{slot} slot: #{error}") | |
for range in ["battle_tag", "hero_id", "class_abbrev", "level", "paragon_level", "logger"] | |
sheets.main.getRange(CellRanges[range.toUpperCase()]).setValue('') | |
for range in ["follower_buffs", "player_buffs"] | |
sheets.main.getRange(CellRanges[range.toUpperCase()]).setValue('N') | |
heroSelectClickHandler = (e) -> | |
Profile.heroSelectClickHandler(e) | |
class API | |
@parse: (path) -> | |
response = UrlFetchApp.fetch("http://#{API.locale}.battle.net/api/d3/#{path}") | |
JSON.parse(response.getContentText()) | |
@locale: 'us' | |
class D3Logger | |
@log: (sheet, text) -> | |
### | |
sheet.getRange(CellRanges.LOGGER).setValue(text) | |
### | |
class SheetCollection | |
constructor: (sheet) -> | |
@spreadsheet = sheet.getParent() | |
@version = sheet.getRange(CellRanges.SHEET_VERSION).getValue() | |
mainSheetName = sheet.getRange(CellRanges.MAIN_SHEET).getValue() | |
advancedSheetName = sheet.getRange(CellRanges.ADVANCED_SHEET).getValue() | |
@main = @findByName(mainSheetName) | |
@advanced = @findByName(advancedSheetName) | |
findByName: (name) -> | |
@all ||= do => | |
sheets = {} | |
for sheet in @spreadsheet.getSheets() | |
sheets[sheet.getRange(CellRanges.THIS_SHEET).getValue()] = sheet | |
sheets | |
@all[name] | |
class Profile | |
constructor: (@sheets) -> | |
D3Logger.log(@sheets.main, "Collecting BattleTag") | |
battleTagCell = @sheets.main.getRange(CellRanges.BATTLE_TAG) | |
@battleTag = battleTagCell.getValue() | |
if !@battleTag? || @battleTag == "" | |
@battleTag = Browser.inputBox("Enter your battletag: Example#1234") | |
return false if @battleTag == 'cancel' | |
@battleTag = @battleTag.split("#").join('-') | |
battleTagCell.setValue(@battleTag.split('-').join('#')) | |
localeCell = @sheets.main.getRange(CellRanges.LOCALIZATION) | |
API.locale = localeCell.getValue() | |
API.locale = if API.locale == '' then 'us' else API.locale | |
heroes: -> | |
@_heroes ||= do => | |
try | |
D3Logger.log(@sheets.main, "Parsing Profile Data") | |
data = API.parse("profile/#{@battleTag}/") | |
(new Hero(@sheets.main, hero) for hero in data.heroes) | |
catch error | |
Browser.msgBox("An error occurred when accessing your profile data. Is your battleTag correct? Error: #{error}") | |
throw "IGNOREME" | |
createHeroSelectionInterface: (options) -> | |
D3Logger.log(@sheets.main, "") | |
heroSelect = UiApp.createApplication().setTitle("Select which hero to use") | |
handler = heroSelect.createServerHandler("heroSelectClickHandler") | |
panel = heroSelect.createVerticalPanel() | |
buttons = [] | |
for option in options | |
btn = heroSelect.createButton(option.description).setId(option.id).addClickHandler(handler) | |
buttons.push btn | |
panel.add(btn) | |
heroSelect.add(panel) | |
@sheets.spreadsheet.show(heroSelect) | |
selectedHero: -> | |
@_selectedHero ||= do => | |
heroId = @sheets.main.getRange(CellRanges.HERO_ID).getValue() | |
if !heroId? || heroId == "" | |
options = ({id: hero.id, description: "#{hero.name} <#{if hero.hardcore then "<span style='color:red'>hardcore</span> " else ""}#{hero['class']}> - Level #{hero.level} (#{hero.paragonLevel})"} for hero in @heroes()) | |
@createHeroSelectionInterface(options) | |
false | |
else | |
D3Logger.log(@sheets.main, "Parsing Hero Data") | |
try | |
hero = API.parse("profile/#{@battleTag}/hero/#{heroId}") | |
@sheets.main.getRange(CellRanges.CLASS_ABBREV).setValue(Hero.abbrev(hero['class'])) | |
@sheets.main.getRange(CellRanges.LEVEL).setValue(hero.level) | |
@sheets.main.getRange(CellRanges.PARAGON_LEVEL).setValue(hero.paragonLevel) | |
new Hero(@sheets, hero) | |
catch error | |
Browser.msgBox("An error occurred when accessing your hero data. Bnet may be temporarily down or the rate limit for requests has been reached (or something else?). Error: #{error}") | |
throw "IGNOREME" | |
populateItems: -> | |
return false unless (hero = @selectedHero()) | |
for slot in ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"] | |
@sheets.main.getRange(CellRanges[slot.toUpperCase()]).setValue('') | |
@sheets.advanced.getRange(CellRanges["#{slot.toUpperCase()}_ADVANCED"]).setValue('') | |
for item in hero.items() | |
item.populateSpreadsheet(@sheets.main) | |
ItemSet.populateSpreadsheet(@sheets) | |
true | |
populateSkills: -> | |
try | |
return false unless (hero = @selectedHero()) | |
hero.skills().populateSpreadsheet(@sheets.main) | |
catch error | |
Browser.msgBox("An error occurred: #{error}") unless error == 'IGNOREME' | |
true | |
populateSheet: -> | |
try | |
return false unless @populateItems() | |
return false unless @populateSkills() | |
catch error | |
Browser.msgBox("An error occurred: #{error}") unless error == 'IGNOREME' | |
true | |
@heroSelectClickHandler: (e) -> | |
heroId = e.parameter.source | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()) | |
sheets.main.getRange(CellRanges.HERO_ID).setValue(heroId) | |
profile = new Profile(sheets) | |
profile.populateSheet() | |
UiApp.getActiveApplication().close() | |
class Hero | |
@ABBREV_MAP: { | |
'wizard': 'W', | |
'barbarian': 'B', | |
'witch-doctor': 'WD', | |
'demon-hunter': 'DH', | |
'monk': 'M' | |
} | |
constructor: (@sheets, @data) -> | |
{@name, @id, @level, @paragonLevel, @class, @hardcore} = @data | |
@resists = {Fire: 0, Lightning: 0, Poison: 0, Physical: 0, Arcane: 0, Cold: 0} | |
items: -> | |
@_items ||= do => | |
items = [] | |
for own slot, item of @data.items | |
itemKlass = Item.klassForSlot(slot) | |
i = new itemKlass(@sheets, slot, item) | |
if @oneWithEverything() | |
for own type of @resists | |
@resists[type] += i.attributes().resists[type] | |
items.push(i) | |
if @oneWithEverything() | |
maxResistValue = 0 | |
for own type, val of @resists | |
if val >= maxResistValue | |
maxResistType = type | |
maxResistValue = val | |
for item in items | |
item.attributes().allResist += item.attributes().resists[maxResistType] | |
items | |
oneWithEverything: -> | |
@class == 'monk' && @skills().has('One With Everything')[0] == 'Y' | |
skills: -> | |
@_skills ||= do => | |
new Skills(@sheets, @class, @data.skills.passive.concat(@data.skills.active)) | |
@abbrev: (name) -> | |
Hero.ABBREV_MAP[name] | |
class Skills | |
constructor: (@sheets, @klass, @data) -> | |
@names = [] | |
for skill in @data | |
@names.push(skill.skill.name) if skill.skill? | |
@names.push(skill.rune.name) if skill.rune? | |
populateSpreadsheet: -> | |
@sheets.main.getRange(CellRanges.PLAYER_BUFFS).setValues(@displayableSkills()) | |
has: (name) -> | |
if name in @names then ['Y'] else ['N'] | |
displayableSkills: -> | |
skills = switch @klass | |
when "wizard" | |
[ | |
[""], | |
@has('Energy Armor'), | |
@has('Pinpoint Barrier'), | |
@has('Prismatic Armor'), | |
@has('Familiar'), | |
@has('Sparkflint'), | |
@has('Vigoron'), | |
@has('Magic Weapon'), | |
@has('Force Weapon'), | |
@has('Blood Magic'), | |
@has('Frost Nova'), | |
@has('Deep Freeze'), | |
@has('Bone Chill'), | |
@has('Ice Armor'), | |
@has('Crystallize'), | |
@has('Slow Time'), | |
@has('Time Warp'), | |
@has('Stretch Time'), | |
@has('Safe Passage'), | |
[""], | |
@has('Blur'), | |
@has('Glass Cannon'), | |
@has("Galvanizing Ward") | |
] | |
when 'demon-hunter' | |
[ | |
[""], | |
@has('Boar Companion'), | |
@has('Sentry'), | |
@has('Aid Station'), | |
@has('Guardian Turret'), | |
@has('Bait the Trap'), | |
@has('Hardened'), | |
@has('Shadow Power'), | |
@has('Gloom'), | |
[""], | |
@has('Steady Aim'), | |
@has('Archery'), | |
@has('Sharpshooter'), | |
@has('Perfectionist'), | |
@has('Brooding'), | |
@has('Cull the Weak') | |
] | |
when 'barbarian' | |
[ | |
[""], | |
@has('War Cry'), | |
@has('Hardened Wrath'), | |
@has('Invigorate'), | |
@has("Veteran's Warning"), | |
@has("Impunity"), | |
@has("Battle Rage"), | |
@has("Marauder's Rage"), | |
@has('Punish'), | |
@has('Overpower'), | |
@has('Crushing Advance'), | |
@has('Killing Spree'), | |
@has('Best Served Cold'), | |
@has('Threatening Shout'), | |
@has('Falter'), | |
[""], | |
@has("Ruthless"), | |
@has("Nerves of Steel"), | |
@has("Weapons Master"), | |
@has("Superstition"), | |
@has("Tough as Nails"), | |
@has('Bloodthirst'), | |
@has('Inspiring Presence') | |
] | |
when 'monk' | |
[ | |
[""], | |
@has('Deadly Reach'), | |
@has('Foresight'), | |
@has('Keen Eye'), | |
@has('Blazing Wrath'), | |
@has("Earth Ally"), | |
@has("Mantra of Evasion"), | |
@has("Hard Target"), | |
@has("Transgression"), | |
@has("Mantra of Healing"), | |
@has("Heavenly Body"), | |
@has("Time of Need"), | |
@has("Sustenance"), | |
@has("Boon of Inspiration"), | |
@has("Mantra of Conviction"), | |
@has("Overawe"), | |
@has("Intimidation"), | |
@has("Reclamation"), | |
@has("Concussion"), | |
@has("Lightning Flash"), | |
[""], | |
@has("Resolve"), | |
@has("Seize the Initiative"), | |
@has("The Guardian's Path"), | |
@has("Sixth Sense") | |
] | |
when 'witch-doctor' | |
[ | |
[""], | |
@has("Life Link"), | |
@has("Provoke the Pack"), | |
@has("Soul Harvest"), | |
[""], | |
@has("Jungle Fortitude"), | |
@has("Pierce the Veil"), | |
@has("Blood Ritual"), | |
@has("Zombie Handler") | |
] | |
else | |
throw "Unrecognized class: #{@klass}" | |
totalCells = CellRanges.verticalSize('PLAYER_BUFFS') | |
padArray(skills, totalCells, ['N']) | |
class ItemSet | |
@recordedSets: {} | |
constructor: (@data) -> | |
@quantity = 0 | |
{@ranks} = @data | |
attributeStrings: -> | |
@_attrs ||= flattenArray(rank.attributes for rank in @ranks when parseInt(rank.required) <= @quantity) | |
attributesRaw: -> | |
@_attributesRaw ||= Item.parseAttributesRaw(@attributeStrings()) | |
@attributesRaw: -> | |
@_attributesRaw ||= Item.combineRawValues(set.attributesRaw() for own slug, set of @recordedSets when set.attributeStrings().length > 0) | |
@record: (data) -> | |
@recordedSets[data.slug] ||= new ItemSet(data) | |
@recordedSets[data.slug].quantity += 1 | |
@recordedSets[data.slug] | |
@populateSpreadsheet: (sheets) -> | |
(new SetBonuses(sheets, 'set', {attributesRaw: @attributesRaw()})).populateSpreadsheet() | |
true | |
class Item | |
constructor: (@sheets, @slot, @basicData) -> | |
[@name, @itemId] = [@basicData.name, @basicData.tooltipParams] | |
attributes: -> | |
@_attributes ||= do => | |
D3Logger.log(@sheets.main, "Parsing item: #{@slot}") | |
try | |
data = API.parse("data/#{@itemId}") | |
@type = Item.parseType(data.type.id) | |
@set = ItemSet.record(data.set) if data.set? | |
Item.parseAttributes(data) | |
catch error | |
Browser.msgBox("An error occurred when attempting to access item properties for the #{@slot} slot. Error: #{error}") | |
throw "IGNOREME" | |
populateSpreadsheet: -> | |
mainRange = @sheets.main.getRange(CellRanges[@slot.toUpperCase()]) | |
advancedRange = @sheets.advanced.getRange(CellRanges["#{@slot.toUpperCase()}_ADVANCED"]) | |
mainRange.setValues(@displayableAttributes(@attributes())) | |
advancedRange.setValues(@displayableAttributes(@attributes(), advanced: true)) | |
displayableAttributes: (attrs, options = {}) -> | |
options.advanced ?= false | |
if options.advanced | |
[ | |
[attrs.loh], [attrs.ls], [attrs.lifeRegen], [attrs.globes], [attrs.blockChance], [attrs.meleeDR], | |
[attrs.rangedDR], [attrs.eliteDR], [attrs.eliteBonus], [attrs.demonBonus] | |
] | |
else | |
[ | |
[attrs.armor], [attrs.strength], [attrs.dexterity], | |
[attrs.intelligence], [attrs.vitality], [attrs.allResist], [attrs.lifePercent], | |
[attrs.critDamage], [attrs.critPercent], [attrs.ias], | |
[attrs.elementalPercent] | |
] | |
@parseType: (type) -> | |
switch type | |
when "CeremonialDagger" | |
"WD Knife" | |
when "FistWeapon" | |
"Fist" | |
when "MightyWeapon1H" | |
"Mighty" | |
when "MightyWeapon2H" | |
"2H Mighty" | |
when "Axe2H" | |
"2H Axe" | |
when "Mace2H" | |
"2H Mace" | |
when "Sword2H" | |
"2H Sword" | |
when "HandXBow" | |
"Hand Crossbow" | |
when "Orb" | |
"W Source" | |
when "Mojo" | |
"WD Mojo" | |
when "Quiver" | |
"DH Quiver" | |
else type | |
@klassForSlot: (slot) -> | |
switch slot | |
when "rightFinger", "leftFinger", "neck" | |
Jewelry | |
when "mainHand" | |
Weapon | |
when "offHand" | |
Offhand | |
else | |
Armor | |
@parseAttributesRaw: (rawData) -> | |
attributes = {} | |
for rawStr in rawData | |
if(match = rawStr.match(/^\+(\d+) (Dexterity|Intelligence|Strength|Vitality)$/)) | |
name = "#{match[2]}_Item" | |
val = attributes[name]?.min || 0 | |
val += parseInt(match[1]) | |
else if(match = rawStr.match(/^\+(\d+) Resistance to All Elements$/)) | |
name = "Resistance_All" | |
val = attributes[name]?.min || 0 | |
val += parseInt(match[1]) | |
else if(match = rawStr.match(/^Critical Hit Chance Increased by (\d+\.\d+)%$/)) | |
name = "Crit_Percent_Bonus_Capped" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Attack Speed Increased by (\d+)%$/)) | |
name = "Attacks_Per_Second_Item_Percent" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^\+(\d+)% Life$/)) | |
name = "Hitpoints_Max_Percent_Bonus_Item" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Critical Hit Damage Increased by (\d+)%$/)) | |
name = "Crit_Damage_Percent" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
# Advanced Stats | |
else if(match = rawStr.match(/^Reduces damage from melee attacks by (\d+)%\.?$/)) | |
name = "Damage_Percent_Reduction_From_Melee" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Reduces damage from ranged attacks by (\d+)%\.?$/)) | |
name = "Damage_Percent_Reduction_From_Ranged" | |
val += attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Increases Damage Against Elites by (\d+)%$/)) | |
name = "Damage_Percent_Bonus_Vs_Elites" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^\+(\d+)% Damage to Demons$/)) | |
name = "Damage_Percent_Bonus_Vs_Monster_Type#Demon" | |
val = attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Reduces damage from elites by (\d+)%\.?$/)) | |
name = "Damage_Percent_Reduction_From_Elites" | |
val += attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^Regenerates (\d+) Life per Second$/)) | |
name = "Hitpoints_Regen_Per_Second" | |
val += attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
else if(match = rawStr.match(/^(\d+\.\d+)% of Damage Dealt Is Converted to Life$/)) | |
name = "Steal_Health_Percent" | |
val += attributes[name]?.min || 0 | |
val += parseFloat(match[1]) / 100 | |
attributes[name] = {min: val, max: val} | |
attributes | |
@parseAttributes: (data) -> | |
# Basic | |
armor = 0 | |
strength = 0 | |
dexterity = 0 | |
intelligence = 0 | |
vitality = 0 | |
allResist = 0 | |
resists = {Fire: 0, Lightning: 0, Poison: 0, Physical: 0, Arcane: 0, Cold: 0} | |
lifePercent = 0 | |
critDamage = 0 | |
critPercent = 0 | |
ias = 0 | |
elementalPercent = 0 | |
minBaseDmg = 0 | |
minBonusDmg = 0 | |
minPhysBonusDmg = 0 | |
minJewelryDmg = 0 | |
maxJewelryDmg = 0 | |
deltaBonusDmg = 0 | |
deltaBaseDmg = 0 | |
deltaPhysBonusDmg = 0 | |
dmgPercent = 0 | |
aps = 0 | |
# Advanced | |
loh = 0 | |
ls = 0 | |
lifeRegen = 0 | |
blockChance = 0 | |
meleeDR = 0 | |
rangedDR = 0 | |
eliteDR = 0 | |
eliteBonus = 0 | |
demonBonus = 0 | |
blockMin = 0 | |
blockMax = 0 | |
globes = 0 | |
for attributes in [data.attributesRaw].concat(if data.gems then (gem.attributesRaw for gem in data.gems) else []) | |
for own name, values of attributes | |
switch name | |
when "Armor_Item" | |
armor += parseFloat(values.min) | |
when "Armor_Bonus_Item" | |
armor += parseFloat(values.min) | |
when "Strength_Item" | |
strength += parseFloat(values.min) | |
when "Dexterity_Item" | |
dexterity += parseFloat(values.min) | |
when "Intelligence_Item" | |
intelligence += parseFloat(values.min) | |
when "Vitality_Item" | |
vitality += parseFloat(values.min) | |
when "Resistance_All" | |
allResist += parseFloat(values.min) | |
when "Resistance#Fire", "Resistance#Lightning", "Resistance#Poison", "Resistance#Physical", "Resistance#Arcane", "Resistance#Cold" | |
resists[name.split('#')[1]] += parseFloat(values.min) | |
when "Hitpoints_Max_Percent_Bonus_Item" | |
lifePercent += parseFloat(values.min)*100 | |
when "Crit_Damage_Percent" | |
critDamage += parseFloat(values.min)*100 | |
when "Crit_Percent_Bonus_Capped" | |
critPercent += parseFloat(values.min)*100 | |
when "Attacks_Per_Second_Percent", "Attacks_Per_Second_Item_Percent" | |
ias += parseFloat(values.min)*100 | |
when "Damage_Type_Percent_Bonus#Fire", "Damage_Type_Percent_Bonus#Lightning", "Damage_Type_Percent_Bonus#Poison", "Damage_Type_Percent_Bonus#Arcane", "Damage_Type_Percent_Bonus#Cold" | |
elementalPercent += parseFloat(values.min)*100 | |
when "Damage_Weapon_Min#Physical", "Damage_Min#Physical" | |
minBaseDmg += parseFloat(values.min) | |
minJewelryDmg += parseFloat(values.min) | |
maxJewelryDmg += parseFloat(values.min) | |
when "Damage_Bonus_Min#Physical" | |
minBaseDmg += parseFloat(values.min) | |
minJewelryDmg += parseFloat(values.min) | |
when "Damage_Weapon_Bonus_Min#Physical" | |
minPhysBonusDmg += parseFloat(values.min) | |
when "Damage_Weapon_Delta#Physical", "Damage_Delta#Physical" | |
deltaBaseDmg += parseFloat(values.min) | |
maxJewelryDmg += parseFloat(values.min) | |
when "Damage_Weapon_Bonus_Delta#Physical" | |
deltaPhysBonusDmg += parseFloat(values.min) | |
when "Damage_Weapon_Percent_Bonus#Physical" | |
dmgPercent += parseFloat(values.min) | |
when "Attacks_Per_Second_Item_Bonus" | |
aps += parseFloat(values.min) | |
when "Hitpoints_On_Hit" | |
loh += parseFloat(values.min) | |
when "Damage_Percent_Bonus_Vs_Elites" | |
eliteBonus += parseFloat(values.min)*100 | |
when "Damage_Percent_Bonus_Vs_Monster_Type#Demon" | |
demonBonus += parseFloat(values.min)*100 | |
when "Steal_Health_Percent" | |
ls += parseFloat(values.min)*100 | |
when "Hitpoints_Regen_Per_Second" | |
lifeRegen += parseFloat(values.min) | |
when "Block_Chance_Item", "Block_Chance_Bonus_Item"*100 | |
blockChance += parseFloat(values.min) | |
when "Damage_Percent_Reduction_From_Melee"*100 | |
meleeDR += parseFloat(values.min) | |
when "Damage_Percent_Reduction_From_Elites"*100 | |
eliteDR += parseFloat(values.min) | |
when "Damage_Percent_Reduction_From_Ranged"*100 | |
rangedDR += parseFloat(values.min) | |
when "Block_Amount_Item_Min" | |
blockMin += parseFloat(values.min) | |
when "Block_Amount_Item_Delta" | |
blockMin += parseFloat(values.min) | |
blockMax += parseFloat(values.min) | |
when "Health_Globe_Bonus_Health" | |
globes += parseFloat(values.min) | |
else | |
if name.match(/^Damage_Weapon_Min#/) || name.match(/^Damage_Weapon_Bonus_Min#/) | |
minBonusDmg += parseFloat(values.min) | |
else if name.match(/^Damage_Weapon_Delta#/ || name.match(/^Damage_Weapon_Bonus_Delta#/)) | |
deltaBonusDmg += parseFloat(values.min) | |
minTooltipDmg = (minPhysBonusDmg + minBaseDmg) * (1 + dmgPercent) + minBonusDmg | |
maxTooltipDmg = if (minBaseDmg == 0 && minPhysBonusDmg == 0) then 0 else (Math.max(minPhysBonusDmg + minBaseDmg, minBaseDmg + deltaBaseDmg - 1) + 1 + deltaPhysBonusDmg) * (1 + dmgPercent) + minBonusDmg + deltaBonusDmg | |
{ | |
armor, strength, dexterity, intelligence, vitality, | |
allResist, resists, lifePercent, critDamage, critPercent, | |
ias, elementalPercent, minPhysBonusDmg, deltaPhysBonusDmg, | |
minBaseDmg, deltaBaseDmg, minBonusDmg, minJewelryDmg, maxJewelryDmg, | |
deltaBonusDmg, dmgPercent, aps, minTooltipDmg, maxTooltipDmg, | |
loh, eliteBonus, demonBonus, ls, lifeRegen, blockChance, meleeDR, | |
eliteDR, rangedDR, blockMin, blockMax, globes | |
} | |
@combineRawValues: (arr) -> | |
result = {} | |
for obj in arr | |
for own key, val of obj | |
result[key] ||= {min: 0, max: 0} | |
result[key].min += val.min | |
result[key].max += val.max | |
result | |
class Armor extends Item | |
class Jewelry extends Item | |
displayableAttributes: (attrs, opts={advanced: false}) -> | |
if opts.advanced | |
super | |
else | |
[ | |
[attrs.minJewelryDmg], [attrs.maxJewelryDmg] | |
].concat(super) | |
class Weapon extends Item | |
displayableAttributes: (attrs, opts={advanced: false}) -> | |
if opts.advanced | |
super | |
else | |
[ | |
[@type], [attrs.minTooltipDmg], [attrs.maxTooltipDmg], [attrs.minBonusDmg], [attrs.minBonusDmg + attrs.deltaBonusDmg], | |
].concat(super).concat([ | |
[attrs.aps] | |
]) | |
class Offhand extends Weapon | |
displayableAttributes: (attrs, opts={advanced: false}) -> | |
if opts.advanced | |
super.concat([ | |
[attrs.blockMin], [attrs.blockMax] | |
]) | |
else | |
super | |
class SetBonuses extends Item | |
constructor: (@sheets, @slot, @basicData) -> | |
attributes: -> | |
@_attributes ||= do => | |
Item.parseAttributes(@basicData) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
I don't advise trying to edit this file directly. This was coded using coffeescript. | |
Coffeescript source here: https://gist.github.com/3779098 | |
*/ | |
function onOpen(){}; | |
function populateProfile(){}; | |
function clearUpgradeFields(){}; | |
function clearAllFields(){}; | |
function spreadsheetName(){}; | |
function heroSelectClickHandler(){}; | |
var API, Armor, CellRanges, D3Logger, Hero, Item, ItemSet, Jewelry, Offhand, Profile, SetBonuses, SheetCollection, Skills, Weapon, clearAllFields, clearUpgradeFields, flattenArray, heroSelectClickHandler, onOpen, padArray, populateProfile, spreadsheetName, | |
__hasProp = {}.hasOwnProperty, | |
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, | |
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; | |
flattenArray = function(arr) { | |
if (arr.length === 0) { | |
return []; | |
} | |
return arr.reduce(function(sum, val) { | |
return sum.concat(val); | |
}); | |
}; | |
padArray = function(arr, size, value) { | |
var i, length, newArr; | |
length = Math.abs(size) - arr.length; | |
newArr = [].concat(arr); | |
if (length <= 0) { | |
return newArr; | |
} | |
i = 0; | |
while (i < length) { | |
if (size < 0) { | |
newArr.unshift(value); | |
} else { | |
newArr.push(value); | |
} | |
i++; | |
} | |
return newArr; | |
}; | |
CellRanges = (function() { | |
function CellRanges() {} | |
/* | |
If you moved things around, you'll need to update the | |
cell ranges here. Standard cell range notation. | |
*/ | |
CellRanges.SHEET_VERSION = "O5"; | |
CellRanges.THIS_SHEET = "O7"; | |
CellRanges.MAIN_SHEET = "N5"; | |
CellRanges.ADVANCED_SHEET = "N7"; | |
CellRanges.BATTLE_TAG = "X4"; | |
CellRanges.HERO_ID = "X5"; | |
CellRanges.CLASS_ABBREV = "X6"; | |
CellRanges.LEVEL = "X7"; | |
CellRanges.PARAGON_LEVEL = "X9"; | |
CellRanges.LOCALIZATION = "X10"; | |
CellRanges.LOGGER = "Z1"; | |
CellRanges.HEAD = "B3:B13"; | |
CellRanges.SHOULDERS = "F3:F13"; | |
CellRanges.HANDS = "B16:B26"; | |
CellRanges.BRACERS = "F16:F26"; | |
CellRanges.TORSO = "J16:J26"; | |
CellRanges.LEGS = "B29:B39"; | |
CellRanges.FEET = "F29:F39"; | |
CellRanges.WAIST = "J29:J39"; | |
CellRanges.NECK = "B42:B54"; | |
CellRanges.RIGHTFINGER = "F42:F54"; | |
CellRanges.LEFTFINGER = "J42:J54"; | |
CellRanges.MAINHAND = "B57:B73"; | |
CellRanges.OFFHAND = "F57:F73"; | |
CellRanges.SET = "J3:J13"; | |
CellRanges.HEAD_UPGRADE = "C3:C13"; | |
CellRanges.SHOULDERS_UPGRADE = "G3:G13"; | |
CellRanges.HANDS_UPGRADE = "C16:C26"; | |
CellRanges.BRACERS_UPGRADE = "G16:G26"; | |
CellRanges.TORSO_UPGRADE = "K16:K26"; | |
CellRanges.LEGS_UPGRADE = "C29:C39"; | |
CellRanges.FEET_UPGRADE = "G29:G39"; | |
CellRanges.WAIST_UPGRADE = "K29:K39"; | |
CellRanges.NECK_UPGRADE = "C42:C54"; | |
CellRanges.RIGHTFINGER_UPGRADE = "G42:G54"; | |
CellRanges.LEFTFINGER_UPGRADE = "K42:K54"; | |
CellRanges.MAINHAND_UPGRADE = "C57:C73"; | |
CellRanges.OFFHAND_UPGRADE = "G57:G73"; | |
CellRanges.SET_UPGRADE = "K3:K13"; | |
CellRanges.HEAD_ADVANCED = "B3:B12"; | |
CellRanges.SHOULDERS_ADVANCED = "F3:F12"; | |
CellRanges.HANDS_ADVANCED = "B15:B24"; | |
CellRanges.BRACERS_ADVANCED = "F15:F24"; | |
CellRanges.TORSO_ADVANCED = "J15:J24"; | |
CellRanges.LEGS_ADVANCED = "B27:B36"; | |
CellRanges.FEET_ADVANCED = "F27:F36"; | |
CellRanges.WAIST_ADVANCED = "J27:J36"; | |
CellRanges.NECK_ADVANCED = "B39:B48"; | |
CellRanges.RIGHTFINGER_ADVANCED = "F39:F48"; | |
CellRanges.LEFTFINGER_ADVANCED = "J39:J48"; | |
CellRanges.MAINHAND_ADVANCED = "B51:B60"; | |
CellRanges.OFFHAND_ADVANCED = "F51:F62"; | |
CellRanges.SET_ADVANCED = "J3:J12"; | |
CellRanges.HEAD_ADVANCED_UPGRADE = "C3:C12"; | |
CellRanges.SHOULDERS_ADVANCED_UPGRADE = "G3:G12"; | |
CellRanges.HANDS_ADVANCED_UPGRADE = "C15:C24"; | |
CellRanges.BRACERS_ADVANCED_UPGRADE = "G15:G24"; | |
CellRanges.TORSO_ADVANCED_UPGRADE = "K15:K24"; | |
CellRanges.LEGS_ADVANCED_UPGRADE = "C27:C36"; | |
CellRanges.FEET_ADVANCED_UPGRADE = "G27:G36"; | |
CellRanges.WAIST_ADVANCED_UPGRADE = "K27:K36"; | |
CellRanges.NECK_ADVANCED_UPGRADE = "C39:C48"; | |
CellRanges.RIGHTFINGER_ADVANCED_UPGRADE = "G39:G48"; | |
CellRanges.LEFTFINGER_ADVANCED_UPGRADE = "K39:K48"; | |
CellRanges.MAINHAND_ADVANCED_UPGRADE = "C51:C60"; | |
CellRanges.OFFHAND_ADVANCED_UPGRADE = "G51:G62"; | |
CellRanges.SET_ADVANCED_UPGRADE = "K3:K12"; | |
CellRanges.FOLLOWER_BUFFS = "X12:X16"; | |
CellRanges.PLAYER_BUFFS = "X17:X41"; | |
CellRanges.verticalSize = function(range) { | |
var matches, maxRange, minRange, totalCells; | |
matches = this[range].match(/^[A-Z]+(\d+)\:[A-Z]+(\d+)$/); | |
minRange = parseInt(matches[1]); | |
maxRange = parseInt(matches[2]); | |
return totalCells = maxRange - minRange + 1; | |
}; | |
return CellRanges; | |
})(); | |
onOpen = function() { | |
return SpreadsheetApp.getActiveSpreadsheet().addMenu('Diablo', [ | |
{ | |
name: 'Populate Profile', | |
functionName: 'populateProfile' | |
}, { | |
name: 'Clear Upgrade Fields', | |
functionName: 'clearUpgradeFields' | |
}, { | |
name: 'Clear All', | |
functionName: 'clearAllFields' | |
} | |
]); | |
}; | |
spreadsheetName = function(dummy) { | |
return SpreadsheetApp.getActiveSheet().getName(); | |
}; | |
populateProfile = function() { | |
var profile, sheets; | |
try { | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()); | |
profile = new Profile(sheets); | |
if (profile.battleTag === 'cancel') { | |
return D3Logger.log(sheets.main, "") && false; | |
} | |
profile.populateSheet(); | |
return D3Logger.log(sheets.main, ""); | |
} catch (error) { | |
if (error !== "IGNOREME") { | |
return Browser.msgBox("An error occurred when attempting to perform that action. Error: " + error); | |
} | |
} | |
}; | |
clearUpgradeFields = function() { | |
var sheets, slot, _i, _len, _ref, _results; | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()); | |
_ref = ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"]; | |
_results = []; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
slot = _ref[_i]; | |
sheets.main.getRange(CellRanges["" + (slot.toUpperCase()) + "_UPGRADE"]).setValue(''); | |
_results.push(sheets.advanced.getRange(CellRanges["" + (slot.toUpperCase()) + "_ADVANCED_UPGRADE"]).setValue('')); | |
} | |
return _results; | |
}; | |
clearAllFields = function() { | |
var range, sheets, slot, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _results; | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()); | |
_ref = ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"]; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
slot = _ref[_i]; | |
try { | |
sheets.main.getRange(CellRanges[slot.toUpperCase()]).setValue(''); | |
sheets.main.getRange(CellRanges["" + (slot.toUpperCase()) + "_UPGRADE"]).setValue(''); | |
sheets.advanced.getRange(CellRanges["" + (slot.toUpperCase()) + "_ADVANCED"]).setValue(''); | |
sheets.advanced.getRange(CellRanges["" + (slot.toUpperCase()) + "_ADVANCED_UPGRADE"]).setValue(''); | |
} catch (error) { | |
Browser.msgBox("an error occurred when clearing out the " + slot + " slot: " + error); | |
} | |
} | |
_ref1 = ["battle_tag", "hero_id", "class_abbrev", "level", "paragon_level", "logger"]; | |
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { | |
range = _ref1[_j]; | |
sheets.main.getRange(CellRanges[range.toUpperCase()]).setValue(''); | |
} | |
_ref2 = ["follower_buffs", "player_buffs"]; | |
_results = []; | |
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) { | |
range = _ref2[_k]; | |
_results.push(sheets.main.getRange(CellRanges[range.toUpperCase()]).setValue('N')); | |
} | |
return _results; | |
}; | |
heroSelectClickHandler = function(e) { | |
return Profile.heroSelectClickHandler(e); | |
}; | |
API = (function() { | |
function API() {} | |
API.parse = function(path) { | |
var response; | |
response = UrlFetchApp.fetch("http://" + API.locale + ".battle.net/api/d3/" + path); | |
return JSON.parse(response.getContentText()); | |
}; | |
API.locale = 'us'; | |
return API; | |
})(); | |
D3Logger = (function() { | |
function D3Logger() {} | |
D3Logger.log = function(sheet, text) { | |
/* | |
sheet.getRange(CellRanges.LOGGER).setValue(text) | |
*/ | |
}; | |
return D3Logger; | |
})(); | |
SheetCollection = (function() { | |
function SheetCollection(sheet) { | |
var advancedSheetName, mainSheetName; | |
this.spreadsheet = sheet.getParent(); | |
this.version = sheet.getRange(CellRanges.SHEET_VERSION).getValue(); | |
mainSheetName = sheet.getRange(CellRanges.MAIN_SHEET).getValue(); | |
advancedSheetName = sheet.getRange(CellRanges.ADVANCED_SHEET).getValue(); | |
this.main = this.findByName(mainSheetName); | |
this.advanced = this.findByName(advancedSheetName); | |
} | |
SheetCollection.prototype.findByName = function(name) { | |
var _this = this; | |
this.all || (this.all = (function() { | |
var sheet, sheets, _i, _len, _ref; | |
sheets = {}; | |
_ref = _this.spreadsheet.getSheets(); | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
sheet = _ref[_i]; | |
sheets[sheet.getRange(CellRanges.THIS_SHEET).getValue()] = sheet; | |
} | |
return sheets; | |
})()); | |
return this.all[name]; | |
}; | |
return SheetCollection; | |
})(); | |
Profile = (function() { | |
function Profile(sheets) { | |
var battleTagCell, localeCell; | |
this.sheets = sheets; | |
D3Logger.log(this.sheets.main, "Collecting BattleTag"); | |
battleTagCell = this.sheets.main.getRange(CellRanges.BATTLE_TAG); | |
this.battleTag = battleTagCell.getValue(); | |
if (!(this.battleTag != null) || this.battleTag === "") { | |
this.battleTag = Browser.inputBox("Enter your battletag: Example#1234"); | |
if (this.battleTag === 'cancel') { | |
return false; | |
} | |
} | |
this.battleTag = this.battleTag.split("#").join('-'); | |
battleTagCell.setValue(this.battleTag.split('-').join('#')); | |
localeCell = this.sheets.main.getRange(CellRanges.LOCALIZATION); | |
API.locale = localeCell.getValue(); | |
API.locale = API.locale === '' ? 'us' : API.locale; | |
} | |
Profile.prototype.heroes = function() { | |
var _this = this; | |
return this._heroes || (this._heroes = (function() { | |
var data, hero, _i, _len, _ref, _results; | |
try { | |
D3Logger.log(_this.sheets.main, "Parsing Profile Data"); | |
data = API.parse("profile/" + _this.battleTag + "/"); | |
_ref = data.heroes; | |
_results = []; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
hero = _ref[_i]; | |
_results.push(new Hero(_this.sheets.main, hero)); | |
} | |
return _results; | |
} catch (error) { | |
Browser.msgBox("An error occurred when accessing your profile data. Is your battleTag correct? Error: " + error); | |
throw "IGNOREME"; | |
} | |
})()); | |
}; | |
Profile.prototype.createHeroSelectionInterface = function(options) { | |
var btn, buttons, handler, heroSelect, option, panel, _i, _len; | |
D3Logger.log(this.sheets.main, ""); | |
heroSelect = UiApp.createApplication().setTitle("Select which hero to use"); | |
handler = heroSelect.createServerHandler("heroSelectClickHandler"); | |
panel = heroSelect.createVerticalPanel(); | |
buttons = []; | |
for (_i = 0, _len = options.length; _i < _len; _i++) { | |
option = options[_i]; | |
btn = heroSelect.createButton(option.description).setId(option.id).addClickHandler(handler); | |
buttons.push(btn); | |
panel.add(btn); | |
} | |
heroSelect.add(panel); | |
return this.sheets.spreadsheet.show(heroSelect); | |
}; | |
Profile.prototype.selectedHero = function() { | |
var _this = this; | |
return this._selectedHero || (this._selectedHero = (function() { | |
var hero, heroId, options; | |
heroId = _this.sheets.main.getRange(CellRanges.HERO_ID).getValue(); | |
if (!(heroId != null) || heroId === "") { | |
options = (function() { | |
var _i, _len, _ref, _results; | |
_ref = this.heroes(); | |
_results = []; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
hero = _ref[_i]; | |
_results.push({ | |
id: hero.id, | |
description: "" + hero.name + " <" + (hero.hardcore ? "<span style='color:red'>hardcore</span> " : "") + hero['class'] + "> - Level " + hero.level + " (" + hero.paragonLevel + ")" | |
}); | |
} | |
return _results; | |
}).call(_this); | |
_this.createHeroSelectionInterface(options); | |
return false; | |
} else { | |
D3Logger.log(_this.sheets.main, "Parsing Hero Data"); | |
try { | |
hero = API.parse("profile/" + _this.battleTag + "/hero/" + heroId); | |
_this.sheets.main.getRange(CellRanges.CLASS_ABBREV).setValue(Hero.abbrev(hero['class'])); | |
_this.sheets.main.getRange(CellRanges.LEVEL).setValue(hero.level); | |
_this.sheets.main.getRange(CellRanges.PARAGON_LEVEL).setValue(hero.paragonLevel); | |
return new Hero(_this.sheets, hero); | |
} catch (error) { | |
Browser.msgBox("An error occurred when accessing your hero data. Bnet may be temporarily down or the rate limit for requests has been reached (or something else?). Error: " + error); | |
throw "IGNOREME"; | |
} | |
} | |
})()); | |
}; | |
Profile.prototype.populateItems = function() { | |
var hero, item, slot, _i, _j, _len, _len1, _ref, _ref1; | |
if (!(hero = this.selectedHero())) { | |
return false; | |
} | |
_ref = ["head", "shoulders", "hands", "bracers", "torso", "legs", "feet", "waist", "neck", "rightfinger", "leftfinger", "mainhand", "offhand", "set"]; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
slot = _ref[_i]; | |
this.sheets.main.getRange(CellRanges[slot.toUpperCase()]).setValue(''); | |
this.sheets.advanced.getRange(CellRanges["" + (slot.toUpperCase()) + "_ADVANCED"]).setValue(''); | |
} | |
_ref1 = hero.items(); | |
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { | |
item = _ref1[_j]; | |
item.populateSpreadsheet(this.sheets.main); | |
} | |
ItemSet.populateSpreadsheet(this.sheets); | |
return true; | |
}; | |
Profile.prototype.populateSkills = function() { | |
var hero; | |
try { | |
if (!(hero = this.selectedHero())) { | |
return false; | |
} | |
hero.skills().populateSpreadsheet(this.sheets.main); | |
} catch (error) { | |
if (error !== 'IGNOREME') { | |
Browser.msgBox("An error occurred: " + error); | |
} | |
} | |
return true; | |
}; | |
Profile.prototype.populateSheet = function() { | |
try { | |
if (!this.populateItems()) { | |
return false; | |
} | |
if (!this.populateSkills()) { | |
return false; | |
} | |
} catch (error) { | |
if (error !== 'IGNOREME') { | |
Browser.msgBox("An error occurred: " + error); | |
} | |
} | |
return true; | |
}; | |
Profile.heroSelectClickHandler = function(e) { | |
var heroId, profile, sheets; | |
heroId = e.parameter.source; | |
sheets = new SheetCollection(SpreadsheetApp.getActiveSheet()); | |
sheets.main.getRange(CellRanges.HERO_ID).setValue(heroId); | |
profile = new Profile(sheets); | |
profile.populateSheet(); | |
return UiApp.getActiveApplication().close(); | |
}; | |
return Profile; | |
})(); | |
Hero = (function() { | |
Hero.ABBREV_MAP = { | |
'wizard': 'W', | |
'barbarian': 'B', | |
'witch-doctor': 'WD', | |
'demon-hunter': 'DH', | |
'monk': 'M' | |
}; | |
function Hero(sheets, data) { | |
var _ref; | |
this.sheets = sheets; | |
this.data = data; | |
_ref = this.data, this.name = _ref.name, this.id = _ref.id, this.level = _ref.level, this.paragonLevel = _ref.paragonLevel, this["class"] = _ref["class"], this.hardcore = _ref.hardcore; | |
this.resists = { | |
Fire: 0, | |
Lightning: 0, | |
Poison: 0, | |
Physical: 0, | |
Arcane: 0, | |
Cold: 0 | |
}; | |
} | |
Hero.prototype.items = function() { | |
var _this = this; | |
return this._items || (this._items = (function() { | |
var i, item, itemKlass, items, maxResistType, maxResistValue, slot, type, val, _i, _len, _ref, _ref1, _ref2; | |
items = []; | |
_ref = _this.data.items; | |
for (slot in _ref) { | |
if (!__hasProp.call(_ref, slot)) continue; | |
item = _ref[slot]; | |
itemKlass = Item.klassForSlot(slot); | |
i = new itemKlass(_this.sheets, slot, item); | |
if (_this.oneWithEverything()) { | |
_ref1 = _this.resists; | |
for (type in _ref1) { | |
if (!__hasProp.call(_ref1, type)) continue; | |
_this.resists[type] += i.attributes().resists[type]; | |
} | |
} | |
items.push(i); | |
} | |
if (_this.oneWithEverything()) { | |
maxResistValue = 0; | |
_ref2 = _this.resists; | |
for (type in _ref2) { | |
if (!__hasProp.call(_ref2, type)) continue; | |
val = _ref2[type]; | |
if (val >= maxResistValue) { | |
maxResistType = type; | |
maxResistValue = val; | |
} | |
} | |
for (_i = 0, _len = items.length; _i < _len; _i++) { | |
item = items[_i]; | |
item.attributes().allResist += item.attributes().resists[maxResistType]; | |
} | |
} | |
return items; | |
})()); | |
}; | |
Hero.prototype.oneWithEverything = function() { | |
return this["class"] === 'monk' && this.skills().has('One With Everything')[0] === 'Y'; | |
}; | |
Hero.prototype.skills = function() { | |
var _this = this; | |
return this._skills || (this._skills = (function() { | |
return new Skills(_this.sheets, _this["class"], _this.data.skills.passive.concat(_this.data.skills.active)); | |
})()); | |
}; | |
Hero.abbrev = function(name) { | |
return Hero.ABBREV_MAP[name]; | |
}; | |
return Hero; | |
})(); | |
Skills = (function() { | |
function Skills(sheets, klass, data) { | |
var skill, _i, _len, _ref; | |
this.sheets = sheets; | |
this.klass = klass; | |
this.data = data; | |
this.names = []; | |
_ref = this.data; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
skill = _ref[_i]; | |
if (skill.skill != null) { | |
this.names.push(skill.skill.name); | |
} | |
if (skill.rune != null) { | |
this.names.push(skill.rune.name); | |
} | |
} | |
} | |
Skills.prototype.populateSpreadsheet = function() { | |
return this.sheets.main.getRange(CellRanges.PLAYER_BUFFS).setValues(this.displayableSkills()); | |
}; | |
Skills.prototype.has = function(name) { | |
if (__indexOf.call(this.names, name) >= 0) { | |
return ['Y']; | |
} else { | |
return ['N']; | |
} | |
}; | |
Skills.prototype.displayableSkills = function() { | |
var skills, totalCells; | |
skills = (function() { | |
switch (this.klass) { | |
case "wizard": | |
return [[""], this.has('Energy Armor'), this.has('Pinpoint Barrier'), this.has('Prismatic Armor'), this.has('Familiar'), this.has('Sparkflint'), this.has('Vigoron'), this.has('Magic Weapon'), this.has('Force Weapon'), this.has('Blood Magic'), this.has('Frost Nova'), this.has('Deep Freeze'), this.has('Bone Chill'), this.has('Ice Armor'), this.has('Crystallize'), this.has('Slow Time'), this.has('Time Warp'), this.has('Stretch Time'), this.has('Safe Passage'), [""], this.has('Blur'), this.has('Glass Cannon'), this.has("Galvanizing Ward")]; | |
case 'demon-hunter': | |
return [[""], this.has('Boar Companion'), this.has('Sentry'), this.has('Aid Station'), this.has('Guardian Turret'), this.has('Bait the Trap'), this.has('Hardened'), this.has('Shadow Power'), this.has('Gloom'), [""], this.has('Steady Aim'), this.has('Archery'), this.has('Sharpshooter'), this.has('Perfectionist'), this.has('Brooding'), this.has('Cull the Weak')]; | |
case 'barbarian': | |
return [[""], this.has('War Cry'), this.has('Hardened Wrath'), this.has('Invigorate'), this.has("Veteran's Warning"), this.has("Impunity"), this.has("Battle Rage"), this.has("Marauder's Rage"), this.has('Punish'), this.has('Overpower'), this.has('Crushing Advance'), this.has('Killing Spree'), this.has('Best Served Cold'), this.has('Threatening Shout'), this.has('Falter'), [""], this.has("Ruthless"), this.has("Nerves of Steel"), this.has("Weapons Master"), this.has("Superstition"), this.has("Tough as Nails"), this.has('Bloodthirst'), this.has('Inspiring Presence')]; | |
case 'monk': | |
return [[""], this.has('Deadly Reach'), this.has('Foresight'), this.has('Keen Eye'), this.has('Blazing Wrath'), this.has("Earth Ally"), this.has("Mantra of Evasion"), this.has("Hard Target"), this.has("Transgression"), this.has("Mantra of Healing"), this.has("Heavenly Body"), this.has("Time of Need"), this.has("Sustenance"), this.has("Boon of Inspiration"), this.has("Mantra of Conviction"), this.has("Overawe"), this.has("Intimidation"), this.has("Reclamation"), this.has("Concussion"), this.has("Lightning Flash"), [""], this.has("Resolve"), this.has("Seize the Initiative"), this.has("The Guardian's Path"), this.has("Sixth Sense")]; | |
case 'witch-doctor': | |
return [[""], this.has("Life Link"), this.has("Provoke the Pack"), this.has("Soul Harvest"), [""], this.has("Jungle Fortitude"), this.has("Pierce the Veil"), this.has("Blood Ritual"), this.has("Zombie Handler")]; | |
default: | |
throw "Unrecognized class: " + this.klass; | |
} | |
}).call(this); | |
totalCells = CellRanges.verticalSize('PLAYER_BUFFS'); | |
return padArray(skills, totalCells, ['N']); | |
}; | |
return Skills; | |
})(); | |
ItemSet = (function() { | |
ItemSet.recordedSets = {}; | |
function ItemSet(data) { | |
this.data = data; | |
this.quantity = 0; | |
this.ranks = this.data.ranks; | |
} | |
ItemSet.prototype.attributeStrings = function() { | |
var rank; | |
return this._attrs || (this._attrs = flattenArray((function() { | |
var _i, _len, _ref, _results; | |
_ref = this.ranks; | |
_results = []; | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
rank = _ref[_i]; | |
if (parseInt(rank.required) <= this.quantity) { | |
_results.push(rank.attributes); | |
} | |
} | |
return _results; | |
}).call(this))); | |
}; | |
ItemSet.prototype.attributesRaw = function() { | |
return this._attributesRaw || (this._attributesRaw = Item.parseAttributesRaw(this.attributeStrings())); | |
}; | |
ItemSet.attributesRaw = function() { | |
var set, slug; | |
return this._attributesRaw || (this._attributesRaw = Item.combineRawValues((function() { | |
var _ref, _results; | |
_ref = this.recordedSets; | |
_results = []; | |
for (slug in _ref) { | |
if (!__hasProp.call(_ref, slug)) continue; | |
set = _ref[slug]; | |
if (set.attributeStrings().length > 0) { | |
_results.push(set.attributesRaw()); | |
} | |
} | |
return _results; | |
}).call(this))); | |
}; | |
ItemSet.record = function(data) { | |
var _base, _name; | |
(_base = this.recordedSets)[_name = data.slug] || (_base[_name] = new ItemSet(data)); | |
this.recordedSets[data.slug].quantity += 1; | |
return this.recordedSets[data.slug]; | |
}; | |
ItemSet.populateSpreadsheet = function(sheets) { | |
(new SetBonuses(sheets, 'set', { | |
attributesRaw: this.attributesRaw() | |
})).populateSpreadsheet(); | |
return true; | |
}; | |
return ItemSet; | |
})(); | |
Item = (function() { | |
function Item(sheets, slot, basicData) { | |
var _ref; | |
this.sheets = sheets; | |
this.slot = slot; | |
this.basicData = basicData; | |
_ref = [this.basicData.name, this.basicData.tooltipParams], this.name = _ref[0], this.itemId = _ref[1]; | |
} | |
Item.prototype.attributes = function() { | |
var _this = this; | |
return this._attributes || (this._attributes = (function() { | |
var data; | |
D3Logger.log(_this.sheets.main, "Parsing item: " + _this.slot); | |
try { | |
data = API.parse("data/" + _this.itemId); | |
_this.type = Item.parseType(data.type.id); | |
if (data.set != null) { | |
_this.set = ItemSet.record(data.set); | |
} | |
return Item.parseAttributes(data); | |
} catch (error) { | |
Browser.msgBox("An error occurred when attempting to access item properties for the " + _this.slot + " slot. Error: " + error); | |
throw "IGNOREME"; | |
} | |
})()); | |
}; | |
Item.prototype.populateSpreadsheet = function() { | |
var advancedRange, mainRange; | |
mainRange = this.sheets.main.getRange(CellRanges[this.slot.toUpperCase()]); | |
advancedRange = this.sheets.advanced.getRange(CellRanges["" + (this.slot.toUpperCase()) + "_ADVANCED"]); | |
mainRange.setValues(this.displayableAttributes(this.attributes())); | |
return advancedRange.setValues(this.displayableAttributes(this.attributes(), { | |
advanced: true | |
})); | |
}; | |
Item.prototype.displayableAttributes = function(attrs, options) { | |
var _ref; | |
if (options == null) { | |
options = {}; | |
} | |
if ((_ref = options.advanced) == null) { | |
options.advanced = false; | |
} | |
if (options.advanced) { | |
return [[attrs.loh], [attrs.ls], [attrs.lifeRegen], [attrs.globes], [attrs.blockChance], [attrs.meleeDR], [attrs.rangedDR], [attrs.eliteDR], [attrs.eliteBonus], [attrs.demonBonus]]; | |
} else { | |
return [[attrs.armor], [attrs.strength], [attrs.dexterity], [attrs.intelligence], [attrs.vitality], [attrs.allResist], [attrs.lifePercent], [attrs.critDamage], [attrs.critPercent], [attrs.ias], [attrs.elementalPercent]]; | |
} | |
}; | |
Item.parseType = function(type) { | |
switch (type) { | |
case "CeremonialDagger": | |
return "WD Knife"; | |
case "FistWeapon": | |
return "Fist"; | |
case "MightyWeapon1H": | |
return "Mighty"; | |
case "MightyWeapon2H": | |
return "2H Mighty"; | |
case "Axe2H": | |
return "2H Axe"; | |
case "Mace2H": | |
return "2H Mace"; | |
case "Sword2H": | |
return "2H Sword"; | |
case "HandXBow": | |
return "Hand Crossbow"; | |
case "Orb": | |
return "W Source"; | |
case "Mojo": | |
return "WD Mojo"; | |
case "Quiver": | |
return "DH Quiver"; | |
default: | |
return type; | |
} | |
}; | |
Item.klassForSlot = function(slot) { | |
switch (slot) { | |
case "rightFinger": | |
case "leftFinger": | |
case "neck": | |
return Jewelry; | |
case "mainHand": | |
return Weapon; | |
case "offHand": | |
return Offhand; | |
default: | |
return Armor; | |
} | |
}; | |
Item.parseAttributesRaw = function(rawData) { | |
var attributes, match, name, rawStr, val, _i, _len, _ref, _ref1, _ref10, _ref11, _ref12, _ref2, _ref3, _ref4, _ref5, _ref6, _ref7, _ref8, _ref9; | |
attributes = {}; | |
for (_i = 0, _len = rawData.length; _i < _len; _i++) { | |
rawStr = rawData[_i]; | |
if ((match = rawStr.match(/^\+(\d+) (Dexterity|Intelligence|Strength|Vitality)$/))) { | |
name = "" + match[2] + "_Item"; | |
val = ((_ref = attributes[name]) != null ? _ref.min : void 0) || 0; | |
val += parseInt(match[1]); | |
} else if ((match = rawStr.match(/^\+(\d+) Resistance to All Elements$/))) { | |
name = "Resistance_All"; | |
val = ((_ref1 = attributes[name]) != null ? _ref1.min : void 0) || 0; | |
val += parseInt(match[1]); | |
} else if ((match = rawStr.match(/^Critical Hit Chance Increased by (\d+\.\d+)%$/))) { | |
name = "Crit_Percent_Bonus_Capped"; | |
val = ((_ref2 = attributes[name]) != null ? _ref2.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Attack Speed Increased by (\d+)%$/))) { | |
name = "Attacks_Per_Second_Item_Percent"; | |
val = ((_ref3 = attributes[name]) != null ? _ref3.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^\+(\d+)% Life$/))) { | |
name = "Hitpoints_Max_Percent_Bonus_Item"; | |
val = ((_ref4 = attributes[name]) != null ? _ref4.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Critical Hit Damage Increased by (\d+)%$/))) { | |
name = "Crit_Damage_Percent"; | |
val = ((_ref5 = attributes[name]) != null ? _ref5.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Reduces damage from melee attacks by (\d+)%\.?$/))) { | |
name = "Damage_Percent_Reduction_From_Melee"; | |
val = ((_ref6 = attributes[name]) != null ? _ref6.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Reduces damage from ranged attacks by (\d+)%\.?$/))) { | |
name = "Damage_Percent_Reduction_From_Ranged"; | |
val += ((_ref7 = attributes[name]) != null ? _ref7.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Increases Damage Against Elites by (\d+)%$/))) { | |
name = "Damage_Percent_Bonus_Vs_Elites"; | |
val = ((_ref8 = attributes[name]) != null ? _ref8.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^\+(\d+)% Damage to Demons$/))) { | |
name = "Damage_Percent_Bonus_Vs_Monster_Type#Demon"; | |
val = ((_ref9 = attributes[name]) != null ? _ref9.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Reduces damage from elites by (\d+)%\.?$/))) { | |
name = "Damage_Percent_Reduction_From_Elites"; | |
val += ((_ref10 = attributes[name]) != null ? _ref10.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^Regenerates (\d+) Life per Second$/))) { | |
name = "Hitpoints_Regen_Per_Second"; | |
val += ((_ref11 = attributes[name]) != null ? _ref11.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} else if ((match = rawStr.match(/^(\d+\.\d+)% of Damage Dealt Is Converted to Life$/))) { | |
name = "Steal_Health_Percent"; | |
val += ((_ref12 = attributes[name]) != null ? _ref12.min : void 0) || 0; | |
val += parseFloat(match[1]) / 100; | |
} | |
attributes[name] = { | |
min: val, | |
max: val | |
}; | |
} | |
return attributes; | |
}; | |
Item.parseAttributes = function(data) { | |
var allResist, aps, armor, attributes, blockChance, blockMax, blockMin, critDamage, critPercent, deltaBaseDmg, deltaBonusDmg, deltaPhysBonusDmg, demonBonus, dexterity, dmgPercent, elementalPercent, eliteBonus, eliteDR, gem, globes, ias, intelligence, lifePercent, lifeRegen, loh, ls, maxJewelryDmg, maxTooltipDmg, meleeDR, minBaseDmg, minBonusDmg, minJewelryDmg, minPhysBonusDmg, minTooltipDmg, name, rangedDR, resists, strength, values, vitality, _i, _len, _ref; | |
armor = 0; | |
strength = 0; | |
dexterity = 0; | |
intelligence = 0; | |
vitality = 0; | |
allResist = 0; | |
resists = { | |
Fire: 0, | |
Lightning: 0, | |
Poison: 0, | |
Physical: 0, | |
Arcane: 0, | |
Cold: 0 | |
}; | |
lifePercent = 0; | |
critDamage = 0; | |
critPercent = 0; | |
ias = 0; | |
elementalPercent = 0; | |
minBaseDmg = 0; | |
minBonusDmg = 0; | |
minPhysBonusDmg = 0; | |
minJewelryDmg = 0; | |
maxJewelryDmg = 0; | |
deltaBonusDmg = 0; | |
deltaBaseDmg = 0; | |
deltaPhysBonusDmg = 0; | |
dmgPercent = 0; | |
aps = 0; | |
loh = 0; | |
ls = 0; | |
lifeRegen = 0; | |
blockChance = 0; | |
meleeDR = 0; | |
rangedDR = 0; | |
eliteDR = 0; | |
eliteBonus = 0; | |
demonBonus = 0; | |
blockMin = 0; | |
blockMax = 0; | |
globes = 0; | |
_ref = [data.attributesRaw].concat(data.gems ? (function() { | |
var _j, _len, _ref, _results; | |
_ref = data.gems; | |
_results = []; | |
for (_j = 0, _len = _ref.length; _j < _len; _j++) { | |
gem = _ref[_j]; | |
_results.push(gem.attributesRaw); | |
} | |
return _results; | |
})() : []); | |
for (_i = 0, _len = _ref.length; _i < _len; _i++) { | |
attributes = _ref[_i]; | |
for (name in attributes) { | |
if (!__hasProp.call(attributes, name)) continue; | |
values = attributes[name]; | |
switch (name) { | |
case "Armor_Item": | |
armor += parseFloat(values.min); | |
break; | |
case "Armor_Bonus_Item": | |
armor += parseFloat(values.min); | |
break; | |
case "Strength_Item": | |
strength += parseFloat(values.min); | |
break; | |
case "Dexterity_Item": | |
dexterity += parseFloat(values.min); | |
break; | |
case "Intelligence_Item": | |
intelligence += parseFloat(values.min); | |
break; | |
case "Vitality_Item": | |
vitality += parseFloat(values.min); | |
break; | |
case "Resistance_All": | |
allResist += parseFloat(values.min); | |
break; | |
case "Resistance#Fire": | |
case "Resistance#Lightning": | |
case "Resistance#Poison": | |
case "Resistance#Physical": | |
case "Resistance#Arcane": | |
case "Resistance#Cold": | |
resists[name.split('#')[1]] += parseFloat(values.min); | |
break; | |
case "Hitpoints_Max_Percent_Bonus_Item": | |
lifePercent += parseFloat(values.min) * 100; | |
break; | |
case "Crit_Damage_Percent": | |
critDamage += parseFloat(values.min) * 100; | |
break; | |
case "Crit_Percent_Bonus_Capped": | |
critPercent += parseFloat(values.min) * 100; | |
break; | |
case "Attacks_Per_Second_Percent": | |
case "Attacks_Per_Second_Item_Percent": | |
ias += parseFloat(values.min) * 100; | |
break; | |
case "Damage_Type_Percent_Bonus#Fire": | |
case "Damage_Type_Percent_Bonus#Lightning": | |
case "Damage_Type_Percent_Bonus#Poison": | |
case "Damage_Type_Percent_Bonus#Arcane": | |
case "Damage_Type_Percent_Bonus#Cold": | |
elementalPercent += parseFloat(values.min) * 100; | |
break; | |
case "Damage_Weapon_Min#Physical": | |
case "Damage_Min#Physical": | |
minBaseDmg += parseFloat(values.min); | |
minJewelryDmg += parseFloat(values.min); | |
maxJewelryDmg += parseFloat(values.min); | |
break; | |
case "Damage_Bonus_Min#Physical": | |
minBaseDmg += parseFloat(values.min); | |
minJewelryDmg += parseFloat(values.min); | |
break; | |
case "Damage_Weapon_Bonus_Min#Physical": | |
minPhysBonusDmg += parseFloat(values.min); | |
break; | |
case "Damage_Weapon_Delta#Physical": | |
case "Damage_Delta#Physical": | |
deltaBaseDmg += parseFloat(values.min); | |
maxJewelryDmg += parseFloat(values.min); | |
break; | |
case "Damage_Weapon_Bonus_Delta#Physical": | |
deltaPhysBonusDmg += parseFloat(values.min); | |
break; | |
case "Damage_Weapon_Percent_Bonus#Physical": | |
dmgPercent += parseFloat(values.min); | |
break; | |
case "Attacks_Per_Second_Item_Bonus": | |
aps += parseFloat(values.min); | |
break; | |
case "Hitpoints_On_Hit": | |
loh += parseFloat(values.min); | |
break; | |
case "Damage_Percent_Bonus_Vs_Elites": | |
eliteBonus += parseFloat(values.min) * 100; | |
break; | |
case "Damage_Percent_Bonus_Vs_Monster_Type#Demon": | |
demonBonus += parseFloat(values.min) * 100; | |
break; | |
case "Steal_Health_Percent": | |
ls += parseFloat(values.min) * 100; | |
break; | |
case "Hitpoints_Regen_Per_Second": | |
lifeRegen += parseFloat(values.min); | |
break; | |
case "Block_Chance_Item": | |
case "Block_Chance_Bonus_Item" * 100: | |
blockChance += parseFloat(values.min); | |
break; | |
case "Damage_Percent_Reduction_From_Melee" * 100: | |
meleeDR += parseFloat(values.min); | |
break; | |
case "Damage_Percent_Reduction_From_Elites" * 100: | |
eliteDR += parseFloat(values.min); | |
break; | |
case "Damage_Percent_Reduction_From_Ranged" * 100: | |
rangedDR += parseFloat(values.min); | |
break; | |
case "Block_Amount_Item_Min": | |
blockMin += parseFloat(values.min); | |
break; | |
case "Block_Amount_Item_Delta": | |
blockMin += parseFloat(values.min); | |
blockMax += parseFloat(values.min); | |
break; | |
case "Health_Globe_Bonus_Health": | |
globes += parseFloat(values.min); | |
break; | |
default: | |
if (name.match(/^Damage_Weapon_Min#/) || name.match(/^Damage_Weapon_Bonus_Min#/)) { | |
minBonusDmg += parseFloat(values.min); | |
} else if (name.match(/^Damage_Weapon_Delta#/ || name.match(/^Damage_Weapon_Bonus_Delta#/))) { | |
deltaBonusDmg += parseFloat(values.min); | |
} | |
} | |
} | |
} | |
minTooltipDmg = (minPhysBonusDmg + minBaseDmg) * (1 + dmgPercent) + minBonusDmg; | |
maxTooltipDmg = minBaseDmg === 0 && minPhysBonusDmg === 0 ? 0 : (Math.max(minPhysBonusDmg + minBaseDmg, minBaseDmg + deltaBaseDmg - 1) + 1 + deltaPhysBonusDmg) * (1 + dmgPercent) + minBonusDmg + deltaBonusDmg; | |
return { | |
armor: armor, | |
strength: strength, | |
dexterity: dexterity, | |
intelligence: intelligence, | |
vitality: vitality, | |
allResist: allResist, | |
resists: resists, | |
lifePercent: lifePercent, | |
critDamage: critDamage, | |
critPercent: critPercent, | |
ias: ias, | |
elementalPercent: elementalPercent, | |
minPhysBonusDmg: minPhysBonusDmg, | |
deltaPhysBonusDmg: deltaPhysBonusDmg, | |
minBaseDmg: minBaseDmg, | |
deltaBaseDmg: deltaBaseDmg, | |
minBonusDmg: minBonusDmg, | |
minJewelryDmg: minJewelryDmg, | |
maxJewelryDmg: maxJewelryDmg, | |
deltaBonusDmg: deltaBonusDmg, | |
dmgPercent: dmgPercent, | |
aps: aps, | |
minTooltipDmg: minTooltipDmg, | |
maxTooltipDmg: maxTooltipDmg, | |
loh: loh, | |
eliteBonus: eliteBonus, | |
demonBonus: demonBonus, | |
ls: ls, | |
lifeRegen: lifeRegen, | |
blockChance: blockChance, | |
meleeDR: meleeDR, | |
eliteDR: eliteDR, | |
rangedDR: rangedDR, | |
blockMin: blockMin, | |
blockMax: blockMax, | |
globes: globes | |
}; | |
}; | |
Item.combineRawValues = function(arr) { | |
var key, obj, result, val, _i, _len; | |
result = {}; | |
for (_i = 0, _len = arr.length; _i < _len; _i++) { | |
obj = arr[_i]; | |
for (key in obj) { | |
if (!__hasProp.call(obj, key)) continue; | |
val = obj[key]; | |
result[key] || (result[key] = { | |
min: 0, | |
max: 0 | |
}); | |
result[key].min += val.min; | |
result[key].max += val.max; | |
} | |
} | |
return result; | |
}; | |
return Item; | |
})(); | |
Armor = (function(_super) { | |
__extends(Armor, _super); | |
function Armor() { | |
return Armor.__super__.constructor.apply(this, arguments); | |
} | |
return Armor; | |
})(Item); | |
Jewelry = (function(_super) { | |
__extends(Jewelry, _super); | |
function Jewelry() { | |
return Jewelry.__super__.constructor.apply(this, arguments); | |
} | |
Jewelry.prototype.displayableAttributes = function(attrs, opts) { | |
if (opts == null) { | |
opts = { | |
advanced: false | |
}; | |
} | |
if (opts.advanced) { | |
return Jewelry.__super__.displayableAttributes.apply(this, arguments); | |
} else { | |
return [[attrs.minJewelryDmg], [attrs.maxJewelryDmg]].concat(Jewelry.__super__.displayableAttributes.apply(this, arguments)); | |
} | |
}; | |
return Jewelry; | |
})(Item); | |
Weapon = (function(_super) { | |
__extends(Weapon, _super); | |
function Weapon() { | |
return Weapon.__super__.constructor.apply(this, arguments); | |
} | |
Weapon.prototype.displayableAttributes = function(attrs, opts) { | |
if (opts == null) { | |
opts = { | |
advanced: false | |
}; | |
} | |
if (opts.advanced) { | |
return Weapon.__super__.displayableAttributes.apply(this, arguments); | |
} else { | |
return [[this.type], [attrs.minTooltipDmg], [attrs.maxTooltipDmg], [attrs.minBonusDmg], [attrs.minBonusDmg + attrs.deltaBonusDmg]].concat(Weapon.__super__.displayableAttributes.apply(this, arguments)).concat([[attrs.aps]]); | |
} | |
}; | |
return Weapon; | |
})(Item); | |
Offhand = (function(_super) { | |
__extends(Offhand, _super); | |
function Offhand() { | |
return Offhand.__super__.constructor.apply(this, arguments); | |
} | |
Offhand.prototype.displayableAttributes = function(attrs, opts) { | |
if (opts == null) { | |
opts = { | |
advanced: false | |
}; | |
} | |
if (opts.advanced) { | |
return Offhand.__super__.displayableAttributes.apply(this, arguments).concat([[attrs.blockMin], [attrs.blockMax]]); | |
} else { | |
return Offhand.__super__.displayableAttributes.apply(this, arguments); | |
} | |
}; | |
return Offhand; | |
})(Weapon); | |
SetBonuses = (function(_super) { | |
__extends(SetBonuses, _super); | |
function SetBonuses(sheets, slot, basicData) { | |
this.sheets = sheets; | |
this.slot = slot; | |
this.basicData = basicData; | |
} | |
SetBonuses.prototype.attributes = function() { | |
var _this = this; | |
return this._attributes || (this._attributes = (function() { | |
return Item.parseAttributes(_this.basicData); | |
})()); | |
}; | |
return SetBonuses; | |
})(Item); |
Set bonuses work now but it parses the tooltip string to get the values: aka, localization is still an issue so only english-locale set bonuses are working at the moment.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The set bonus stats don't come in an easily-parseable format like gems and affixes do :( Might have to hardcode all the set bonuses in to get those to work. ugh. Maybe it's acceptable not to, not too hard to just add your own set bonuses up.