damage: add match option, part filter, cb phial
This commit is contained in:
118
bin/mhdamage.py
118
bin/mhdamage.py
@@ -9,6 +9,44 @@ from mhapi.db import MHDB
|
|||||||
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
|
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
|
||||||
from mhapi.model import SharpnessLevel
|
from mhapi.model import SharpnessLevel
|
||||||
from mhapi import skills
|
from mhapi import skills
|
||||||
|
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR
|
||||||
|
|
||||||
|
|
||||||
|
def weapon_match_tuple(arg):
|
||||||
|
parts = arg.split(",")
|
||||||
|
if len(parts) == 1:
|
||||||
|
wtype = parts[0]
|
||||||
|
element = None
|
||||||
|
elif len(parts) == 2:
|
||||||
|
wtype = parts[0]
|
||||||
|
element = parts[1]
|
||||||
|
else:
|
||||||
|
raise ValueError("Bad arg, use 'weapon_type,element_or_status'")
|
||||||
|
wtype = get_wtype_match(wtype)
|
||||||
|
if element is not None:
|
||||||
|
element = get_element_match(element)
|
||||||
|
return (wtype, element)
|
||||||
|
|
||||||
|
|
||||||
|
def get_wtype_match(term):
|
||||||
|
abbr_result = WTYPE_ABBR.get(term.upper())
|
||||||
|
if abbr_result is not None:
|
||||||
|
return abbr_result
|
||||||
|
term = term.title()
|
||||||
|
for wtype in WEAPON_TYPES:
|
||||||
|
if wtype.startswith(term):
|
||||||
|
return wtype
|
||||||
|
raise ValueError("Unknown weapon type: %s" % term)
|
||||||
|
|
||||||
|
|
||||||
|
def get_element_match(term):
|
||||||
|
term = term.title()
|
||||||
|
for element in ELEMENTS:
|
||||||
|
if element.startswith(term):
|
||||||
|
return element
|
||||||
|
if term.lower() == "raw":
|
||||||
|
return "Raw"
|
||||||
|
raise ValueError("Unknown element or status: %s" % term)
|
||||||
|
|
||||||
|
|
||||||
def percent_change(a, b):
|
def percent_change(a, b):
|
||||||
@@ -31,18 +69,35 @@ def parse_args(argv):
|
|||||||
default=False,
|
default=False,
|
||||||
help="add Awaken (FreeElement), default off")
|
help="add Awaken (FreeElement), default off")
|
||||||
parser.add_argument("-a", "--attack-up",
|
parser.add_argument("-a", "--attack-up",
|
||||||
type=int, choices=xrange(0, 5), default=0,
|
type=int, choices=range(0, 5), default=0,
|
||||||
help="1-4 for AuS, M, L, XL")
|
help="1-4 for AuS, M, L, XL")
|
||||||
parser.add_argument("-c", "--critical-eye",
|
parser.add_argument("-c", "--critical-eye",
|
||||||
type=int, choices=xrange(0, 5), default=0,
|
type=int, choices=range(0, 5), default=0,
|
||||||
help="1-4 for CE+1, +2, +3 and Critical God")
|
help="1-4 for CE+1, +2, +3 and Critical God")
|
||||||
parser.add_argument("-e", "--element-up",
|
parser.add_argument("-e", "--element-up",
|
||||||
type=int, choices=xrange(0, 5), default=0,
|
type=int, choices=range(0, 5), default=0,
|
||||||
help="1-4 for (element) Atk +1, +2, +3 and"
|
help="1-4 for (element) Atk +1, +2, +3 and"
|
||||||
" Element Attack Up")
|
" Element Attack Up")
|
||||||
|
parser.add_argument("-t", "--artillery",
|
||||||
|
type=int, choices=[0,1,2], default=0,
|
||||||
|
help="0-2 for no artillery, novice, god")
|
||||||
|
parser.add_argument("-p", "--parts",
|
||||||
|
help="Limit analysis to specified parts"
|
||||||
|
+" (comma separated list)")
|
||||||
|
parser.add_argument("-m", "--match", nargs="*",
|
||||||
|
help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW"
|
||||||
|
+" Include all matching weapons in their final form."
|
||||||
|
+" Supports abbreviations like LS for Long Sword"
|
||||||
|
+" and Para for Paralysis or Blast for Blastblight."
|
||||||
|
+" If just WEAPON_TYPE is given, include all final"
|
||||||
|
+" weapons of that type."
|
||||||
|
+" Examples: 'Great Sword,Raw'"
|
||||||
|
+" 'Sword and Shield,Para'"
|
||||||
|
+" 'HH,Blast' 'Hammer'",
|
||||||
|
type=weapon_match_tuple, default=[])
|
||||||
parser.add_argument("monster",
|
parser.add_argument("monster",
|
||||||
help="Full name of monster")
|
help="Full name of monster")
|
||||||
parser.add_argument("weapon", nargs="+",
|
parser.add_argument("weapon", nargs="*",
|
||||||
help="One or more weapons of same class to compare,"
|
help="One or more weapons of same class to compare,"
|
||||||
" full names")
|
" full names")
|
||||||
|
|
||||||
@@ -59,13 +114,29 @@ if __name__ == '__main__':
|
|||||||
if not monster:
|
if not monster:
|
||||||
raise ValueError("Monster '%s' not found" % args.monster)
|
raise ValueError("Monster '%s' not found" % args.monster)
|
||||||
monster_damage = db.get_monster_damage(monster.id)
|
monster_damage = db.get_monster_damage(monster.id)
|
||||||
|
|
||||||
weapons = []
|
weapons = []
|
||||||
|
weapon_type = None
|
||||||
|
|
||||||
|
for match_tuple in args.match:
|
||||||
|
# TODO: better validation
|
||||||
|
wtype, element = match_tuple
|
||||||
|
match_weapons = db.get_weapons_by_query(wtype=wtype, element=element,
|
||||||
|
final=1)
|
||||||
|
weapons.extend(match_weapons)
|
||||||
|
|
||||||
for name in args.weapon:
|
for name in args.weapon:
|
||||||
weapon = db.get_weapon_by_name(name)
|
weapon = db.get_weapon_by_name(name)
|
||||||
if not weapon:
|
if not weapon:
|
||||||
raise ValueError("Weapon '%s' not found" % name)
|
raise ValueError("Weapon '%s' not found" % name)
|
||||||
weapons.append(weapon)
|
weapons.append(weapon)
|
||||||
|
|
||||||
|
if not weapons:
|
||||||
|
print "Err: no matching weapons"
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
names = [w.name for w in weapons]
|
||||||
|
|
||||||
monster_breaks = db.get_monster_breaks(monster.id)
|
monster_breaks = db.get_monster_breaks(monster.id)
|
||||||
weapon_type = weapons[0]["wtype"]
|
weapon_type = weapons[0]["wtype"]
|
||||||
motion = motiondb[weapon_type].average
|
motion = motiondb[weapon_type].average
|
||||||
@@ -78,8 +149,15 @@ if __name__ == '__main__':
|
|||||||
skills.CriticalEye.name(args.critical_eye),
|
skills.CriticalEye.name(args.critical_eye),
|
||||||
skills.ElementAttackUp.name(args.element_up)]
|
skills.ElementAttackUp.name(args.element_up)]
|
||||||
print "Skills:", ", ".join(skill for skill in skill_names if skill)
|
print "Skills:", ", ".join(skill for skill in skill_names if skill)
|
||||||
|
|
||||||
|
if args.parts:
|
||||||
|
limit_parts = args.parts.split(",")
|
||||||
|
else:
|
||||||
|
limit_parts = None
|
||||||
|
|
||||||
weapon_damage_map = dict()
|
weapon_damage_map = dict()
|
||||||
for name, row in zip(args.weapon, weapons):
|
for row in weapons:
|
||||||
|
name = row["name"]
|
||||||
row_type = row["wtype"]
|
row_type = row["wtype"]
|
||||||
if row_type != weapon_type:
|
if row_type != weapon_type:
|
||||||
raise ValueError("Weapon '%s' is different type" % name)
|
raise ValueError("Weapon '%s' is different type" % name)
|
||||||
@@ -91,9 +169,15 @@ if __name__ == '__main__':
|
|||||||
attack_skill=args.attack_up,
|
attack_skill=args.attack_up,
|
||||||
critical_eye_skill=args.critical_eye,
|
critical_eye_skill=args.critical_eye,
|
||||||
element_skill=args.element_up,
|
element_skill=args.element_up,
|
||||||
awaken=args.awaken)
|
awaken=args.awaken,
|
||||||
|
artillery_level=args.artillery,
|
||||||
|
limit_parts=args.parts)
|
||||||
print "%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity),
|
print "%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity),
|
||||||
if wd.etype:
|
if wd.etype:
|
||||||
|
if wd.etype2:
|
||||||
|
print "(%4.0f %s, %4.0f %s)" \
|
||||||
|
% (wd.eattack, wd.etype, wd.eattack2, wd.etype2),
|
||||||
|
else:
|
||||||
print "(%4.0f %s)" % (wd.eattack, wd.etype),
|
print "(%4.0f %s)" % (wd.eattack, wd.etype),
|
||||||
print SharpnessLevel.name(wd.sharpness)
|
print SharpnessLevel.name(wd.sharpness)
|
||||||
weapon_damage_map[name] = wd
|
weapon_damage_map[name] = wd
|
||||||
@@ -101,7 +185,11 @@ if __name__ == '__main__':
|
|||||||
print str(e)
|
print str(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
damage_map_base = weapon_damage_map[args.weapon[0]]
|
damage_map_base = weapon_damage_map[weapons[0].name]
|
||||||
|
|
||||||
|
if limit_parts:
|
||||||
|
parts = limit_parts
|
||||||
|
else:
|
||||||
parts = damage_map_base.parts
|
parts = damage_map_base.parts
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@@ -109,17 +197,17 @@ if __name__ == '__main__':
|
|||||||
damage_map_base[part].total,
|
damage_map_base[part].total,
|
||||||
weapon_damage_map[w][part].total
|
weapon_damage_map[w][part].total
|
||||||
)
|
)
|
||||||
for w in args.weapon[1:]]
|
for w in names[1:]]
|
||||||
ediffs = [percent_change(
|
ediffs = [percent_change(
|
||||||
damage_map_base[part].element,
|
damage_map_base[part].element,
|
||||||
weapon_damage_map[w][part].element
|
weapon_damage_map[w][part].element
|
||||||
)
|
)
|
||||||
for w in args.weapon[1:]]
|
for w in names[1:]]
|
||||||
bdiffs = [percent_change(
|
bdiffs = [percent_change(
|
||||||
damage_map_base[part].break_diff(),
|
damage_map_base[part].break_diff(),
|
||||||
weapon_damage_map[w][part].break_diff()
|
weapon_damage_map[w][part].break_diff()
|
||||||
)
|
)
|
||||||
for w in args.weapon[1:]]
|
for w in names[1:]]
|
||||||
tdiff_s = ",".join("%+0.1f%%" % i for i in tdiffs)
|
tdiff_s = ",".join("%+0.1f%%" % i for i in tdiffs)
|
||||||
ediff_s = ",".join("%+0.1f%%" % i for i in ediffs)
|
ediff_s = ",".join("%+0.1f%%" % i for i in ediffs)
|
||||||
bdiff_s = ",".join("%+0.1f%%" % i for i in bdiffs)
|
bdiff_s = ",".join("%+0.1f%%" % i for i in bdiffs)
|
||||||
@@ -134,6 +222,14 @@ if __name__ == '__main__':
|
|||||||
ediff_s,
|
ediff_s,
|
||||||
damage.break_diff(),
|
damage.break_diff(),
|
||||||
bdiff_s)
|
bdiff_s)
|
||||||
|
if weapon_type == "Charge Blade":
|
||||||
|
for level in (0, 1, 2, 3, 5):
|
||||||
|
print " " * 20, level,
|
||||||
|
for wname in names:
|
||||||
|
wd = weapon_damage_map[wname]
|
||||||
|
damage = wd.cb_phial_damage[part][level]
|
||||||
|
print "(%0.f, %0.f, %0.f);" % damage,
|
||||||
|
print
|
||||||
|
|
||||||
print " --------------------"
|
print " --------------------"
|
||||||
|
|
||||||
@@ -143,7 +239,7 @@ if __name__ == '__main__':
|
|||||||
base,
|
base,
|
||||||
weapon_damage_map[w].averages[avg_type]
|
weapon_damage_map[w].averages[avg_type]
|
||||||
)
|
)
|
||||||
for w in args.weapon[1:]]
|
for w in names[1:]]
|
||||||
|
|
||||||
diff_s = ",".join("%+0.1f%%" % i for i in diffs)
|
diff_s = ",".join("%+0.1f%%" % i for i in diffs)
|
||||||
|
|
||||||
|
|||||||
157
mhapi/damage.py
157
mhapi/damage.py
@@ -11,6 +11,7 @@ from mhapi.model import SharpnessLevel, _break_find
|
|||||||
WEAKPART_WEIGHT = 0.5
|
WEAKPART_WEIGHT = 0.5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def raw_damage(true_raw, sharpness, affinity, monster_hitbox, motion):
|
def raw_damage(true_raw, sharpness, affinity, monster_hitbox, motion):
|
||||||
"""
|
"""
|
||||||
Calculate raw damage to a monster part with the given true raw,
|
Calculate raw damage to a monster part with the given true raw,
|
||||||
@@ -165,7 +166,7 @@ class WeaponMonsterDamage(object):
|
|||||||
attack_skill=skills.AttackUp.NONE,
|
attack_skill=skills.AttackUp.NONE,
|
||||||
critical_eye_skill=skills.CriticalEye.NONE,
|
critical_eye_skill=skills.CriticalEye.NONE,
|
||||||
element_skill=skills.ElementAttackUp.NONE,
|
element_skill=skills.ElementAttackUp.NONE,
|
||||||
awaken=False):
|
awaken=False, artillery_level=0, limit_parts=None):
|
||||||
self.weapon = weapon_row
|
self.weapon = weapon_row
|
||||||
self.monster = monster_row
|
self.monster = monster_row
|
||||||
self.monster_damage = monster_damage
|
self.monster_damage = monster_damage
|
||||||
@@ -176,6 +177,8 @@ class WeaponMonsterDamage(object):
|
|||||||
self.critical_eye_skill = critical_eye_skill
|
self.critical_eye_skill = critical_eye_skill
|
||||||
self.element_skill = element_skill
|
self.element_skill = element_skill
|
||||||
self.awaken = awaken
|
self.awaken = awaken
|
||||||
|
self.artillery_level = artillery_level
|
||||||
|
self.limit_parts = limit_parts
|
||||||
|
|
||||||
self.damage_map = defaultdict(PartDamage)
|
self.damage_map = defaultdict(PartDamage)
|
||||||
self.average = 0
|
self.average = 0
|
||||||
@@ -183,6 +186,9 @@ class WeaponMonsterDamage(object):
|
|||||||
self.best_weighted = 0
|
self.best_weighted = 0
|
||||||
self.break_weighted = 0
|
self.break_weighted = 0
|
||||||
|
|
||||||
|
# map of part -> (map of burst_level -> (raw, ele, burst))
|
||||||
|
self.cb_phial_damage = defaultdict(dict)
|
||||||
|
|
||||||
self.weapon_type = self.weapon["wtype"]
|
self.weapon_type = self.weapon["wtype"]
|
||||||
self.true_raw = (self.weapon["attack"]
|
self.true_raw = (self.weapon["attack"]
|
||||||
/ WeaponType.multiplier(self.weapon_type))
|
/ WeaponType.multiplier(self.weapon_type))
|
||||||
@@ -202,6 +208,8 @@ class WeaponMonsterDamage(object):
|
|||||||
self.damage_type = WeaponType.damage_type(self.weapon_type)
|
self.damage_type = WeaponType.damage_type(self.weapon_type)
|
||||||
self.etype = self.weapon["element"]
|
self.etype = self.weapon["element"]
|
||||||
self.eattack = self.weapon["element_attack"]
|
self.eattack = self.weapon["element_attack"]
|
||||||
|
self.etype2 = self.weapon["element_2"]
|
||||||
|
self.eattack2 = self.weapon["element_2_attack"]
|
||||||
if not self.etype and self.awaken:
|
if not self.etype and self.awaken:
|
||||||
self.etype = self.weapon.awaken
|
self.etype = self.weapon.awaken
|
||||||
self.eattack = self.weapon.awaken_attack
|
self.eattack = self.weapon.awaken_attack
|
||||||
@@ -210,6 +218,10 @@ class WeaponMonsterDamage(object):
|
|||||||
self.eattack = int(self.eattack)
|
self.eattack = int(self.eattack)
|
||||||
else:
|
else:
|
||||||
self.eattack = 0
|
self.eattack = 0
|
||||||
|
if self.eattack2:
|
||||||
|
self.eattack2 = int(self.eattack2)
|
||||||
|
else:
|
||||||
|
self.eattack2 = 0
|
||||||
|
|
||||||
self.true_raw = skills.AttackUp.modified(attack_skill,
|
self.true_raw = skills.AttackUp.modified(attack_skill,
|
||||||
self.true_raw)
|
self.true_raw)
|
||||||
@@ -217,6 +229,8 @@ class WeaponMonsterDamage(object):
|
|||||||
self.affinity)
|
self.affinity)
|
||||||
self.eattack = skills.ElementAttackUp.modified(element_skill,
|
self.eattack = skills.ElementAttackUp.modified(element_skill,
|
||||||
self.eattack)
|
self.eattack)
|
||||||
|
self.eattack2 = skills.ElementAttackUp.modified(element_skill,
|
||||||
|
self.eattack2)
|
||||||
|
|
||||||
self.parts = []
|
self.parts = []
|
||||||
self.break_count = 0
|
self.break_count = 0
|
||||||
@@ -228,8 +242,8 @@ class WeaponMonsterDamage(object):
|
|||||||
weakpart_raw=0,
|
weakpart_raw=0,
|
||||||
weakpart_element=0,
|
weakpart_element=0,
|
||||||
)
|
)
|
||||||
self.max_raw_part = (None, 0)
|
self.max_raw_part = (None, -1)
|
||||||
self.max_element_part = (None, 0)
|
self.max_element_part = (None, -1)
|
||||||
self._calculate_damage()
|
self._calculate_damage()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -245,6 +259,9 @@ class WeaponMonsterDamage(object):
|
|||||||
if m:
|
if m:
|
||||||
part = m.group(1)
|
part = m.group(1)
|
||||||
alt = m.group(2)
|
alt = m.group(2)
|
||||||
|
|
||||||
|
if self.limit_parts is not None and part not in self.limit_parts:
|
||||||
|
continue
|
||||||
#print part, alt
|
#print part, alt
|
||||||
hitbox = 0
|
hitbox = 0
|
||||||
hitbox_cut = int(row["cut"])
|
hitbox_cut = int(row["cut"])
|
||||||
@@ -266,6 +283,14 @@ class WeaponMonsterDamage(object):
|
|||||||
if self.etype in "Fire Water Ice Thunder Dragon".split():
|
if self.etype in "Fire Water Ice Thunder Dragon".split():
|
||||||
ehitbox = int(row[str(self.etype.lower())])
|
ehitbox = int(row[str(self.etype.lower())])
|
||||||
element = element_damage(self.eattack, self.sharpness, ehitbox)
|
element = element_damage(self.eattack, self.sharpness, ehitbox)
|
||||||
|
if self.etype2:
|
||||||
|
# handle dual blades double element/status
|
||||||
|
element = element / 2.0
|
||||||
|
if self.etype2 in "Fire Water Ice Thunder Dragon".split():
|
||||||
|
ehitbox2 = int(row[str(self.etype2.lower())])
|
||||||
|
element2 = element_damage(self.eattack2,
|
||||||
|
self.sharpness, ehitbox2)
|
||||||
|
element += element2 / 2.0
|
||||||
|
|
||||||
part_damage = self.damage_map[part]
|
part_damage = self.damage_map[part]
|
||||||
part_damage.set_damage(raw, element, hitbox, ehitbox, state=alt)
|
part_damage.set_damage(raw, element, hitbox, ehitbox, state=alt)
|
||||||
@@ -292,6 +317,25 @@ class WeaponMonsterDamage(object):
|
|||||||
self.averages["break_raw"] = self.break_weakpart_raw()
|
self.averages["break_raw"] = self.break_weakpart_raw()
|
||||||
self.averages["break_element"] = self.break_weakpart_element()
|
self.averages["break_element"] = self.break_weakpart_element()
|
||||||
self.averages["break_only"] = self.break_only()
|
self.averages["break_only"] = self.break_only()
|
||||||
|
self._calculate_cb_phial_damage()
|
||||||
|
|
||||||
|
def _calculate_cb_phial_damage(self):
|
||||||
|
if self.weapon_type != "Charge Blade":
|
||||||
|
return
|
||||||
|
if self.weapon.phial == "Impact":
|
||||||
|
fn = cb_impact_phial_damage
|
||||||
|
else:
|
||||||
|
fn = cb_element_phial_damage
|
||||||
|
for part in self.parts:
|
||||||
|
part_damage = self.damage_map[part]
|
||||||
|
hitbox = part_damage.hitbox
|
||||||
|
ehitbox = part_damage.ehitbox
|
||||||
|
for level in (0, 1, 2, 3, 5):
|
||||||
|
damage_tuple = fn(self.true_raw, self.eattack, self.sharpness,
|
||||||
|
self.affinity, hitbox, ehitbox, level,
|
||||||
|
shield_charged=True,
|
||||||
|
artillery_level=self.artillery_level)
|
||||||
|
self.cb_phial_damage[part][level] = damage_tuple
|
||||||
|
|
||||||
def uniform(self):
|
def uniform(self):
|
||||||
average = 0.0
|
average = 0.0
|
||||||
@@ -328,6 +372,10 @@ class WeaponMonsterDamage(object):
|
|||||||
return average / total_ehitbox
|
return average / total_ehitbox
|
||||||
|
|
||||||
def weakpart_weighted_raw(self, weak_weight=WEAKPART_WEIGHT):
|
def weakpart_weighted_raw(self, weak_weight=WEAKPART_WEIGHT):
|
||||||
|
if len(self.parts) == 1:
|
||||||
|
other_weight = 0
|
||||||
|
weak_weight = 1
|
||||||
|
else:
|
||||||
other_weight = (1 - weak_weight) / (len(self.parts) - 1)
|
other_weight = (1 - weak_weight) / (len(self.parts) - 1)
|
||||||
average = 0
|
average = 0
|
||||||
for part, damage in self.damage_map.iteritems():
|
for part, damage in self.damage_map.iteritems():
|
||||||
@@ -339,6 +387,10 @@ class WeaponMonsterDamage(object):
|
|||||||
return average
|
return average
|
||||||
|
|
||||||
def weakpart_weighted_element(self, weak_weight=WEAKPART_WEIGHT):
|
def weakpart_weighted_element(self, weak_weight=WEAKPART_WEIGHT):
|
||||||
|
if len(self.parts) == 1:
|
||||||
|
other_weight = 0
|
||||||
|
weak_weight = 1
|
||||||
|
else:
|
||||||
other_weight = (1 - weak_weight) / (len(self.parts) - 1)
|
other_weight = (1 - weak_weight) / (len(self.parts) - 1)
|
||||||
average = 0
|
average = 0
|
||||||
for part, damage in self.damage_map.iteritems():
|
for part, damage in self.damage_map.iteritems():
|
||||||
@@ -534,3 +586,102 @@ def element_x_attack_up(value, level=1):
|
|||||||
value += 90
|
value += 90
|
||||||
else:
|
else:
|
||||||
raise ValueError("level must be 1, 2, or 3")
|
raise ValueError("level must be 1, 2, or 3")
|
||||||
|
|
||||||
|
|
||||||
|
def cb_impact_phial_damage(true_raw, element, sharpness, affinity,
|
||||||
|
monster_hitbox, monster_ehitbox,
|
||||||
|
burst_level, artillery_level=0,
|
||||||
|
shield_charged=False):
|
||||||
|
"""
|
||||||
|
@burst_level: 0 for shield thrust, 1 for side chop, 2 for double swing,
|
||||||
|
3 for AED, 5 for super AED w/ 5 phials
|
||||||
|
@artillery_level: 1 for Novice, 2 for God or Novice + Felyne Bombardier
|
||||||
|
|
||||||
|
See
|
||||||
|
https://www.reddit.com/r/MonsterHunter/comments/391a5i/mh4u_charge_blade_phial_damage/
|
||||||
|
|
||||||
|
Note this contradicts data from the other link, but this is more recent.
|
||||||
|
"""
|
||||||
|
motions = _cb_get_motions(burst_level, shield_charged)
|
||||||
|
if burst_level == 5:
|
||||||
|
multiplier = 0.33
|
||||||
|
elif burst_level == 3:
|
||||||
|
multiplier = 0.1
|
||||||
|
else:
|
||||||
|
multiplier = 0.05
|
||||||
|
|
||||||
|
if artillery_level == 1:
|
||||||
|
multiplier *= 1.3
|
||||||
|
elif artillery_level == 2:
|
||||||
|
multiplier *= 1.4
|
||||||
|
elif artillery_level != 0:
|
||||||
|
raise ValueError("artillery_level must be 0, 1 (Novice), or 2 (God)")
|
||||||
|
|
||||||
|
if shield_charged and burst_level != 5:
|
||||||
|
multiplier *= 1.3
|
||||||
|
|
||||||
|
if shield_charged and burst_level == 0:
|
||||||
|
# Shield Thrust gets one blast if shield is charged
|
||||||
|
burst_level = 1
|
||||||
|
|
||||||
|
# burst damage is fixed, doesn't depend on monster hitbox
|
||||||
|
burst_dmg = true_raw * multiplier * burst_level
|
||||||
|
raw_dmg = sum([raw_damage(true_raw, sharpness, affinity, monster_hitbox,
|
||||||
|
motion)
|
||||||
|
for motion in motions])
|
||||||
|
ele_dmg = (element_damage(element, sharpness, monster_ehitbox)
|
||||||
|
* len(motions))
|
||||||
|
return (raw_dmg, ele_dmg, burst_dmg)
|
||||||
|
|
||||||
|
|
||||||
|
def cb_element_phial_damage(true_raw, element, sharpness, affinity,
|
||||||
|
monster_hitbox, monster_ehitbox,
|
||||||
|
burst_level, artillery_level=0,
|
||||||
|
shield_charged=False):
|
||||||
|
motions = _cb_get_motions(burst_level, shield_charged)
|
||||||
|
|
||||||
|
if burst_level == 5:
|
||||||
|
multiplier = 4.5 * 3
|
||||||
|
elif burst_level == 3:
|
||||||
|
multiplier = 4.5
|
||||||
|
else:
|
||||||
|
multiplier = 3
|
||||||
|
|
||||||
|
if shield_charged and burst_level != 5:
|
||||||
|
multiplier *= 1.35
|
||||||
|
|
||||||
|
if shield_charged and burst_level == 0:
|
||||||
|
# Shield Thrust gets one blast if shield is charged
|
||||||
|
burst_level = 1
|
||||||
|
|
||||||
|
burst_dmg = (element / 10.0 * multiplier * burst_level
|
||||||
|
* monster_ehitbox / 100.0)
|
||||||
|
raw_dmg = sum([raw_damage(true_raw, sharpness, affinity, monster_hitbox,
|
||||||
|
motion)
|
||||||
|
for motion in motions])
|
||||||
|
ele_dmg = (element_damage(element, sharpness, monster_ehitbox)
|
||||||
|
* len(motions))
|
||||||
|
return (raw_dmg, ele_dmg, burst_dmg)
|
||||||
|
|
||||||
|
|
||||||
|
def _cb_get_motions(burst_level, shield_charged):
|
||||||
|
# See https://www.reddit.com/r/MonsterHunter/comments/2ue8qw/charge_blade_attack_motion_values/
|
||||||
|
if burst_level == 0:
|
||||||
|
# Shield Thrust
|
||||||
|
motions = [8, 12]
|
||||||
|
elif burst_level == 1:
|
||||||
|
# Burst Side Chop
|
||||||
|
motions = [31] if shield_charged else [26]
|
||||||
|
elif burst_level == 2:
|
||||||
|
# Double Side Swing
|
||||||
|
motions = [21, 96] if shield_charged else [18, 80]
|
||||||
|
elif burst_level == 3:
|
||||||
|
# AED or Super Burst
|
||||||
|
motions = [108] if shield_charged else [90]
|
||||||
|
elif burst_level == 5:
|
||||||
|
# super AED or Ultra Burst, 5 phials filled
|
||||||
|
# Note: w/o phials it's [17, 90], but that is very rarely used
|
||||||
|
motions = [25, 99, 100]
|
||||||
|
else:
|
||||||
|
raise ValueError("burst_level must be 0, 1, 2, 3, or 5 (Super AED)")
|
||||||
|
return motions
|
||||||
|
|||||||
@@ -5,6 +5,54 @@ Shared utility classes and functions.
|
|||||||
import codecs
|
import codecs
|
||||||
|
|
||||||
|
|
||||||
|
ELEMENTS = """
|
||||||
|
Fire
|
||||||
|
Water
|
||||||
|
Thunder
|
||||||
|
Ice
|
||||||
|
Dragon
|
||||||
|
Poison
|
||||||
|
Paralysis
|
||||||
|
Sleep
|
||||||
|
Blastblight
|
||||||
|
""".split()
|
||||||
|
|
||||||
|
|
||||||
|
WEAPON_TYPES = [
|
||||||
|
"Great Sword",
|
||||||
|
"Long Sword",
|
||||||
|
"Sword and Shield",
|
||||||
|
"Dual Blades",
|
||||||
|
"Hammer",
|
||||||
|
"Hunting Horn",
|
||||||
|
"Lance",
|
||||||
|
"Gunlance",
|
||||||
|
"Switch Axe",
|
||||||
|
"Charge Blade",
|
||||||
|
"Insect Glaive",
|
||||||
|
"Light Bowgun",
|
||||||
|
"Heavy Bowgun",
|
||||||
|
"Bow",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
WTYPE_ABBR = dict(
|
||||||
|
GS="Great Sword",
|
||||||
|
LS="Long Sword",
|
||||||
|
SS="Sword and Shield",
|
||||||
|
SNS="Sword and Shield",
|
||||||
|
DB="Dual Blades",
|
||||||
|
HH="Hunting Horn",
|
||||||
|
LA="Lance",
|
||||||
|
GL="Gunlance",
|
||||||
|
SA="Switch Axe",
|
||||||
|
CB="Charge Blade",
|
||||||
|
IG="Insect Glave",
|
||||||
|
LBG="Light Bowgun",
|
||||||
|
HBG="Heavy Bowgun"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EnumBase(object):
|
class EnumBase(object):
|
||||||
_names = dict()
|
_names = dict()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user