add mhx damage
This commit is contained in:
@@ -7,8 +7,8 @@ import copy
|
|||||||
|
|
||||||
import _pathfix
|
import _pathfix
|
||||||
|
|
||||||
from mhapi.db import MHDB
|
from mhapi.db import MHDB, MHDBX
|
||||||
from mhapi.damage import MotionValueDB, WeaponMonsterDamage, WeaponType
|
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
|
||||||
from mhapi.model import SharpnessLevel, Weapon
|
from mhapi.model import SharpnessLevel, Weapon
|
||||||
from mhapi import skills
|
from mhapi import skills
|
||||||
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR
|
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR
|
||||||
@@ -31,30 +31,29 @@ def weapon_match_tuple(arg):
|
|||||||
|
|
||||||
|
|
||||||
def _make_db_sharpness_string(level_string):
|
def _make_db_sharpness_string(level_string):
|
||||||
print "level string", level_string
|
#print "level string", level_string
|
||||||
level_value = SharpnessLevel.__dict__[level_string.upper()]
|
level_value = SharpnessLevel.__dict__[level_string.upper()]
|
||||||
print "level value", level_value
|
#print "level value", level_value
|
||||||
values = []
|
values = []
|
||||||
for i in xrange(SharpnessLevel.PURPLE+1):
|
for i in xrange(SharpnessLevel.PURPLE+1):
|
||||||
if i <= level_value:
|
if i <= level_value:
|
||||||
values.append("1")
|
values.append("1")
|
||||||
else:
|
else:
|
||||||
values.append("0")
|
values.append("0")
|
||||||
print "sharp values %r" % values
|
#print "sharp values %r" % values
|
||||||
return " ".join([".".join(values)] * 2)
|
return " ".join([".".join(values)] * 2)
|
||||||
|
|
||||||
|
|
||||||
def weapon_stats_tuple(arg):
|
def weapon_stats_tuple(arg):
|
||||||
parts = arg.split(",")
|
parts = arg.split(",")
|
||||||
print "parts %r" % parts
|
#print "parts %r" % parts
|
||||||
if len(parts) < 4:
|
if len(parts) < 4:
|
||||||
print "not enough parts"
|
print "not enough parts"
|
||||||
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
|
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
|
||||||
weapon = {}
|
weapon = {}
|
||||||
weapon["name"] = parts[0]
|
weapon["name"] = parts[0]
|
||||||
weapon["wtype"] = get_wtype_match(parts[1])
|
weapon["wtype"] = get_wtype_match(parts[1])
|
||||||
multiplier = WeaponType.multiplier(weapon["wtype"])
|
weapon["attack"] = int(parts[2])
|
||||||
weapon["attack"] = multiplier * int(parts[2])
|
|
||||||
weapon["affinity"] = parts[3]
|
weapon["affinity"] = parts[3]
|
||||||
weapon["sharpness"] = _make_db_sharpness_string(parts[4])
|
weapon["sharpness"] = _make_db_sharpness_string(parts[4])
|
||||||
if len(parts) == 5:
|
if len(parts) == 5:
|
||||||
@@ -64,13 +63,13 @@ def weapon_stats_tuple(arg):
|
|||||||
weapon["element"] = get_element_match(parts[5])
|
weapon["element"] = get_element_match(parts[5])
|
||||||
weapon["element_attack"] = int(parts[6])
|
weapon["element_attack"] = int(parts[6])
|
||||||
else:
|
else:
|
||||||
print "bad part number"
|
#print "bad part number"
|
||||||
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
|
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
|
||||||
weapon["element_2"] = None
|
weapon["element_2"] = None
|
||||||
weapon["awaken"] = None
|
weapon["awaken"] = None
|
||||||
weapon["element_2_attack"] = None
|
weapon["element_2_attack"] = None
|
||||||
weapon["_id"] = -1
|
weapon["_id"] = -1
|
||||||
print "making model"
|
#print "making model"
|
||||||
return Weapon(weapon)
|
return Weapon(weapon)
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +117,8 @@ def get_skill_names(args):
|
|||||||
"Awaken" if args.awaken else "",
|
"Awaken" if args.awaken else "",
|
||||||
skills.AttackUp.name(args.attack_up),
|
skills.AttackUp.name(args.attack_up),
|
||||||
skills.CriticalEye.name(args.critical_eye),
|
skills.CriticalEye.name(args.critical_eye),
|
||||||
skills.ElementAttackUp.name(args.element_up)]
|
skills.ElementAttackUp.name(args.element_up),
|
||||||
|
"Blunt Power" if args.blunt_power else ""]
|
||||||
|
|
||||||
|
|
||||||
def percent_change(a, b):
|
def percent_change(a, b):
|
||||||
@@ -151,6 +151,9 @@ def _add_skill_args(parser):
|
|||||||
help="With virus affinity boost, must be either"
|
help="With virus affinity boost, must be either"
|
||||||
+" 15 (normal) or 30 (with Frenzy Res skill)",
|
+" 15 (normal) or 30 (with Frenzy Res skill)",
|
||||||
type=int, choices=[0, 15, 30], default=0)
|
type=int, choices=[0, 15, 30], default=0)
|
||||||
|
parser.add_argument("-b", "--blunt-power", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Blunt Power (MHX), default off")
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv):
|
def parse_args(argv):
|
||||||
@@ -172,6 +175,9 @@ def parse_args(argv):
|
|||||||
parser.add_argument("-d", "--diff", action="store_true", default=False,
|
parser.add_argument("-d", "--diff", action="store_true", default=False,
|
||||||
help="Show percent difference in damage to each part"
|
help="Show percent difference in damage to each part"
|
||||||
+" from first weapon in list.")
|
+" from first weapon in list.")
|
||||||
|
parser.add_argument("-x", "--monster-hunter-cross", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="Assume weapons are true attack, use MHX values")
|
||||||
parser.add_argument("-m", "--match", nargs="*",
|
parser.add_argument("-m", "--match", nargs="*",
|
||||||
help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW"
|
help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW"
|
||||||
+" Include all matching weapons in their final form."
|
+" Include all matching weapons in their final form."
|
||||||
@@ -318,7 +324,10 @@ def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts):
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
args = parse_args(None)
|
args = parse_args(None)
|
||||||
|
|
||||||
db = MHDB(_pathfix.db_path)
|
if args.monster_hunter_cross:
|
||||||
|
db = MHDBX()
|
||||||
|
else:
|
||||||
|
db = MHDB()
|
||||||
motiondb = MotionValueDB(_pathfix.motion_values_path)
|
motiondb = MotionValueDB(_pathfix.motion_values_path)
|
||||||
|
|
||||||
monster = db.get_monster_by_name(args.monster)
|
monster = db.get_monster_by_name(args.monster)
|
||||||
@@ -399,7 +408,9 @@ if __name__ == '__main__':
|
|||||||
awaken=skill_args.awaken,
|
awaken=skill_args.awaken,
|
||||||
artillery_level=skill_args.artillery,
|
artillery_level=skill_args.artillery,
|
||||||
limit_parts=args.parts,
|
limit_parts=args.parts,
|
||||||
frenzy_bonus=skill_args.frenzy)
|
frenzy_bonus=skill_args.frenzy,
|
||||||
|
is_true_attack=args.monster_hunter_cross,
|
||||||
|
blunt_power=skill_args.blunt_power)
|
||||||
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:
|
if wd.etype2:
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ def raw_damage(true_raw, sharpness, affinity, monster_hitbox, motion):
|
|||||||
* monster_hitbox / 100.0)
|
* monster_hitbox / 100.0)
|
||||||
|
|
||||||
|
|
||||||
def element_damage(element, sharpness, monster_ehitbox):
|
def element_damage(raw_element, sharpness, monster_ehitbox):
|
||||||
"""
|
"""
|
||||||
Calculate elemental damage to a monster part with the given elemental
|
Calculate elemental damage to a monster part with the given elemental
|
||||||
attack, the given sharpness, and the given monster elemental weakness.
|
attack, the given sharpness, and the given monster elemental weakness.
|
||||||
Note that this is independent of the motion value of the attack.
|
Note that this is independent of the motion value of the attack.
|
||||||
"""
|
"""
|
||||||
return floor(element / 10.0
|
return floor(raw_element
|
||||||
* SharpnessLevel.element_modifier(sharpness)
|
* SharpnessLevel.element_modifier(sharpness)
|
||||||
* monster_ehitbox / 100.0)
|
* monster_ehitbox / 100.0)
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ class WeaponMonsterDamage(object):
|
|||||||
critical_eye_skill=skills.CriticalEye.NONE,
|
critical_eye_skill=skills.CriticalEye.NONE,
|
||||||
element_skill=skills.ElementAttackUp.NONE,
|
element_skill=skills.ElementAttackUp.NONE,
|
||||||
awaken=False, artillery_level=0, limit_parts=None,
|
awaken=False, artillery_level=0, limit_parts=None,
|
||||||
frenzy_bonus=0):
|
frenzy_bonus=0, blunt_power=False, is_true_attack=False):
|
||||||
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
|
||||||
@@ -183,6 +183,8 @@ class WeaponMonsterDamage(object):
|
|||||||
self.element_skill = element_skill
|
self.element_skill = element_skill
|
||||||
self.awaken = awaken
|
self.awaken = awaken
|
||||||
self.artillery_level = artillery_level
|
self.artillery_level = artillery_level
|
||||||
|
self.blunt_power = blunt_power
|
||||||
|
self.is_true_attack = is_true_attack
|
||||||
self.limit_parts = limit_parts
|
self.limit_parts = limit_parts
|
||||||
# 15 normaly for overcoming the virus, 30 with frenzy res skill
|
# 15 normaly for overcoming the virus, 30 with frenzy res skill
|
||||||
assert frenzy_bonus in (0, 15, 30)
|
assert frenzy_bonus in (0, 15, 30)
|
||||||
@@ -199,15 +201,19 @@ class WeaponMonsterDamage(object):
|
|||||||
self.cb_phial_damage = defaultdict(dict)
|
self.cb_phial_damage = defaultdict(dict)
|
||||||
|
|
||||||
self.weapon_type = self.weapon["wtype"]
|
self.weapon_type = self.weapon["wtype"]
|
||||||
self.true_raw = (self.weapon["attack"]
|
if is_true_attack:
|
||||||
/ WeaponType.multiplier(self.weapon_type))
|
self.true_raw = self.weapon["attack"]
|
||||||
|
else:
|
||||||
|
self.true_raw = (self.weapon["attack"]
|
||||||
|
/ WeaponType.multiplier(self.weapon_type))
|
||||||
if sharp_plus:
|
if sharp_plus:
|
||||||
self.sharpness = self.weapon.sharpness_plus.max
|
self.sharpness = self.weapon.sharpness_plus.max
|
||||||
else:
|
else:
|
||||||
self.sharpness = self.weapon.sharpness.max
|
self.sharpness = self.weapon.sharpness.max
|
||||||
#print "sharpness=", self.sharpness
|
#print "sharpness=", self.sharpness
|
||||||
if self.weapon["affinity"]:
|
if self.weapon["affinity"]:
|
||||||
if "/" in self.weapon["affinity"]:
|
if (isinstance(self.weapon["affinity"], str)
|
||||||
|
and "/" in self.weapon["affinity"]):
|
||||||
self.chaotic = True
|
self.chaotic = True
|
||||||
# Handle chaotic gore affinity, e.g. -35/10. This means that
|
# Handle chaotic gore affinity, e.g. -35/10. This means that
|
||||||
# 35% of the time it does a negative critical (75% damage)
|
# 35% of the time it does a negative critical (75% damage)
|
||||||
@@ -241,6 +247,11 @@ class WeaponMonsterDamage(object):
|
|||||||
else:
|
else:
|
||||||
self.eattack2 = 0
|
self.eattack2 = 0
|
||||||
|
|
||||||
|
if not self.is_true_attack and self.eattack:
|
||||||
|
self.eattack /= 10
|
||||||
|
if self.eattack2:
|
||||||
|
self.eattack2 /= 10
|
||||||
|
|
||||||
self.true_raw = skills.AttackUp.modified(attack_skill,
|
self.true_raw = skills.AttackUp.modified(attack_skill,
|
||||||
self.true_raw)
|
self.true_raw)
|
||||||
self.affinity = skills.CriticalEye.modified(critical_eye_skill,
|
self.affinity = skills.CriticalEye.modified(critical_eye_skill,
|
||||||
@@ -250,6 +261,14 @@ class WeaponMonsterDamage(object):
|
|||||||
self.eattack2 = skills.ElementAttackUp.modified(element_skill,
|
self.eattack2 = skills.ElementAttackUp.modified(element_skill,
|
||||||
self.eattack2)
|
self.eattack2)
|
||||||
|
|
||||||
|
if self.blunt_power:
|
||||||
|
if self.sharpness in (SharpnessLevel.RED, SharpnessLevel.ORANGE):
|
||||||
|
self.true_raw += 30
|
||||||
|
elif self.sharpness == SharpnessLevel.YELLOW:
|
||||||
|
self.true_raw += 25
|
||||||
|
elif self.sharpness == SharpnessLevel.GREEN:
|
||||||
|
self.true_raw += 15
|
||||||
|
|
||||||
self.parts = []
|
self.parts = []
|
||||||
self.break_count = 0
|
self.break_count = 0
|
||||||
|
|
||||||
@@ -266,6 +285,8 @@ class WeaponMonsterDamage(object):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def attack(self):
|
def attack(self):
|
||||||
|
if self.is_true_attack:
|
||||||
|
return self.true_raw
|
||||||
return self.true_raw * WeaponType.multiplier(self.weapon_type)
|
return self.true_raw * WeaponType.multiplier(self.weapon_type)
|
||||||
|
|
||||||
def _calculate_damage(self):
|
def _calculate_damage(self):
|
||||||
|
|||||||
65
mhapi/db.py
65
mhapi/db.py
@@ -4,6 +4,7 @@ Module for accessing the sqlite monster hunter db from
|
|||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import json
|
||||||
|
|
||||||
from mhapi import model
|
from mhapi import model
|
||||||
|
|
||||||
@@ -528,3 +529,67 @@ class MHDB(object):
|
|||||||
else:
|
else:
|
||||||
ucomps = None
|
ucomps = None
|
||||||
item_data.set_components(ccomps, ucomps)
|
item_data.set_components(ccomps, ucomps)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MHDBX(object):
|
||||||
|
"""
|
||||||
|
Wrapper around Monster Hunter Cross (X) JSON data. Attempts limited
|
||||||
|
compatibility with original 4U MHDB class.
|
||||||
|
|
||||||
|
Uses MHDB object, as temporariy hack for MHX data that is not yet
|
||||||
|
available or integrated.
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
"""
|
||||||
|
Loads JSON data, keeps in memory.
|
||||||
|
"""
|
||||||
|
module_path = os.path.dirname(__file__)
|
||||||
|
mhx_db_path = os.path.abspath(os.path.join(module_path, "..",
|
||||||
|
"db", "mhx"))
|
||||||
|
|
||||||
|
self._4udb = MHDB()
|
||||||
|
self._weapon_list = []
|
||||||
|
self._weapons_by_name = {}
|
||||||
|
with open(os.path.join(mhx_db_path, "weapon_list.json")) as f:
|
||||||
|
wlist = json.load(f)
|
||||||
|
for i, wdata in enumerate(wlist):
|
||||||
|
wdata["_id"] = i
|
||||||
|
weapon = model.Weapon(wdata)
|
||||||
|
self._weapon_list.append(weapon)
|
||||||
|
self._weapons_by_name[weapon.name_jp] = weapon
|
||||||
|
|
||||||
|
def get_weapon_by_name(self, name):
|
||||||
|
return self._weapons_by_name.get(name)
|
||||||
|
|
||||||
|
def get_monster_by_name(self, *args, **kwargs):
|
||||||
|
return self._4udb.get_monster_by_name(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_monster_damage(self, *args, **kwargs):
|
||||||
|
return self._4udb.get_monster_damage(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_monster_breaks(self, *args, **kwargs):
|
||||||
|
return self._4udb.get_monster_breaks(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_weapons_by_query(self, wtype=None, element=None,
|
||||||
|
final=None):
|
||||||
|
"""
|
||||||
|
@element can have the special value 'Raw' to search for weapons
|
||||||
|
with no element. Otherwise @element is searched for in both
|
||||||
|
awaken and native, and can be a status or an element.
|
||||||
|
|
||||||
|
@final should be string '1' or '0'
|
||||||
|
"""
|
||||||
|
final = int(final)
|
||||||
|
results = []
|
||||||
|
for w in self._weapon_list:
|
||||||
|
if wtype is not None and w.wtype != wtype:
|
||||||
|
continue
|
||||||
|
if (element is not None
|
||||||
|
and element not in (w.element, w.element_2)
|
||||||
|
and not (element == "Raw" and not w.element)):
|
||||||
|
continue
|
||||||
|
if final is not None and w.final != final:
|
||||||
|
continue
|
||||||
|
results.append(w)
|
||||||
|
return results
|
||||||
|
|||||||
@@ -162,6 +162,16 @@ class SharpnessLevel(EnumBase):
|
|||||||
PURPLE: (1.44, 1.20),
|
PURPLE: (1.44, 1.20),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_modifier_mhx = {
|
||||||
|
RED: (0.50, 0.25),
|
||||||
|
ORANGE: (0.75, 0.50),
|
||||||
|
YELLOW: (1.00, 0.75),
|
||||||
|
GREEN: (1.05, 1.00),
|
||||||
|
BLUE: (1.20, 1.0625),
|
||||||
|
WHITE: (1.32, 1.125),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def raw_modifier(cls, sharpness):
|
def raw_modifier(cls, sharpness):
|
||||||
return cls._modifier[sharpness][0]
|
return cls._modifier[sharpness][0]
|
||||||
@@ -177,8 +187,11 @@ class WeaponSharpness(ModelBase):
|
|||||||
points at each level. E.g. the 0th item in the list is the amount of
|
points at each level. E.g. the 0th item in the list is the amount of
|
||||||
RED sharpness, the 1st item is ORANGE, etc.
|
RED sharpness, the 1st item is ORANGE, etc.
|
||||||
"""
|
"""
|
||||||
def __init__(self, db_string):
|
def __init__(self, db_string_or_list):
|
||||||
self.value_list = [int(s) for s in db_string.split(".")]
|
if isinstance(db_string_or_list, list):
|
||||||
|
self.value_list = db_string_or_list
|
||||||
|
else:
|
||||||
|
self.value_list = [int(s) for s in db_string_or_list.split(".")]
|
||||||
self._max = None
|
self._max = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -364,13 +377,21 @@ class Weapon(ItemCraftable):
|
|||||||
if self.wtype in ("Light Bowgun", "Heavy Bowgun", "Bow"):
|
if self.wtype in ("Light Bowgun", "Heavy Bowgun", "Bow"):
|
||||||
self._data["sharpness"] = self._data["sharpness_plus"] = None
|
self._data["sharpness"] = self._data["sharpness_plus"] = None
|
||||||
return
|
return
|
||||||
parts = self._row["sharpness"].split(" ")
|
if isinstance(self._row["sharpness"], list):
|
||||||
if len(parts) != 2:
|
# MHX JSON data, already desired format, but doesn't have
|
||||||
raise ValueError("Bad sharpness value in db: '%s'"
|
# purple so we append 0
|
||||||
% self._row["sharpness"])
|
self.sharpness = WeaponSharpness(self._row["sharpness"] + [0])
|
||||||
normal, plus = parts
|
self.sharpness_plus = WeaponSharpness(
|
||||||
self._data["sharpness"] = WeaponSharpness(normal)
|
self._row["sharpness_plus"] + [0])
|
||||||
self._data["sharpness_plus"] = WeaponSharpness(plus)
|
else:
|
||||||
|
# 4U data from db
|
||||||
|
parts = self._row["sharpness"].split(" ")
|
||||||
|
if len(parts) != 2:
|
||||||
|
raise ValueError("Bad sharpness value in db: '%s'"
|
||||||
|
% self._row["sharpness"])
|
||||||
|
normal, plus = parts
|
||||||
|
self._data["sharpness"] = WeaponSharpness(normal)
|
||||||
|
self._data["sharpness_plus"] = WeaponSharpness(plus)
|
||||||
|
|
||||||
def is_not_localized(self):
|
def is_not_localized(self):
|
||||||
return (self.name == self.name_jp)
|
return (self.name == self.name_jp)
|
||||||
|
|||||||
Reference in New Issue
Block a user