189 lines
7.8 KiB
Python
189 lines
7.8 KiB
Python
import matplotlib.pyplot as plt
|
|
import json
|
|
import numpy as np
|
|
|
|
#baselines_known_x = np.array([ 70, 72, 74, 75, 76, 78, 80, 82, 84, 85, 86, 89, 90, 91, 93, 95, 97, 98, 100 ])
|
|
#baselines_known_y = np.array([ 341.53, 358.29, 374.68, 383.35, 394.06, 410.75, 432.14, 453.45, 469.94, 480.93, 491.21, 524.26, 536.69, 546.26, 569.78, 592.45, 615.65, 626.75, 648.21 ]) / 2.05
|
|
|
|
item_type = "bow"
|
|
items_file = "../clean.json"
|
|
items_new_file = "../clean3.json"
|
|
|
|
min_level = 70
|
|
baselines_known_x = np.array([ 70, 75, 80, 85, 90, 95, 100 ])
|
|
baselines_known_y = np.array([ 341.53, 383.35, 432.14, 480.93, 536.69, 592.45, 648.21 ]) / 2.05
|
|
baselines_known_y_new = np.zeros(len(baselines_known_x))
|
|
for i, (x, y) in enumerate(zip(baselines_known_x, baselines_known_y)):
|
|
baselines_known_y_new[i] = y * (1 - 0.01 * (x - 70))
|
|
baselines_known_y = baselines_known_y_new
|
|
|
|
def interpolate_baseline(level):
|
|
i = 0
|
|
while baselines_known_x[i] <= level:
|
|
if baselines_known_x[i] == level:
|
|
return baselines_known_y[i]
|
|
i += 1
|
|
if i == len(baselines_known_x):
|
|
return baselines_known_y[-1]
|
|
start = i - 1
|
|
slope = ((baselines_known_y[i] - baselines_known_y[start])
|
|
/ (baselines_known_x[i] - baselines_known_x[start]))
|
|
dx = level - baselines_known_x[start]
|
|
return baselines_known_y[start] + slope * dx
|
|
|
|
reqs = ["dexReq", "strReq", "defReq", "agiReq", "intReq"]
|
|
# tefaw
|
|
powders_new = [ [4.5, 6.5, 8.5, 9, 10.5, 12.5, "T"],
|
|
[4.5, 6.5, 8, 8.5, 10, 12, "E"],
|
|
[3.5, 6, 7, 7.5, 9, 11, "F"],
|
|
[4, 6.5, 7.5, 8, 9.5, 11, "A"],
|
|
[3.5, 5, 6.5, 7, 8.5, 10, "W"]]
|
|
powders_old = [ [4.5, 6.5, 8.5, 9, 10.5, 22.5, "T"], # LOL only updating the 6th col
|
|
[4.5, 6.5, 8, 8.5, 10, 20, "E"],
|
|
[3.5, 6, 7, 7.5, 9, 17, "F"],
|
|
[4, 6.5, 7.5, 8, 9.5, 17, "A"],
|
|
[3.5, 5, 6.5, 7, 8.5, 15, "W"]]
|
|
powders_old = powders_new
|
|
def get_appropriate_powder_idx(item):
|
|
for i, req in enumerate(reqs):
|
|
if req in item and item[req] > 0:
|
|
return i
|
|
return 0
|
|
|
|
def get_display_name(item):
|
|
if "displayName" in item:
|
|
return item["displayName"]
|
|
return item["name"]
|
|
item_data = json.load(open(items_file))["items"]
|
|
item_map = {get_display_name(item): item for item in item_data}
|
|
#item_new_data = json.load(open(items_new_file))["items"]
|
|
#item_new_map = {get_display_name(item): item for item in item_new_data}
|
|
|
|
attack_speed_mods = {"SUPER_SLOW": 0.51, "VERY_SLOW": 0.83, "SLOW": 1.5, "NORMAL": 2.05, "FAST": 2.5, "VERY_FAST": 3.1, "SUPER_FAST": 4.3}
|
|
attack_speed_target_mult = {"SUPER_SLOW": 4, "VERY_SLOW": 2.5, "SLOW": 1.4, "NORMAL": 1, "FAST": 0.8, "VERY_FAST": 0.66, "SUPER_FAST": 0.48}
|
|
dps_to_baseline = dict()
|
|
min_mult = 10
|
|
max_mult = 0
|
|
for k in attack_speed_mods:
|
|
mult = attack_speed_mods[k] * attack_speed_target_mult[k]
|
|
if mult < min_mult:
|
|
min_mult = mult
|
|
if mult > max_mult:
|
|
max_mult = mult
|
|
dps_to_baseline[k] = 1/mult
|
|
|
|
weapon_type_mods = {"wand": 0.6, "spear": 0.8, "dagger": 1.0, "bow": 1.2, "relik": 1.2}
|
|
print((min_mult, max_mult))
|
|
|
|
tiers_mod = {"Normal": 0.8, "Unique": 1.0, "Rare": 1.1, "Legendary": 1.3, "Fabled": 1.5, "Mythic": 1.7, "Set": 1.05}
|
|
tiers_colors = {"Normal": (0.9, 0.9, 0.9), "Unique": (1, 1, 1/3), "Rare": (1, 1/3, 1), "Legendary": (1/3, 1, 1), "Fabled": (1, 1/3, 1/3), "Mythic": (2/3, 0, 2/3), "Set": (1/3, 1, 1/3)}
|
|
|
|
|
|
damage_types = ["nDam", "eDam", "tDam", "wDam", "fDam", "aDam"]
|
|
|
|
# tefaw
|
|
damage_baseline_modifiers = [0.05, 0.05, -0.05, 0, -0.05]
|
|
def guess_design_modifier(item, base_dps):
|
|
level = item["lvl"]
|
|
tier = item["tier"]
|
|
nominal_baseline = interpolate_baseline(level) * weapon_type_mods[item["type"]] * tiers_mod[tier]
|
|
explanation = []
|
|
num_reqs = 0
|
|
total_modifier = 0
|
|
for i, req in enumerate(reqs):
|
|
if req in item and item[req] > 0:
|
|
num_reqs += 1
|
|
total_modifier += damage_baseline_modifiers[i]
|
|
explanation.append((req, damage_baseline_modifiers[i]))
|
|
|
|
num_damage_types = 0
|
|
for damage_type in damage_types[1:]:
|
|
if damage_type in item:
|
|
damages = item[damage_type].split("-")
|
|
if int(damages[1]) != 0:
|
|
num_damage_types += 1
|
|
|
|
is_rainbow = num_damage_types == 5 or num_reqs == 5
|
|
if is_rainbow:
|
|
total_modifier = 0.15
|
|
explanation = [("rainbow", 0.15)]
|
|
elif num_reqs > 1 or num_damage_types > 1:
|
|
total_modifier += 0.05
|
|
explanation.append(("multi_element", 0.05))
|
|
#total_modifier = 0.05
|
|
#explanation = [("multi_element", 0.05)]
|
|
|
|
nslots = 0
|
|
if "slots" in item:
|
|
nslots = item["slots"]
|
|
if nslots == 0:
|
|
total_modifier += 0.1
|
|
explanation.append(("zero_slot", 0.1))
|
|
elif nslots == 1:
|
|
total_modifier += 0.05
|
|
explanation.append(("one_slot", 0.05))
|
|
|
|
item_baseline = dps_to_baseline[item["atkSpd"]] * base_dps
|
|
actual_modifier = (item_baseline - nominal_baseline) / nominal_baseline
|
|
explained_baseline = nominal_baseline * (1 + total_modifier)
|
|
delta = (item_baseline - explained_baseline) / nominal_baseline
|
|
if delta >= 0.04 and delta < 0.075:
|
|
total_modifier += 0.05
|
|
explanation.append(("offensive", 0.05))
|
|
elif delta >= 0.075:
|
|
total_modifier += 0.1
|
|
explanation.append(("hyper_offensive", 0.1))
|
|
elif delta <= -0.04 and delta > -0.075:
|
|
total_modifier -= 0.05
|
|
explanation.append(("defensive", -0.05))
|
|
elif delta <= -0.075:
|
|
total_modifier -= 0.1
|
|
explanation.append(("hyper_defensive", -0.1))
|
|
if abs(delta) > 0.2:
|
|
print("LARGE BASELINE ERROR FOR ITEM " + get_display_name(item))
|
|
return total_modifier, actual_modifier, explanation
|
|
|
|
def get_data(item, powders):
|
|
total_damage = 0
|
|
for damage_type in damage_types:
|
|
if damage_type in item:
|
|
damages = item[damage_type].split("-")
|
|
total_damage += int(damages[0]) + int(damages[1])
|
|
total_damage /= 2
|
|
attack_speed_mod = attack_speed_mods[item["atkSpd"]]
|
|
dps = total_damage * attack_speed_mod
|
|
postpowder_damage = total_damage
|
|
if "slots" in item:
|
|
powder = powders[get_appropriate_powder_idx(item)][5] # Assume tier6 always.
|
|
postpowder_damage += powder * item["slots"]
|
|
return (dps, postpowder_damage * attack_speed_mod)
|
|
|
|
item_dat = {cat: [] for cat in weapon_type_mods}
|
|
for name, item in item_map.items():
|
|
if "lvl" not in item:
|
|
continue
|
|
if item["lvl"] >= min_level and item["category"] == "weapon":
|
|
dps, postpowder_dps = get_data(item, powders_old)
|
|
# new_item = item_new_map[name]
|
|
# new_dps, new_postpowder_dps = get_data(new_item, powders_new)
|
|
total, actual, explain = guess_design_modifier(item, dps)
|
|
item_dat[item["type"]].append((get_display_name(item), item["lvl"], item["id"], item["tier"],
|
|
dps, postpowder_dps,
|
|
total, actual, explain))
|
|
item_dat["baseline_xs"] = baselines_known_x.tolist()
|
|
item_dat["baseline_ys"] = baselines_known_y.tolist()
|
|
#item_dat["baseline_ys_new"] = baselines_known_y_new.tolist()
|
|
json.dump(item_dat, open("dps_data.json", "w"), indent=2)
|
|
json.dump(item_dat, open("dps_data_compress.json", "w"))
|
|
# item_lvl, item_dps, item_tiercolor = zip(*item_dat)
|
|
# plt.scatter(item_lvl, item_dps, color=item_tiercolor)
|
|
#
|
|
# for tier, mod in tiers_mod.items():
|
|
# plt.plot(baselines_known_x, baselines_known_y * max_mult * mod, '--', color=tiers_colors[tier])
|
|
# plt.plot(baselines_known_x, baselines_known_y * min_mult * mod, '--', color=tiers_colors[tier])
|
|
# plt.xticks([70, 75, 80, 85, 90, 95, 100])
|
|
# plt.xlabel("Combat level")
|
|
# plt.ylabel("Base dps")
|
|
# plt.title(f"{item_type} base dps vs level")
|
|
# plt.grid()
|
|
# plt.show()
|