Skip to content

Instantly share code, notes, and snippets.

@AlexanderFabisch
Created June 5, 2013 10:34
Show Gist options
  • Save AlexanderFabisch/5713016 to your computer and use it in GitHub Desktop.
Save AlexanderFabisch/5713016 to your computer and use it in GitHub Desktop.
Compare PVP scores of characters in Diablo 3.
require 'rubygems'
require 'diablo3-api-client'
# TODO
# fury / barb attributes
# block
# edp only for dh
# efp for barbs?
# monks
# wizards
# witch doctors
class Set
def initialize set
@name = set["name"]
@bonuses = {}
set["ranks"].each do |rank|
@bonuses[rank["required"]] = rank["attributes"]
end
@items = {}
set["items"].each do |item|
@items[item["name"]] = false
end
end
def add_item name
@items[name] = true
end
def attributes
attr = []
items = @items.values.count true
@bonuses.each do |n, a|
attr += a if n <= items
end
attr
end
end
class Weapon
attr_accessor :wtype, :dps, :min, :max, :aps, :ed, :elemin, :elemax
def initialize wtype, dps, min, max, aps, ed, elemin, elemax
@wtype = wtype
@dps = dps || 0.0
@min = min || 0.0
@max = max || 0.0
@aps = aps || 0.0
@ed = ed || 0.0
@elemin = elemin || 0.0
@elemax = elemax || 0.0
end
end
def parse_items hero
mainhand = nil
offhand = nil
sets = {}
items = []
for item in hero.items
attr = {}
weapon = true
is_offhand = item.class.scope == "offHand"
unless item.set.nil?
sets[item.set["name"]] = Set.new item.set if sets[item.set["name"]].nil?
sets[item.set["name"]].add_item item.name
end
wtype = ""
case item.type["id"]
when /(Bow|Crossbow|HandXbow|Sword|MightyWeapon1H|Mace|Axe|Axe2H|Dagger|Sword|Polearm|Spear|FistWeapon|Wand)/
wtype = item.type["id"]
when /(Shield|Helm|ChestArmor|Boots|Gloves|Shoulders|Legs|Bracers|GenericBelt|Ring|Amulet|Belt|Belt_Barbarian|Cloak|Quiver|SpiritStone_Monk|Orb|WizardHat|VoodooMask|Mojo)/
weapon = false
else
puts "WARNING: unknown wtype " + item.type["id"]
end
item.attributes_raw.each do |name, attribute|
parse_raw_attribute name, attribute, attr, weapon, is_offhand
end
item.attributes.each do |attribute|
parse_attribute attribute, attr, weapon, is_offhand
end
unless attr["delta"].nil?
init_attr attr, "max"
attr["max"] += attr["delta"]
attr["delta"] = nil
end
#puts item.name
#puts item.type["id"]
#puts item.attributes_raw # Damage_Bonus_Min#Physical, Damage_Min#Physical, Damage_Delta#Physical
if weapon
baps = if attr["baps"].nil? then 0.0 else attr["baps"] end
unless is_offhand
mainhand = Weapon.new wtype, item.dps["min"], item.min_damage["min"], item.max_damage["min"], item.attacks_per_second["min"]-baps, attr["ed"], attr["elemin"], attr["elemax"]
else
offhand = Weapon.new wtype, item.dps["min"], item.min_damage["min"], item.max_damage["min"], item.attacks_per_second["min"]-baps, attr["ed"], attr["elemin"], attr["elemax"]
end
end
for gem in item.gems
gem["attributesRaw"].each do |name, attribute|
parse_raw_attribute name, attribute, attr, weapon, is_offhand
end
gem["attributes"].each do |attribute|
parse_attribute attribute, attr
end
end
items << attr
end
for name, set in sets
set.attributes.each do |attribute|
parse_attribute attribute, attr, false, false, true
end
end
[items, mainhand, offhand]
end
def parse_raw_attribute name, attribute, attr, weapon=false, offhand=false
case name
when "Movement_Scalar"
init_attr attr, "ms"
attr["ms"] += attribute["min"].to_f * 100.0
when /(Dexterity|Intelligence|Strength)_Item/
stat = $1.downcase[0...3]
init_attr attr, stat
attr[stat] += attribute["min"].to_f
when "Vitality_Item"
init_attr attr, "vita"
attr["vita"] += attribute["min"].to_f
when "Hitpoints_Max_Percent_Bonus_Item"
init_attr attr, "life"
attr["life"] += attribute["min"].to_f
when "Resistance_All"
init_attr attr, "allres"
attr["allres"] += attribute["min"].to_f
when /Resistance#(Physical|Poison|Fire|Cold|Arcane|Lightning)/
res = $1.downcase[0...2]
init_attr attr, res
attr[res + "res"] = attribute["min"].to_f
when "Resource_Max_Bonus#Discipline"
init_attr attr, "disc"
attr["disc"] += attribute["min"].to_f
when "Damage_Bonus_Min#Physical"
init_attr attr, "min"
attr["min"] += attribute["min"].to_f
when "Damage_Min#Physical"
init_attr attr, "min2"
attr["min2"] += attribute["min"].to_f
when "Damage_Delta#Physical"
init_attr attr, "delta"
attr["delta"] += attribute["min"].to_f
when "Attacks_Per_Second_Item_Percent"
init_attr attr, "wias"
attr["wias"] += attribute["min"].to_f
when "Attacks_Per_Second_Percent"
init_attr attr, "ias"
attr["ias"] += attribute["min"].to_f
when "Damage_Weapon_Percent_Bonus#Physical"
init_attr attr, "ed"
attr["ed"] += attribute["min"].to_f
when "Crit_Damage_Percent"
init_attr attr, "cd"
attr["cd"] += attribute["min"].to_f
when "Crit_Percent_Bonus_Capped"
init_attr attr, "cc"
attr["cc"] += attribute["min"].to_f
when "Armor_Item", "Armor_Bonus_Item"
init_attr attr, "armor"
attr["armor"] += attribute["min"].to_f
when "Gold_PickUp_Radius"
init_attr attr, "pickup"
attr["pickup"] += attribute["min"].to_f
when "Experience_Bonus_Percent"
init_attr attr, "exp"
attr["exp"] += attribute["min"].to_f
when "Damage_Percent_Reduction_From_Melee"
init_attr attr, "meeleedr"
attr["meeleedr"] += attribute["min"].to_f
when "Power_Damage_Percent_Bonus#DemonHunter_HungeringArrow"
init_attr attr, "hungering_arrow"
attr["hungering_arrow"] += attribute["min"].to_f
when "Damage_Percent_Bonus_Vs_Elites"
init_attr attr, "dmg_vs_elites"
attr["dmg_vs_elites"] += attribute["min"].to_f
when "Sockets"
when "Item_Indestructible"
when "Durability_Cur"
when "Durability_Max"
when "Requirement_When_Equipped#BowAny"
else
#puts "#{name} => #{attribute}"
end
if not attr["min2"].nil? and not attr["delta"].nil?
init_attr attr, "min"
init_attr attr, "max"
attr["min"] += attr["min2"]
attr["max"] += attr["min2"] + attr["delta"]
attr["min2"] = nil
attr["delta"] = nil
end
end
def parse_attribute attribute, attr, weapon=false, offhand=false, set=false
case attribute
when /\+(\d+)% Movement Speed/
init_attr attr, "ms"
attr["ms"] += $1.to_f if set
when /\+(\d+) Dexterity/
init_attr attr, "dex"
attr["dex"] += $1.to_f if set
when /\+(\d+) Strength/
init_attr attr, "str"
attr["str"] += $1.to_f if set
when /\+(\d+) Intelligence/
init_attr attr, "int"
attr["int"] += $1.to_f if set
when /\+(\d+) Vitality/
init_attr attr, "vita"
attr["vita"] += $1.to_f if set
when /\+(\d+)% Life/
init_attr attr, "life"
attr["life"] += $1.to_f/100.0 if set
when /\+(\d+) Armor/
when /\+(\d+) Resistance to All Elements/
init_attr attr, "allres"
attr["allres"] += $1.to_f if set
when /\+(\d+) (Physical|Poison|Fire|Cold|Arcane|Lightning) Resistance/
attr[$2.downcase[0...2] + "res"] = $1 if set
when /\+(\d+)% Chance to Block/
attr["block"] = $1.to_f/100.0
when /Attack Speed Increased by (\d+)%/
attr["ias"] = $1.to_f/100.0 if set
when /\+(\d+).+?(\d+) (Fire|Holy|Poison|Lightning|Arcane|Cold) Damage/
attr["elemin"] = $1.to_f
attr["elemax"] = $2.to_f
when /\+(\d+)% Damage/
attr["ed"] = $1.to_f/100.0 if set
when /Critical Hit Damage Increased by (\d+)%/
init_attr attr, "cd"
attr["cd"] += $1.to_f/100.0 if set
when /Critical Hit Chance Increased by (\d+\.*\d*)%/
init_attr attr, "cc"
attr["cc"] += $1.to_f/100.0 if set
when /Adds (\d+)% to (Fire|Holy|Poison|Lightning|Arcane|Cold) Damage/
attr["elemental_ed"] = $1.to_f/100.0
when /\+(\d+) Maximum Discipline.*/
attr["disc"] = $1.to_f if set
when /Increases Discipline Regeneration by (\d+) per Second.*/
attr["discreg"] = $1.to_f
when /Regenerates (\d+) Life per Second/
attr["lifereg"] = $1.to_f
when /Each Hit Adds \+(\d+) Life/
init_attr attr, "loh"
attr["loh"] += $1.to_f
when /(\d+\.\d*)% of Damage Dealt Is Converted to Life/
attr["ll"] = $1.to_f/100.0
when /Increases Gold and Health Pickup by (\d+) Yards\./
attr["pickup"] = $1 if set
when /Reduces damage from ranged attacks by (\d+)%\./
attr["rangedr"] = $1.to_f/100.0
when /Health Globes and Potions Grant \+(\d+) Life\./
attr["potions"] = $1.to_f
when /Increases Hatred Regeneration by (\d+\.*\d*) per Second.*/
attr["hatredreg"] = $1.to_f
when /Reduces duration of control impairing effects by (\d+)%/
when /\+(\d+.+?\d+) Attacks per Second/
attr["baps"] = $1.to_f
when /Increases Damage Against Elites by (\d+)%/
when /Reduces damage from elites by (\d+)%\./
when /Reduces damage from Cold attacks by (\d+)%\./
when /Increases (Bash) Damage by (\d+)%.*/
when /Increases (Spirit Barrage) Damage by (\d+)%.*/
when /Level Requirement Reduced by (\d+)/
when /\+(\d+)% Extra Gold from Monsters/
when /(\d+)% Better Chance of Finding Magical Items/
when /Increases Bonus Experience by (\d+)%/
else
end
end
def init_attr attr, key
attr[key] = 0.0 if attr[key].nil?
end
def merge_items items
attributes = {}
items.each do |item|
item.each do |a, v|
if attributes[a].nil?
attributes[a] = v
elsif [Fixnum, Float].include? attributes[a].class
attributes[a] += v
end
end
end
attributes
end
def effective_life stats, items, char, buffs, mlvl=63, verbose=false
result = {}
life = stats["life"]
result["life"] = life
dr_char = if ["barbarian", "monk"].include? char and buffs.include? "character" then 0.3 else 0.0 end
result["dr_char"] = dr_char
res = [items["phres"], items["fires"], items["cores"], items["lires"], items["pores"], items["arres"]]
avg_res = if char == "monk" and buffs.include? "one-with-everything"
res.delete_if do |r| r.nil? end.max
else
res.inject(0.0) do |sum, el| sum + el.to_f end.to_f / 6.0
end
allres = if items["allres"].nil? then 0.0 else items["allres"] end
result["allres"] = allres
avg_res += allres + stats["intelligence"]/10.0
dodge_chance = get_dodge_chance(stats["dexterity"])
result["dodge_chance"] = dodge_chance
armor = items["armor"].to_f + stats["strength"].to_f
case char
when "wizard"
if buffs.include? "glas-cannon"
avg_res *= 0.9
armor *= 0.9
end
end
result["avg_res"] = avg_res
dr_res = avg_res/(avg_res+mlvl*5)
result["dr_res"] = dr_res
result["armor"] = armor
dr_armor = armor/(armor+mlvl*50)
result["dr_armor"] = dr_armor
dr_range = if items["rangedr"].nil? then 0.0 else items["rangedr"] end
dr_meelee = if items["meeleedr"].nil? then 0.0 else items["meeleedr"] end
dr_rm = (dr_range + dr_meelee) / 2.0
result["dr_rm"] = dr_rm
total_dr = 1.0 - (1.0-dodge_chance)*(1.0-dr_armor)*(1.0-dr_res)*(1.0-dr_rm)*(1.0-dr_char)
result["total_dr"] = total_dr
block_chance = stats["blockChance"]
block_amount_min = stats["blockAmountMin"]
block_amount_max = stats["blockAmountMax"]
# TODO block
ehp = life / (1.0-total_dr)
result["ehp"] = ehp
pretty_print result if verbose
result
end
def get_dodge_chance dex
return (([([dex-1000, 7000]).min,0]).max*0.01 +
([([dex-500, 500]).min, 0]).max*0.02 +
([([dex-100, 400]).min, 0]).max*0.025 +
([dex, 100]).min*0.1) / 100.0
end
def effective_disc stats, items, verbose=false
result = {}
disc = 30.0 + if items["disc"].nil? then 0.0 else items["disc"] end
result["disc"] = disc
discreg = 1.0 + if items["discreg"].nil? then 0.0 else items["discreg"] end
result["discreg"] = discreg
erp = disc / (14-2*discreg) * 14
result["erp"] = erp
pretty_print result if verbose
result
end
def effective_fury stats, items, verbose=false
result = {}
fury = 100.0 + if items["fury"].nil? then 0.0 else items["fury"] end
result["fury"] = fury
furyreg = -3.0 + if items["furyreg"].nil? then 0.0 else items["furyreg"] end
result["furyreg"] = furyreg
erp = fury / (14-2*furyreg) * 14
result["erp"] = erp
pretty_print result if verbose
result
end
def unbuffed_dps stats, items, mainhand, offhand, char, buffs, verbose=false
result = {}
weapons = [mainhand, offhand].delete_if do |w| w.nil? end
dual_wield = weapons.size == 2
damage = []
aps = []
case char
when "barbarian"
mainstat = stats["strength"].to_f
when "demon-hunter", "monk"
mainstat = stats["dexterity"].to_f
when "wizard", "witch-doctor"
mainstat = stats["intelligence"].to_f
else
puts "unknown character: " + char
end
result["mainstat"] = mainstat
cc = 0.05 + if items["cc"].nil? then 0.0 else items["cc"] end
cd = 0.5 + if items["cd"].nil? then 0.0 else items["cd"] end
ias = if items["ias"].nil? then 0.0 else items["ias"] end
ed = 0.0
pvped = if items["dmg_vs_elites"].nil? then 0.0 else items["dmg_vs_elites"] end
additional_dmg = if items["min"].nil? then 0.0 else items["min"] end
additional_dmg += if items["max"].nil? then 0.0 else items["max"] end
additional_dmg /= 2.0
result["additional_dmg"] = additional_dmg
weapons.each do |weapon|
weapon_dmg = (weapon.min + weapon.max)/2.0
result["weapon_dmg"] = weapon_dmg
base_dmg = weapon_dmg + additional_dmg
result["base_dmg"] = base_dmg
elemental_dmg = (weapon.elemin+weapon.elemax)/2.0
calculated_wdps = (weapon_dmg + elemental_dmg)*weapon.aps
if calculated_wdps != weapon.dps then puts "WARNING: calculated_wdps #{calculated_wdps} != weapon.dps #{weapon.dps}" end
unless items["elemental_ed"].nil?
elemental_dmg += base_dmg * items["elemental_ed"]
end
result["elemental_dmg"] = elemental_dmg
damage << (base_dmg + elemental_dmg)
aps << weapon.aps
end
if buffs.include? "focused-mind"
ias += 0.03
end
case char
when "barbarian"
if buffs.include? "battle-rage"
if buffs.include? "maraudar's-rage"
ed += 0.3
else
ed += 0.15
end
cc += 0.03
end
if buffs.include? "wrath-of-the-berserker"
cc += 0.1
ias += 0.25
end
if buffs.include? "ruthless"
cc += 0.05
cd += 0.5
end
if buffs.include? "weapon-master"
case mainhand.wtype
when "MightyWeapon1H"
# TODO 3 fury each hit?!
when "Mace", "Axe", "Axe2H"
cc += 0.1
when "Sword", "Dagger"
ed += 0.15
when "Polearm", "Spear"
ias += 0.1
else
puts "WARNING: unknown wtype " + mainhand.wtype
end
end
when "demon-hunter"
if buffs.include? "archery"
case mainhand.wtype
when "Bow"
ed += 0.15
when "HandXbow"
cc += 0.1
when "Crossbow"
cd += 0.5
else
puts "WARNING: unknown wtype " + mainhand.wtype
end
end
when "wizard"
if buffs.include? "glas-cannon"
ed += 0.15
end
end
result["cc"] = cc
result["cd"] = cd
result["ias"] = ias
result["ed"] = ed
result["pvped"] = pvped
baps = if items["baps"].nil? then 0.0 else items["baps"] end
aps.map! do |a| baps+a end
damage.map! do |dmg| dmg * (1.0 + ed) * (1.0 + pvped) end
result["damage"] = damage
result["aps"] = aps
aps_total = if dual_wield then 2 * aps[0] * aps[1] * (1.15+ias) / (aps[0] + aps[1]) else aps[0] * (1.0+ias) end
result["aps_total"] = aps_total
unbuffed_dps = (1.0+mainstat/100.0) * (1.0+cc*cd) * aps_total * if dual_wield then (damage[0] + damage[1])/2 else damage[0] end
result["dps"] = unbuffed_dps
pretty_print result if verbose
result
end
def pretty_print scores, w=20
puts "".ljust(50, ".")
scores.each do |k, v|
res = if [Float, Fixnum].include? v.class
sprintf("%.3f", v)
elsif v.class == Array
v.map do |i| sprintf("%.3f", i) end.join ", "
else
v.to_s + v.class.to_s
end
puts "\t" + (k+":").ljust(w, ".") + res.rjust(w, ".")
end
end
verbose = false
# char specific passive skill buffs
buffs = ["character",
# barbarian
"weapon-master",
"ruthless",
#"battle-rage",
#"maraudar's-rage",
#"wrath-of-the-berserker",
# demon hunter
"archery",
# monk
"one-with-everything",
# wizard
"glas-cannon",
# followers
#"focused-mind",
]
players = [
#{ "name" => "Darai", "tag" => "Rorschach", "number" => 2926, "exclude" => nil },
#{ "name" => "Yan", "tag" => "Scorpion", "number" => 2883, "chars" => "demon-hunter", "exclude" => nil },
#{ "name" => "Slovenka", "tag" => "Bitsurugi", "number" => 2597, "exclude" => nil },
#{ "name" => "Alizee", "tag" => "Sawyer", "number" => 2666, "exclude" => nil },
#{ "name" => "Karl", "tag" => "Karl", "number" => 2673, "exclude" => nil },
#{ "name" => "Clemenza", "tag" => "Kamhul", "number" => 2452, "exclude" => nil },
#{ "name" => "Poisonivy", "tag" => "Antili", "number" => 2847, "exclude" => nil },
#{ "name" => "Paradogz", "tag" => "Paradogz", "number" => 2133, "exclude" => nil },
#{ "name" => "Anakin", "tag" => "Anakin", "number" => 2978, "exclude" => nil },
#{ "name" => "Hillarius", "tag" => "Hillarius", "number" => 1372, "exclude" => nil },
# Barbarians
#{ "name" => "Pulsedraft", "tag" => "Pulsedraft", "number" => 2965, "chars" => ["barbarian"], "exclude" => ["Pooliemuli", "Cuzlure", "BlasiHasi", "TheHoff"] },
#{ "name" => "Braveheart", "tag" => "Braveheart", "number" => 2324, "chars" => ["barbarian"], "exclude" => ["Astus", "Cuirass", "Auktionshaus", "Bullseye"] },
#{ "name" => "Quickness", "tag" => "Quickness", "number" => 1919, "chars" => ["barbarian"], "exclude" => nil },
#{ "name" => "Johni", "tag" => "JOHNi", "number" => 2140, "chars" => ["barbarian"], "exclude" => ["RINGEDiamant", "R\u00FCstungen"] },
# Demon Hunters
{ "name" => "Freaky", "tag" => "Alexander", "number" => 2557, "chars" => ["demon-hunter"], "exclude" => ["Alexander"] },
{ "name" => "Onkelfeix", "tag" => "Onkelfeix", "number" => 1125, "chars" => ["demon-hunter"], "exclude" => ["Tradeheini", "Ingeborg"] },
{ "name" => "Xerxes", "tag" => "Xerxes", "number" => 2516, "chars" => ["demon-hunter"], "exclude" => ["Kenny", "Horatio", "hurine"] },
# Monks
#{ "name" => "Raky", "tag" => "raky", "number" => 2522, "chars" => ["monk"], "exclude" => nil },
#{ "name" => "Psi", "tag" => "Psi", "number" => 2724, "chars" => ["monk"], "exclude" => nil },
#{ "name" => "Lokk", "tag" => "lokk", "number" => 2851, "chars" => ["monk"], "exclude" => nil },
# Witch Doctors
#{ "name" => "Sinured", "tag" => "Sinured", "number" => 2182, "chars" => ["witch-doctor"], "exclude" => ["Azula"] },
#{ "name" => "Apocalypto", "tag" => "Apocalypto", "number" => 2474, "chars" => ["witch-doctor"], "exclude" => nil },
#{ "name" => "Anocsa", "tag" => "Anocsa", "number" => 2133, "chars" => ["witch-doctor"], "exclude" => nil },
# Wizards
#{ "name" => "Hamster", "tag" => "pawhi", "number" => 2897, "chars" => ["wizard"], "exclude" => nil },
#{ "name" => "Xenter", "tag" => "Xenter", "number" => 2860, "chars" => ["wizard"], "exclude" => ["Xentercore"] },
#{ "name" => "SirBacke", "tag" => "Alexander", "number" => 21697, "chars" => ["wizard"], "exclude" => nil },
#{ "name" => "Koenig", "tag" => "Koenig", "number" => 2201, "chars" => ["wizard"], "exclude" => ["WonderWoman"] },
#{ "name" => "Silencer", "tag" => "Silencer", "number" => 2339, "chars" => ["wizard"], "exclude" => ["Seran"] },
]
players.each do |player|
profile = Diablo3::Api::Client::Profile.new(player["tag"], player["number"])
puts player["name"]
profile.heroes.each do |hero|
if player["chars"].include? hero.class_name
if not player["exclude"].nil? and player["exclude"].include? hero.name
puts "Excluded" if verbose
next
end
items, mainhand, offhand = parse_items hero
attributes = merge_items items
if mainhand.nil?
puts "Skipping mule" if verbose
next
end
dual_wield = !(mainhand.nil? or offhand.nil?)
puts hero.stats if verbose
puts "Char: #{hero.name} (#{hero.class_name})"
ehp = effective_life hero.stats, attributes, char=hero.class_name, buffs, mlvl=63, verbose=verbose
dps = unbuffed_dps hero.stats, attributes, mainhand, offhand, hero.class_name, buffs, verbose=verbose
aps = dps["aps_total"]
ms = if attributes["ms"].nil? then 0.0 else attributes["ms"] end
ms = [ms.to_f, 25.0].min
erp = 0.0
pvp = 0.0
case hero.class_name
when "barbarian" # TODO parse fury + regen
erp = effective_fury hero.stats, attributes, verbose=verbose
pvp = (ehp["ehp"]/141421.4) * (dps["dps"]/141421.4) * (1.0+erp["erp"])**0.1 * (1.0+aps)**0.1 * (1.0+ms)**0.1
when "demon-hunter"
erp = effective_disc hero.stats, attributes, verbose=verbose
pvp = (ehp["ehp"]/141421.4) * (dps["dps"]/141421.4) * (1.0+erp["erp"])**0.1 * (1.0+aps)**0.1 * (1.0+ms)**0.1
else
puts "WARNING: unknown character"
next
end
result = {"EHP" => ehp["ehp"], "ERP" => erp["erp"], "DPS" => dps["dps"], "APS" => aps, "MS" => ms, "PVP" => pvp}
pretty_print result, w=10 if pvp > 1.0
end
end
end
@AlexanderFabisch
Copy link
Author

