暗黑破坏神3(diablo3)属性计算器:支持一次更换任意多个装备/技能/符文/buff/跟随者
暗黑破坏神3(diablo3)属性计算器:支持一次更换任意多个装备/技能/符文/buff/跟随者
by AKara 2012-07-29 @ http://blog.csdn.net/akara @ akarachen(at)gmail.com @weibo.com/akaras
众所周知,暗黑破坏神3 的拍卖场里面的有效装备掉落比游戏中的好;
所以我经常对比装备属性,来计算整体数值的提升情况如何。
网上早有相关的公式和工具,但是使用起来觉得不是很方便,
于是写了个python的小程序 d3diff.py 做这个事情。
---------------------------------
先说使用步骤:
(1) 按自己的情况,逐一对应填写 props 字典。
(2) 将想换上的每件装备 和 身上正穿着的对应部位装备做「属性差」计算,逐一写入 diff_list 列表。
(3) 将 monster_level 设置为可以一战的怪物等级。
(4) python.exe d3diff.py 即可输出属性提升报表。
---------------------------------
现在结合源码的测试代码,来个完整的例子:
我的当前野蛮人属性数据(单持钉锤+暴风盾,不带护甲值加成被动技能,不带跟随者,无战孔buff),如下:
那么现在我想测试如下几个搭配效果:
(1) 跟随者:带巫女,并只训练“专注精神”。
「属性差」: {"武器攻击速度": +0.03}
(2)被动技能:选择"意志坚定"。
「属性差」:{"护甲值加成": +0.25}
(3)被动技能:选择"钢铁胆识"。
「属性差」:{"体能转换护甲值比率": +1.00}
(4)假设我现在正处于 主动技能“战吼” + “顽抗战吼”符文 效果中。
「属性差」:{"护甲值加成": +0.20, "抗性加成": +0.50}
(5)把主手钉锤 和 副手暴风盾,切换为双手斧头。
因为双手斧头会占用副手,所以 「属性差」是:斧头属性 - 钉锤属性 - 暴风盾属性,即:
{
"武器伤害下限": 945 - 420,
"武器伤害上限": 1424 - 926,
"武器攻击速度": 1.0 - 1.2,
"暴击伤害": 0 - 0.8,
"力量": 135 - 84 - 60,
"智力": 127 - 83,
"敏捷": 123 - 0,
"体能": 137 - 34,
"全抗性": 0 - 70,
"护甲值": 0 - 1037
}
如下面代码一样将属性差填入diff_list字段后,可以运行得出结果。
对比一下完成上面(1)(2)(3)(4)(5)后的游戏截图,正确:
---------------------------------
源码:
# -*- encoding: GBK -*-## d3diff.py (Python2.5 或 Python2.7均可)## 暗黑破坏神3(diablo3) - 属性计算器:支持一次更换任意多个 装备/技能/符文/buff/跟随者。# 用法:# 填入任意多个 装备/技能/符文/buff/跟随者/(甚至假设的装备) 带来的差异值,# 然后运行输出人物如下属性的变化情况报表。# # (1)伤害计算(DPS:支持所有握持武器方式——单持,双持,双手) # (2)生命值上限计算(MAXHP)# (3)护甲值(ARMOR:纯护甲值,暂不支持被动技能加成)# (4)有效受击点数(EHP,只考虑稳定的护甲和抗性因素,不考虑闪躲和格挡等机率性因素)# (5)伤害减免(DR,护甲值带来的减免)# (6)物理抗性减免(PDR,物理抗性带来的减免)# (7)冰寒抗性减免(IDR,冰寒抗性带来的减免)# (8)火焰抗性减免(FDR,火焰抗性带来的减免)# (9)电击抗性减免(LDR,电击抗性带来的减免)# (10)毒素抗性减免(pDR,毒素抗性带来的减免)# (11)秘法/神圣抗性减免(A/HDR,秘法/神圣抗性带来的减免)### 误差:# (1)由于武器伤害,面板抗性等显示为浮点舍入后数值,# 所以计算结果存在小范围结果误差。影响不大。 ## by AKara 2012.07# akaras(at)gmail.com# akaras@weibo.com#import copyALL_ELEMENTAL_TYPE = ("物理抗性", "冰寒抗性", "火焰抗性", "电击抗性", "毒素抗性", "秘法/神圣抗性")REPORT_KEY_ORDER = ("伤害", "生命值上限", "有效受击点数", "护甲值","伤害减免", "物理抗性减免", "冰寒抗性减免", "火焰抗性减免", "电击抗性减免", "毒素抗性减免", "秘法/神圣抗性减免")CLASS_MAIN_ATTR_MAP = {"野蛮人": "力量", "秘术师": "智力", "狩魔猎人": "敏捷", "巫医": "智力", "武僧": "敏捷"}# 计算伤害(DPS)def _calc_weapon_dmg_not_dual_wielding(props):return (props["武器伤害下限"] + props["附加伤害下限"] + \props["武器伤害上限"] + props["附加伤害上限"]) / 2.0def _calc_weapon_speed_not_dual_wielding(props):return props["武器攻击速度"]def _calc_weapon_dmg_dual_wielding(props):main_hand_dmg = _calc_weapon_dmg_not_dual_wielding(props)off_hand_dmg = (props["副手武器伤害下限"] + props["附加伤害下限"] + \props["副手武器伤害上限"] + props["附加伤害上限"]) / 2.0return (main_hand_dmg + off_hand_dmg) / 2.0def _calc_weapon_speed_dual_wielding(props): # 计算调和平均值return 2.0 * ((props["武器攻击速度"] * props["副手武器攻击速度"]) / \(props["武器攻击速度"] + props["副手武器攻击速度"]))def _calc_common_dps_modifiers(props):main_attr_name = CLASS_MAIN_ATTR_MAP[props["种族"]]return (1 + props["暴击机率"] * props["暴击伤害"]) * \(1 + props[main_attr_name] / 100.0) * \(1 + props["技能伤害加成"])def _calc_dps_dual_wielding(props):return _calc_weapon_dmg_dual_wielding(props) * \_calc_weapon_speed_dual_wielding(props) * \(1.15 + props["附加攻击速度加成"]) * \_calc_common_dps_modifiers(props)def _calc_dps_not_dual_wielding(props):return _calc_weapon_dmg_not_dual_wielding(props) * \_calc_weapon_speed_not_dual_wielding(props) * \(1 + props["附加攻击速度加成"]) * \_calc_common_dps_modifiers(props)def calc_dps(props):if props["双持"] == "是":return _calc_dps_dual_wielding(props)else:return _calc_dps_not_dual_wielding(props)# 计算生命值上限def calc_max_hp(props):vit_factor = max(props["等级"] - 25, 10)max_hp = (36 + (4 * props["等级"])) + (vit_factor * props["体能"])max_hp *= (1 + props["总生命值加成"])return max_hp# 计算有效受击点数(EHP: Effective Hit Points)def calc_ehp(props, monster_level):max_hp = calc_max_hp(props)all_dr = calc_all_dr(props, monster_level)# 护甲伤害减免armor_dr = all_dr["伤害减免"]# 元素抗性减免取算术平均值elemental_avg_dr = 0for res in ALL_ELEMENTAL_TYPE:elemental_avg_dr += all_dr[res + "减免"]elemental_avg_dr /= float(len(ALL_ELEMENTAL_TYPE))# 种族伤害减免:野蛮人和武僧收到的伤害比其他种族少30%if props["种族"] in ("野蛮人", "武僧"):class_dr = 0.30else:class_dr = 0.00# 护甲值伤害减免 和 元素抗性减免 ,种族伤害减免 合并计算total_dr = 1 - (1 - armor_dr) * (1 - elemental_avg_dr) * (1 - class_dr)return max_hp / (1 - total_dr)# 计算全类型伤害减免def _calc_armor_dr(armor, monster_level):return armor / (50.0 * monster_level + armor)def _calc_elemental_dr(res, monster_level):return res / (5.0 * monster_level + res)def calc_all_dr(props, monster_level):all_dr = {}all_dr["伤害减免"] = _calc_armor_dr(props["护甲值"], monster_level)for res in ALL_ELEMENTAL_TYPE:all_dr[res + "减免"] = _calc_elemental_dr(props[res], monster_level)return all_dr# 计算护甲值def get_and_update_final_armor(props):final_armor = (props["护甲值"] + \props["体能转换护甲值比率"] * props["体能"]) * \(1 + props["护甲值加成"])props["护甲值"] = final_armorreturn final_armor# 考虑抗性加成def _update_final_res(props):for res in ALL_ELEMENTAL_TYPE:props[res] *= (1 + props["抗性加成"])# 获得所有关心的数值结果def get_all_stats(props, monster_level):# 首先更新加成后的最终护甲值final_armor = get_and_update_final_armor(props)# 再更新加成后的最终抗性值_update_final_res(props)# 再计算各种减免all_dr = calc_all_dr(props, monster_level)all_stats = {"伤害": calc_dps(props),"生命值上限": calc_max_hp(props),"有效受击点数": calc_ehp(props, monster_level),"护甲值": final_armor,}all_stats.update(all_dr)return all_statsdef _get_flag(v):if v < 0: return ""elif v == 0: return ""else: return "+"def _update_all_res(props, v):for res in ALL_ELEMENTAL_TYPE:props[res] += v# 根据差异值,打印报表def _apply_diff(old_props, diff, monster_level):# 再复制一份copy_old_props = copy.deepcopy(old_props)old_all_stats = get_all_stats(copy_old_props, monster_level)# 应用差异属性# 再复制一份new_props = copy.deepcopy(old_props)for k, v in diff.iteritems():if k == "双持": # 唯一替换性质的字段new_props[k] = velif k == "全抗性":_update_all_res(new_props, v)else:new_props[k] += vif k == "力量": # 影响纯护甲值new_props["护甲值"] += velif k == "智力": # 影响抗性_update_all_res(new_props, v/10.0)new_all_stats = get_all_stats(new_props, monster_level)# 格式化报表report_texts = []report_texts.append("------------ 总输出差异 ------------")for k in REPORT_KEY_ORDER:old_v = old_all_stats[k]new_v = new_all_stats[k]delta = new_v - old_vreport_texts.append("%s%s%.04f (%.04f -> %.04f)"%(\k.upper().ljust(20), _get_flag(delta), delta, old_v, new_v))diff_desc = []for k, v in diff.iteritems(): if k == "双持":diff_desc.append('%s%s'%(k.upper().ljust(20), v))else:diff_desc.append('%s%s%.02f'%(k.upper().ljust(20), _get_flag(v), v))report_texts.append("\n------------ 总输入差异 ------------ \n%s"%(\"\n".join(diff_desc)))return "\n" + "\n".join(report_texts) + "\n"# 穿上一整套装备,打印报表def apply_diff_list(old_props, diff_list, monster_level):final_diff = {}for diff in diff_list:for k, v in diff.iteritems(): # sum or updateif k in final_diff:final_diff[k] += velse:final_diff[k] = vreturn _apply_diff(props, final_diff, monster_level)if __name__ == "__main__":################################################################# 下面用我的野蛮人数据来做测试举例,具体截图见博客非代码部分内容。# 注意:所有百分比的值,都用小数表示,比如暴击伤害 184%,则填1.84################################################################props = {# 基础种族和等级"种族": "野蛮人", # "野蛮人", ""秘术师", "狩魔猎人", "巫医", "武僧""等级": 60,# 一级面板属性"力量": 1544,"敏捷": 145,"智力": 356,"体能": 963,"护甲值": 5012, # 注意:请填纯护甲值(即只含所有防具护甲值 和 力量属性对应的护甲)"伤害": 21593.65,# 二级面板属性# 攻击"力量伤害加成": 15.44,"技能伤害加成": 0.00,"每秒攻击次数": 1.35,"暴击机率": 0.215,"暴击伤害": 1.84,# 防御"格挡值下限": 2800,"格挡值上限": 3744,"格挡机率": 0.32,"闪躲机率": 0.111,"伤害减免": 0.7134, # Diablo3面板中的这"伤害减免"单指护甲值的伤害减免"物理抗性": 616,"冰寒抗性": 578,"火焰抗性": 580,"电击抗性": 588,"毒素抗性": 624,"秘法/神圣抗性": 584,"控场减免": 0,"远程伤害减免": 0,"近战伤害减免": 0.0784,"荆棘伤害": 0,# 生命"生命值上限": 40777,"总生命值加成": 0.20,"每秒生命恢复": 333.0,"生命窃取": 0.00,"击杀生命恢复": 0.00,"击中生命恢复": 1309.0,"生命之球效果加成": 0.0,"拾取距离加成": 14.0,# DPS计算所需的额外装备属性数值"双持": "否", # "是"或"否",根据情况自己修改"武器伤害下限": 420,"武器伤害上限": 926,"武器攻击速度": 1.2,"副手武器伤害下限": 0, # "双持"字段为"是",本字段才非0"副手武器伤害上限": 0, # "双持"字段为"是",本字段才非0"副手武器攻击速度": 0, # "双持"字段为"是",本字段才非0"附加攻击速度加成": 0.08+0.07,"附加伤害下限": 6,"附加伤害上限": 12,# 护甲值计算所需要的额外属性数值"护甲值加成": 0.00,"体能转换护甲值比率": 0.00, # 抗性计算"抗性加成": 0.00,}# 要换上哪些装备,一件件填"差值"。# 再次注意,是对比现有装备/技能/buff/跟随者的"差值",多+少-。diff_list = [# 带巫女,训练“专注精神”技能# 注:这个技能直接加的是武器攻击速度,不是 附加攻击速度加成){"武器攻击速度": +0.03},# 野蛮人使用被动技能"意志坚定",增加25%护甲值{"护甲值加成": +0.25},# 野蛮人使用被动技能"钢铁胆识", 体能100%增添到护甲值{"体能转换护甲值比率": +1.00},# 假设我使用了战孔(+20%护甲) + 顽抗战孔符文(+50%抗性){"护甲值加成": +0.20,"抗性加成": +0.50},# 从单持钉锤 + 暴风盾,切换为双手斧头(-__-){"武器伤害下限": 945-420, "武器伤害上限": 1424-926,"武器攻击速度": 1.0-1.2,"暴击伤害": -0.8, "力量": 135-84-60, "智力":127-83, "敏捷":123-0, "体能":137-34,"全抗性":0-70, "护甲值":-1037},# 假设,我为主手武器换上一颗满级绿宝石#{"暴击伤害": 1.00 - 0.80},# 又假设,我的暴击机率增加20%会怎样 :-)#{"暴击机率": +0.20},]# 面板:默认怪等级与人物当前等级一致,例如我:60级。# 炼狱:炼狱没有60级怪:(A1)61级, (A2)62级, (A3)(A4)63级。# 看着面板数据打炼狱的人永远不知道现实有多残酷。monster_level = props["等级"] # 炼狱生活请自理# 打印报表print apply_diff_list(props, diff_list, monster_level)
最后,吐槽时间:
Diablo3的装备系统在跟随者频繁切换时存盘有bug呀,
我测试过程中两件稀有装备永久性突变级别和属性,还有一次全身装备短暂丢失。
损失几千万金币,想去投诉索赔,但是去官网论坛一看又觉得很惭愧,
还是别给运营大爷们添乱了:丢失了几亿的帖子都几个月没处理了呢。