diff --git a/bin/mhdamage.py b/bin/mhdamage.py index 1531542..42b5c4a 100755 --- a/bin/mhdamage.py +++ b/bin/mhdamage.py @@ -186,6 +186,9 @@ def parse_args(argv): parser.add_argument("-p", "--parts", help="Limit analysis to specified parts" +" (comma separated list)") + parser.add_argument("-o", "--motion", type=int, + help="Use specified motion value instead of weapon " + +"average") parser.add_argument("-l", "--phial", help="Show CB phial damage at the sepcified level" +" (1, 2, 3, 5=ultra) instead of normal motion" @@ -290,6 +293,24 @@ def print_sorted_damage(names, damage_map_base, weapon_damage_map, parts): print "% 2d" % part_damage.average(), print + if len(names) > 1: + w1 = weapon_damage_map[names_sorted[0]] + w2 = weapon_damage_map[names_sorted[1]] + m, ratio = w1.compare_break_even(w2) + print + print "Comparison of '%s' and '%s'" % ( + names_sorted[0], names_sorted[1]) + print "Hitbox ratio:", m, "%0.2f" % ratio + + for line in w1.get_raw_element_ratios(): + line = list(line) + if m*line[3] > m*ratio: + line.append(names_sorted[0]) + else: + line.append(names_sorted[1]) + # (part, raw, element, ratio) + print "%-22s %02d %02d %0.2f %s" % tuple(line) + def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts): for part in parts: @@ -432,6 +453,9 @@ def main(): motion = motiondb[weapon_type].average print "Weapon Type: %s" % weapon_type print "Average Motion: %0.1f" % motion + if args.motion: + motion = args.motion + print "Specified Motion: %0.1f" % motion print "Monster Breaks: %s" % ", ".join(monster_breaks) skill_names = get_skill_names(args) print "Common Skills:", ", ".join(skill for skill in skill_names if skill) diff --git a/mhapi/damage.py b/mhapi/damage.py index 9b7875e..8862ece 100644 --- a/mhapi/damage.py +++ b/mhapi/damage.py @@ -21,24 +21,40 @@ def raw_damage(true_raw, sharpness, affinity, monster_hitbox, motion): Calculate raw damage to a monster part with the given true raw, sharpness, monster raw weakness, and weapon motion value. """ - return floor(true_raw - * SharpnessLevel.raw_modifier(sharpness) - * (1 + (affinity / 400.0)) - * motion / 100.0 + return floor(raw_damage_nohitbox(true_raw, sharpness, affinity, motion) * monster_hitbox / 100.0) +def raw_damage_nohitbox(true_raw, sharpness, affinity, motion): + """ + Calculate raw damage to a monster part with the given true raw, + sharpness, monster raw weakness, and weapon motion value. + """ + return (true_raw + * SharpnessLevel.raw_modifier(sharpness) + * (1 + (affinity / 400.0)) + * motion / 100.0) + + def element_damage(raw_element, sharpness, monster_ehitbox): """ Calculate elemental damage to a monster part with the given elemental attack, the given sharpness, and the given monster elemental weakness. Note that this is independent of the motion value of the attack. """ - return floor(raw_element - * SharpnessLevel.element_modifier(sharpness) + return floor(element_damage_nohitbox(raw_element, sharpness) * monster_ehitbox / 100.0) +def element_damage_nohitbox(raw_element, sharpness): + """ + Calculate elemental damage to a monster part with the given elemental + attack, the given sharpness, and the given monster elemental weakness. + Note that this is independent of the motion value of the attack. + """ + return (raw_element * SharpnessLevel.element_modifier(sharpness)) + + class MotionType(object): CUT = "cut" IMPACT = "impact" @@ -506,6 +522,52 @@ class WeaponMonsterDamage(object): average += damage.total return average / self.break_count + def compare_break_even(self, other_wd, motion=None): + """ + Compare with another weapon damage, to determine the break even + point of the raw/element hitbox. Assumes same element. + + Returns (m, ratio), where ratio is the break even for the ratio of + raw hitbox to element hitbox. If m is 1, then self is better + when the ratio is larger (favors raw). If m is -1, then self is + better when the ratio is smaller (favors element). + """ + r1, e1 = self.nohitbox_damage(motion) + r2, e2 = other_wd.nohitbox_damage(motion) + #print r1, e1 + #print r2, e2 + rdiff = r1 - r2 + ediff = e2 - e1 + m = 1 + if rdiff < 0: + m = -1 + return m, (float(ediff) / rdiff) + + def get_raw_element_ratios(self): + # TODO: better MIXED handling + if self.damage_type in (WeaponType.CUT, WeaponType.MIXED): + raw_type = "cut" + else: + raw_type = "impact" + hitboxes = [] + for row in self.monster_damage._rows: + part = row["body_part"] + hitbox = int(row[raw_type]) + ehitbox = int(row[str(self.etype.lower())]) + hitboxes.append((part, hitbox, ehitbox, float(hitbox) / ehitbox)) + return hitboxes + + def nohitbox_damage(self, motion=None): + """ + Note: uses first element only, so not good for dual element DB. + """ + if motion is None: + motion = self.motion + raw = raw_damage_nohitbox(self.true_raw, self.sharpness, + self.affinity, motion) + element = element_damage_nohitbox(self.eattack, self.sharpness) + return (raw, element) + def __getitem__(self, key): return self.damage_map[key]