https://github.com/murphyslaw/diablo3-api-client is required for this script.

Explanation (German)

Legende:

EHP: Effective Health Points. Was das ist sollte ja mittlerweile jedem klar sein.

EDP: Effective Discipline Points. Hier wird die Disziplinregeneration reingerechnet. Als Grundlage wird dabei der Skill Smoke Screen (SS) genommen. Dieser hat einen Cooldown von 2 Sekunden, kostet 14 Disziplin und es wird angenommen, dass er immer neu gecastet wird. Mit den EDP kann man direkt ausrechnen wie oft man hintereinander SS casten kann. Bei 84 EDP kann man z. B. 84/14 = 6 mal SS casten. Die Formel dafür ist:

EDP = 14_Disc / (14-2_Discreg)

DPS: Damage Per Second.

APS: Attacks Per Second.

MS: Movement Speed.

Score: Ein Punktwert, der die Eignung eines Charakter für PvP abschätzt. Je mehr, desto besser. Im Moment wird er durch folgende Formel berechnet:

Score = (EHP_DPS_(1+APS)^0.1_(1+EDP)^0.1_(1+MS)^0.1)/20.000.000.000

Das ganze hat folgenden Hintergrund: angenommen Spieler 1 hat EHP_1 Effective Health Points und DPS_1 Damage Per Second und Spieler 2 EHP_2 und DPS_2. Dann killt Spieler 1 Spieler 2 theoretisch in EHP_2/DPS_1 Sekunden und umgekehrt. Damit Spieler 1 Spieler 2 schlägt, muss Spieler 1 länger leben, d. h.

EHP_1/DPS_2 > EHP_2/DPS_1

Das lässt sich umstellen zu

EHP_1 * DPS_1 > EHP_2 * DPS_2

Man kann alle Charaktere direkt mit dem Wert EHP*DPS für PvP ranken. So wird das bei diabloprogress.com auch gemacht. Das setzt aber voraus, dass sich die beiden Spieler nur gegenüber stehen und stumpfsinnig aufeinander einballern.

Tatsächlich gibt es allerdings noch weitere Faktoren, die zwar nicht direkt den Killspeed, sehr wohl aber die Überlebensfähigkeit beeinflussen. Das sind der MS, der das Ausweichen und Abhauen vereinfacht, die APS, die die Beweglichkeit ebenfalls erhöhen und die EDP, die einen öfter gegen Schaden immun macht, je höher sie ist. Da utopisch hohe Werte allerdings nicht so viel besser als mittelgute Werte sind, sollte der Faktor mit dem sie den PvP-Score beeinflussen allerdings nicht so stark sein, wie z. B. bei den DPS. Stattdessen nimmt der Einfluss mit steigendem Wert immer weiter ab. Deshalb ist hier der Exponent 0.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment