model and db refactor, damage improvements
This commit is contained in:
@@ -33,7 +33,7 @@ if __name__ == '__main__':
|
|||||||
db_path = os.path.join(db_path, "..", "db", "mh4u.db")
|
db_path = os.path.join(db_path, "..", "db", "mh4u.db")
|
||||||
db = MHDB(db_path)
|
db = MHDB(db_path)
|
||||||
|
|
||||||
items = db.get_item_names(rewards.ITEM_TYPES)
|
items = db.get_items(rewards.ITEM_TYPES)
|
||||||
|
|
||||||
# write all names json to /items.json
|
# write all names json to /items.json
|
||||||
items_file = os.path.join(outdir, "items.json")
|
items_file = os.path.join(outdir, "items.json")
|
||||||
@@ -48,13 +48,13 @@ if __name__ == '__main__':
|
|||||||
else:
|
else:
|
||||||
out.write(", ")
|
out.write(", ")
|
||||||
out.write('"')
|
out.write('"')
|
||||||
out.write(item["name"])
|
out.write(item.name)
|
||||||
out.write('"')
|
out.write('"')
|
||||||
out.write("]")
|
out.write("]")
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
name = item["name"]
|
name = item.name
|
||||||
item_id = item["_id"]
|
item_id = item.id
|
||||||
encoded_name = name.encode("utf8")
|
encoded_name = name.encode("utf8")
|
||||||
item_file = os.path.join(outdir, encoded_name + ".txt")
|
item_file = os.path.join(outdir, encoded_name + ".txt")
|
||||||
print "Writing", item_id, item_file
|
print "Writing", item_id, item_file
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import argparse
|
||||||
|
|
||||||
import _pathfix
|
import _pathfix
|
||||||
|
|
||||||
from mhapi.db import MHDB
|
from mhapi.db import MHDB
|
||||||
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
|
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
|
||||||
|
from mhapi.model import SharpnessLevel
|
||||||
|
from mhapi import skills
|
||||||
|
|
||||||
|
|
||||||
def percent_change(a, b):
|
def percent_change(a, b):
|
||||||
@@ -15,50 +17,89 @@ def percent_change(a, b):
|
|||||||
return (100.0 * (b-a) / a)
|
return (100.0 * (b-a) / a)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
def parse_args(argv):
|
||||||
if len(sys.argv) < 4:
|
parser = argparse.ArgumentParser(
|
||||||
print "Usage: %s 'monster name' 'weapon name'+" % sys.argv[0]
|
"Calculate damage to monster from different weapons of the"
|
||||||
sys.exit(os.EX_USAGE)
|
" same class"
|
||||||
|
)
|
||||||
|
parser.add_argument("-s", "--sharpness-plus-one", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="add Sharpness +1 skill, default off")
|
||||||
|
parser.add_argument("-f", "--awaken", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="add Awaken (FreeElement), default off")
|
||||||
|
parser.add_argument("-a", "--attack-up",
|
||||||
|
type=int, choices=xrange(0, 5), default=0,
|
||||||
|
help="1-4 for AuS, M, L, XL")
|
||||||
|
parser.add_argument("-c", "--critical-eye",
|
||||||
|
type=int, choices=xrange(0, 5), default=0,
|
||||||
|
help="1-4 for CE+1, +2, +3 and Critical God")
|
||||||
|
parser.add_argument("-e", "--element-up",
|
||||||
|
type=int, choices=xrange(0, 5), default=0,
|
||||||
|
help="1-4 for (element) Atk +1, +2, +3 and"
|
||||||
|
" Element Attack Up")
|
||||||
|
parser.add_argument("monster",
|
||||||
|
help="Full name of monster")
|
||||||
|
parser.add_argument("weapon", nargs="+",
|
||||||
|
help="One or more weapons of same class to compare,"
|
||||||
|
" full names")
|
||||||
|
|
||||||
sharp_plus = bool(int(sys.argv[1]))
|
return parser.parse_args(argv)
|
||||||
monster_name = sys.argv[2]
|
|
||||||
weapon_names = sys.argv[3:]
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args(None)
|
||||||
|
|
||||||
db = MHDB(_pathfix.db_path)
|
db = MHDB(_pathfix.db_path)
|
||||||
motiondb = MotionValueDB(_pathfix.motion_values_path)
|
motiondb = MotionValueDB(_pathfix.motion_values_path)
|
||||||
|
|
||||||
monster = db.get_monster_by_name(monster_name)
|
monster = db.get_monster_by_name(args.monster)
|
||||||
if not monster:
|
if not monster:
|
||||||
raise ValueError("Monster '%s' not found" % monster_name)
|
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 = []
|
||||||
for name in weapon_names:
|
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)
|
||||||
|
|
||||||
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
|
||||||
print "Weapon Type: %s" % weapon_type
|
print "Weapon Type: %s" % weapon_type
|
||||||
print "Average Motion: %0.1f" % motion
|
print "Average Motion: %0.1f" % motion
|
||||||
print "Monster Breaks: %s" % ", ".join(monster_breaks)
|
print "Monster Breaks: %s" % ", ".join(monster_breaks)
|
||||||
|
skill_names = ["Sharpness +1" if args.sharpness_plus_one else "",
|
||||||
|
"Awaken" if args.awaken else "",
|
||||||
|
skills.AttackUp.name(args.attack_up),
|
||||||
|
skills.CriticalEye.name(args.critical_eye),
|
||||||
|
skills.ElementAttackUp.name(args.element_up)]
|
||||||
|
print "Skills:", ", ".join(skill for skill in skill_names if skill)
|
||||||
weapon_damage_map = dict()
|
weapon_damage_map = dict()
|
||||||
for name, row in zip(weapon_names, weapons):
|
for name, row in zip(args.weapon, weapons):
|
||||||
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)
|
||||||
try:
|
try:
|
||||||
weapon_damage_map[name] = WeaponMonsterDamage(row,
|
wd = WeaponMonsterDamage(row,
|
||||||
monster, monster_damage,
|
monster, monster_damage,
|
||||||
motion, sharp_plus,
|
motion, args.sharpness_plus_one,
|
||||||
monster_breaks)
|
monster_breaks,
|
||||||
|
attack_skill=args.attack_up,
|
||||||
|
critical_eye_skill=args.critical_eye,
|
||||||
|
element_skill=args.element_up,
|
||||||
|
awaken=args.awaken)
|
||||||
|
print "%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity),
|
||||||
|
if wd.etype:
|
||||||
|
print "(%4.0f %s)" % (wd.eattack, wd.etype),
|
||||||
|
print SharpnessLevel.name(wd.sharpness)
|
||||||
|
weapon_damage_map[name] = wd
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print str(e)
|
print str(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
damage_map_base = weapon_damage_map[weapon_names[0]]
|
damage_map_base = weapon_damage_map[args.weapon[0]]
|
||||||
parts = damage_map_base.parts
|
parts = damage_map_base.parts
|
||||||
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
@@ -66,17 +107,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 weapon_names[1:]]
|
for w in args.weapon[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 weapon_names[1:]]
|
for w in args.weapon[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 weapon_names[1:]]
|
for w in args.weapon[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)
|
||||||
@@ -100,7 +141,7 @@ if __name__ == '__main__':
|
|||||||
base,
|
base,
|
||||||
weapon_damage_map[w].averages[avg_type]
|
weapon_damage_map[w].averages[avg_type]
|
||||||
)
|
)
|
||||||
for w in weapon_names[1:]]
|
for w in args.weapon[1:]]
|
||||||
|
|
||||||
diff_s = ",".join("%+0.1f%%" % i for i in diffs)
|
diff_s = ",".join("%+0.1f%%" % i for i in diffs)
|
||||||
|
|
||||||
|
|||||||
1
db/motion_values.json
Normal file
1
db/motion_values.json
Normal file
File diff suppressed because one or more lines are too long
@@ -4,6 +4,9 @@ import json
|
|||||||
import difflib
|
import difflib
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from mhapi import skills
|
||||||
|
from mhapi.model import SharpnessLevel
|
||||||
|
|
||||||
|
|
||||||
WEAKPART_WEIGHT = 0.5
|
WEAKPART_WEIGHT = 0.5
|
||||||
|
|
||||||
@@ -14,7 +17,7 @@ def raw_damage(true_raw, sharpness, affinity, monster_hitbox, motion):
|
|||||||
sharpness, monster raw weakness, and weapon motion value.
|
sharpness, monster raw weakness, and weapon motion value.
|
||||||
"""
|
"""
|
||||||
return (true_raw
|
return (true_raw
|
||||||
* Sharpness.raw_modifier(sharpness)
|
* SharpnessLevel.raw_modifier(sharpness)
|
||||||
* (1 + (affinity / 400.0))
|
* (1 + (affinity / 400.0))
|
||||||
* motion / 100.0
|
* motion / 100.0
|
||||||
* monster_hitbox / 100.0)
|
* monster_hitbox / 100.0)
|
||||||
@@ -27,44 +30,10 @@ def element_damage(element, sharpness, monster_ehitbox):
|
|||||||
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 (element / 10.0
|
return (element / 10.0
|
||||||
* Sharpness.element_modifier(sharpness)
|
* SharpnessLevel.element_modifier(sharpness)
|
||||||
* monster_ehitbox / 100.0)
|
* monster_ehitbox / 100.0)
|
||||||
|
|
||||||
|
|
||||||
class Sharpness(object):
|
|
||||||
"""
|
|
||||||
Enumeration for weapon sharpness.
|
|
||||||
"""
|
|
||||||
|
|
||||||
RED = 0
|
|
||||||
ORANGE = 1
|
|
||||||
YELLOW = 2
|
|
||||||
GREEN = 3
|
|
||||||
BLUE = 4
|
|
||||||
WHITE = 5
|
|
||||||
PURPLE = 6
|
|
||||||
|
|
||||||
ALL = range(0, PURPLE + 1)
|
|
||||||
|
|
||||||
_modifier = {
|
|
||||||
RED: (0.50, 0.25),
|
|
||||||
ORANGE: (0.75, 0.50),
|
|
||||||
YELLOW: (1.00, 0.75),
|
|
||||||
GREEN: (1.05, 1.00),
|
|
||||||
BLUE: (1.20, 1.06),
|
|
||||||
WHITE: (1.32, 1.12),
|
|
||||||
PURPLE: (1.44, 1.20),
|
|
||||||
}
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def raw_modifier(cls, sharpness):
|
|
||||||
return cls._modifier[sharpness][0]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def element_modifier(cls, sharpness):
|
|
||||||
return cls._modifier[sharpness][1]
|
|
||||||
|
|
||||||
|
|
||||||
class MotionType(object):
|
class MotionType(object):
|
||||||
CUT = "cut"
|
CUT = "cut"
|
||||||
IMPACT = "impact"
|
IMPACT = "impact"
|
||||||
@@ -192,13 +161,21 @@ class WeaponMonsterDamage(object):
|
|||||||
Does not include overall monster defense.
|
Does not include overall monster defense.
|
||||||
"""
|
"""
|
||||||
def __init__(self, weapon_row, monster_row, monster_damage_rows, motion,
|
def __init__(self, weapon_row, monster_row, monster_damage_rows, motion,
|
||||||
sharp_plus=False, breakable_parts=None):
|
sharp_plus=False, breakable_parts=None,
|
||||||
|
attack_skill=skills.AttackUp.NONE,
|
||||||
|
critical_eye_skill=skills.CriticalEye.NONE,
|
||||||
|
element_skill=skills.ElementAttackUp.NONE,
|
||||||
|
awaken=False):
|
||||||
self.weapon = weapon_row
|
self.weapon = weapon_row
|
||||||
self.monster = monster_row
|
self.monster = monster_row
|
||||||
self.monster_damage = monster_damage_rows
|
self.monster_damage = monster_damage_rows
|
||||||
self.motion = motion
|
self.motion = motion
|
||||||
self.sharp_plus = sharp_plus
|
self.sharp_plus = sharp_plus
|
||||||
self.breakable_parts = breakable_parts
|
self.breakable_parts = breakable_parts
|
||||||
|
self.attack_skill = attack_skill
|
||||||
|
self.critical_eye_skill = critical_eye_skill
|
||||||
|
self.element_skill = element_skill
|
||||||
|
self.awaken = awaken
|
||||||
|
|
||||||
self.damage_map = defaultdict(PartDamage)
|
self.damage_map = defaultdict(PartDamage)
|
||||||
self.average = 0
|
self.average = 0
|
||||||
@@ -209,16 +186,25 @@ class WeaponMonsterDamage(object):
|
|||||||
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))
|
||||||
sharp = _parse_sharpness(self.weapon)
|
|
||||||
if sharp_plus:
|
if sharp_plus:
|
||||||
self.sharpness = sharp[1]
|
self.sharpness = self.weapon.sharpness_plus.max
|
||||||
else:
|
else:
|
||||||
self.sharpness = sharp[0]
|
self.sharpness = self.weapon.sharpness.max
|
||||||
#print "sharpness=", self.sharpness
|
#print "sharpness=", self.sharpness
|
||||||
self.affinity = int(self.weapon["affinity"] or 0)
|
self.affinity = int(self.weapon["affinity"] or 0)
|
||||||
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"]
|
||||||
|
if not self.etype and self.awaken:
|
||||||
|
self.etype = self.weapon.awaken
|
||||||
|
self.eattack = self.weapon.awaken_attack
|
||||||
|
|
||||||
|
self.true_raw = skills.AttackUp.modified(attack_skill,
|
||||||
|
self.true_raw)
|
||||||
|
self.affinity = skills.CriticalEye.modified(critical_eye_skill,
|
||||||
|
self.affinity)
|
||||||
|
self.eattack = skills.ElementAttackUp.modified(element_skill,
|
||||||
|
self.eattack)
|
||||||
|
|
||||||
self.parts = []
|
self.parts = []
|
||||||
self.break_count = 0
|
self.break_count = 0
|
||||||
@@ -234,8 +220,13 @@ class WeaponMonsterDamage(object):
|
|||||||
self.max_element_part = (None, 0)
|
self.max_element_part = (None, 0)
|
||||||
self._calculate_damage()
|
self._calculate_damage()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def attack(self):
|
||||||
|
return self.true_raw * WeaponType.multiplier(self.weapon_type)
|
||||||
|
|
||||||
def _calculate_damage(self):
|
def _calculate_damage(self):
|
||||||
for row in self.monster_damage:
|
for row in self.monster_damage._rows:
|
||||||
|
# TODO: refactor to take advantage of new model
|
||||||
part = row["body_part"]
|
part = row["body_part"]
|
||||||
alt = None
|
alt = None
|
||||||
m = re.match(r"([^(]+) \(([^)]+)\)", part)
|
m = re.match(r"([^(]+) \(([^)]+)\)", part)
|
||||||
@@ -548,18 +539,3 @@ 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 _parse_sharpness(weapon_row):
|
|
||||||
"""
|
|
||||||
Parse the sharpness field from a weapon row, to determine
|
|
||||||
the max sharpness of the weapon with and without sharpness +1.
|
|
||||||
"""
|
|
||||||
db_values = weapon_row["sharpness"].split(" ")
|
|
||||||
sharpness = [Sharpness.RED, Sharpness.RED]
|
|
||||||
for i, db_value in enumerate(db_values):
|
|
||||||
values = [int(s) for s in db_value.split(".")]
|
|
||||||
for s in Sharpness.ALL:
|
|
||||||
if values[s] > 0:
|
|
||||||
sharpness[i] = s
|
|
||||||
return sharpness
|
|
||||||
|
|||||||
250
mhapi/db.py
250
mhapi/db.py
@@ -2,19 +2,69 @@
|
|||||||
Module for accessing the sqlite monster hunter db from
|
Module for accessing the sqlite monster hunter db from
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os.path
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from mhapi import model
|
from mhapi import model
|
||||||
|
|
||||||
|
|
||||||
|
def field_model(key):
|
||||||
|
"""
|
||||||
|
Model to replace each row with the value of single field in the row,
|
||||||
|
with the specified key.
|
||||||
|
"""
|
||||||
|
def model_fn(row):
|
||||||
|
return row[key]
|
||||||
|
return model_fn
|
||||||
|
|
||||||
|
|
||||||
|
def _db_path():
|
||||||
|
module_path = os.path.dirname(__file__)
|
||||||
|
project_path = os.path.abspath(os.path.join(module_path, ".."))
|
||||||
|
return os.path.join(project_path, "db", "mh4u.db")
|
||||||
|
|
||||||
|
|
||||||
class MHDB(object):
|
class MHDB(object):
|
||||||
def __init__(self, path, use_cache=True):
|
"""
|
||||||
|
Wrapper around the Android App sqlite3 db. The following conventions
|
||||||
|
are used:
|
||||||
|
|
||||||
|
- get_ENTITY_NAME will return a single entity by id
|
||||||
|
- get_ENTITY_NAME_by_name will return a single entity by name
|
||||||
|
- get_ENTITY_NAMEs will return a list of all entities in the db
|
||||||
|
- get_ENTITY_NAME_names will return a list of all names of the
|
||||||
|
entities in the db, possibly with a type param.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, path=None, use_cache=False):
|
||||||
|
"""
|
||||||
|
If use_cache=True, a lot of memory could be used. No attempt is
|
||||||
|
made to de-dupe data between keys, e.g. if you access an item
|
||||||
|
by id and by name, it will be fetched and stored in the cache
|
||||||
|
twice. Disk cache, sqlite caching, and the smallness of the
|
||||||
|
database should make in-memory caching unnecessary for most use
|
||||||
|
cases.
|
||||||
|
"""
|
||||||
|
if path is None:
|
||||||
|
path = _db_path()
|
||||||
self.conn = sqlite3.connect(path)
|
self.conn = sqlite3.connect(path)
|
||||||
self.conn.row_factory = sqlite3.Row
|
self.conn.row_factory = sqlite3.Row
|
||||||
self.use_cache = use_cache
|
self.use_cache = use_cache
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
|
|
||||||
def _get_memoized(self, key, query, *args):
|
def _query_one(self, key, query, args=(), model_cls=None,
|
||||||
if self.use_cache:
|
no_cache=False):
|
||||||
|
values = self._query_all(key, query, args, model_cls, no_cache)
|
||||||
|
if values:
|
||||||
|
return values[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _query_all(self, key, query, args=(), model_cls=None,
|
||||||
|
no_cache=False, collection_cls=None):
|
||||||
|
assert isinstance(args, tuple)
|
||||||
|
assert model_cls is None or collection_cls is None
|
||||||
|
if self.use_cache and not no_cache:
|
||||||
if key in self.cache:
|
if key in self.cache:
|
||||||
v = self.cache[key].get(args)
|
v = self.cache[key].get(args)
|
||||||
if v is not None:
|
if v is not None:
|
||||||
@@ -22,10 +72,14 @@ class MHDB(object):
|
|||||||
else:
|
else:
|
||||||
self.cache[key] = {}
|
self.cache[key] = {}
|
||||||
cursor = self.conn.execute(query, args)
|
cursor = self.conn.execute(query, args)
|
||||||
v = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
if self.use_cache:
|
if model_cls:
|
||||||
self.cache[key][args] = v
|
rows = [model_cls(row) for row in rows]
|
||||||
return v
|
if collection_cls:
|
||||||
|
rows = collection_cls(rows)
|
||||||
|
if self.use_cache and not no_cache:
|
||||||
|
self.cache[key][args] = rows
|
||||||
|
return rows
|
||||||
|
|
||||||
def cursor(self):
|
def cursor(self):
|
||||||
return self.conn.cursor()
|
return self.conn.cursor()
|
||||||
@@ -36,41 +90,65 @@ class MHDB(object):
|
|||||||
def close(self):
|
def close(self):
|
||||||
return self.conn.close()
|
return self.conn.close()
|
||||||
|
|
||||||
|
def get_item_types(self):
|
||||||
|
"""
|
||||||
|
List of strings.
|
||||||
|
"""
|
||||||
|
return self._query_all("item_types", """
|
||||||
|
SELECT DISTINCT type FROM items
|
||||||
|
""", model_cls=field_model("type"))
|
||||||
|
|
||||||
def get_item_names(self, item_types):
|
def get_item_names(self, item_types):
|
||||||
|
"""
|
||||||
|
List of unicode strings.
|
||||||
|
"""
|
||||||
item_types.sort()
|
item_types.sort()
|
||||||
placeholders = ", ".join(["?"] * len(item_types))
|
placeholders = ", ".join(["?"] * len(item_types))
|
||||||
v = self._get_memoized("item_names", """
|
return self._query_all("item_names", """
|
||||||
SELECT _id, name FROM items
|
SELECT _id, name FROM items
|
||||||
WHERE type IN (%s)
|
WHERE type IN (%s)
|
||||||
""" % placeholders, *item_types)
|
""" % placeholders, tuple(item_types), model_cls=field_model("name"))
|
||||||
return v
|
|
||||||
|
|
||||||
def get_monster_names(self):
|
def get_items(self, item_types=None):
|
||||||
v = self._get_memoized("monster_names", """
|
"""
|
||||||
SELECT name FROM monsters
|
List of item objects.
|
||||||
""")
|
"""
|
||||||
return v
|
q = "SELECT * FROM items"
|
||||||
|
if item_types:
|
||||||
|
item_types.sort()
|
||||||
|
placeholders = ", ".join(["?"] * len(item_types))
|
||||||
|
q += "\nWHERE type IN (%s)" % placeholders
|
||||||
|
item_types = tuple(item_types)
|
||||||
|
else:
|
||||||
|
item_types = ()
|
||||||
|
return self._query_all("items", q, item_types, model_cls=model.Item)
|
||||||
|
|
||||||
def get_item(self, item_id):
|
def get_item(self, item_id):
|
||||||
v = self._get_memoized("item", """
|
"""
|
||||||
|
Single item object or None.
|
||||||
|
"""
|
||||||
|
return self._query_one("item", """
|
||||||
SELECT * FROM items
|
SELECT * FROM items
|
||||||
WHERE _id=?
|
WHERE _id=?
|
||||||
""", item_id)
|
""", (item_id,), model_cls=model.Item)
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def get_item_by_name(self, name):
|
def get_item_by_name(self, name):
|
||||||
v = self._get_memoized("item", """
|
"""
|
||||||
|
Single item object or None.
|
||||||
|
"""
|
||||||
|
return self._query_one("item", """
|
||||||
SELECT * FROM items
|
SELECT * FROM items
|
||||||
WHERE name=?
|
WHERE name=?
|
||||||
""", name)
|
""", (name,), model_cls=model.Item)
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def get_wyporium_trade(self, item_id):
|
def get_wyporium_trade(self, item_id):
|
||||||
v = self._get_memoized("wyporium", """
|
"""
|
||||||
|
Single wyporium row or None.
|
||||||
|
"""
|
||||||
|
return self._query_one("wyporium", """
|
||||||
SELECT * FROM wyporium
|
SELECT * FROM wyporium
|
||||||
WHERE item_in_id=?
|
WHERE item_in_id=?
|
||||||
""", item_id)
|
""", (item_id,))
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def search_item_name(self, term, item_type=None):
|
def search_item_name(self, term, item_type=None):
|
||||||
"""
|
"""
|
||||||
@@ -92,42 +170,50 @@ class MHDB(object):
|
|||||||
query += "AND type = ?"
|
query += "AND type = ?"
|
||||||
args += [item_type]
|
args += [item_type]
|
||||||
|
|
||||||
cursor = self.conn.execute(query, args)
|
return self._query_all("search_item", query, tuple(args),
|
||||||
return cursor.fetchall()
|
no_cache=True, model_cls=model.Item)
|
||||||
|
|
||||||
def get_monster_by_name(self, name):
|
def get_monsters(self):
|
||||||
v = self._get_memoized("monster", """
|
return self._query_all("monsters", """
|
||||||
SELECT * FROM monsters
|
SELECT * FROM monsters
|
||||||
WHERE name=?
|
""", model_cls=model.Monster)
|
||||||
""", name)
|
|
||||||
return v[0] if v else None
|
def get_monster_names(self):
|
||||||
|
"""
|
||||||
|
List of unicode strings.
|
||||||
|
"""
|
||||||
|
return self._query_all("monster_names", """
|
||||||
|
SELECT name FROM monsters
|
||||||
|
""", model_cls=field_model("name"))
|
||||||
|
|
||||||
def get_monster(self, monster_id):
|
def get_monster(self, monster_id):
|
||||||
v = self._get_memoized("monster", """
|
return self._query_one("monster", """
|
||||||
SELECT * FROM monsters
|
SELECT * FROM monsters
|
||||||
WHERE _id=?
|
WHERE _id=?
|
||||||
""", monster_id)
|
""", (monster_id,), model_cls=model.Monster)
|
||||||
return v[0] if v else None
|
|
||||||
|
def get_monster_by_name(self, name):
|
||||||
|
return self._query_one("monster", """
|
||||||
|
SELECT * FROM monsters
|
||||||
|
WHERE name=?
|
||||||
|
""", (name,), model_cls=model.Monster)
|
||||||
|
|
||||||
def get_quest(self, quest_id):
|
def get_quest(self, quest_id):
|
||||||
v = self._get_memoized("quest", """
|
return self._query_one("quest", """
|
||||||
SELECT * FROM quests
|
SELECT * FROM quests
|
||||||
WHERE _id=?
|
WHERE _id=?
|
||||||
""", quest_id)
|
""", (quest_id,), model_cls=model.Quest)
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def get_quests(self):
|
def get_quests(self):
|
||||||
v = self._get_memoized("quests", """
|
return self._query_all("quests", """
|
||||||
SELECT * FROM quests
|
SELECT * FROM quests
|
||||||
""")
|
""", model_cls=model.Quest)
|
||||||
return v
|
|
||||||
|
|
||||||
def get_quest_rewards(self, quest_id):
|
def get_quest_rewards(self, quest_id):
|
||||||
v = self._get_memoized("quest_rewards", """
|
return self._query_all("quest_rewards", """
|
||||||
SELECT * FROM quest_rewards
|
SELECT * FROM quest_rewards
|
||||||
WHERE quest_id=?
|
WHERE quest_id=?
|
||||||
""", quest_id)
|
""", (quest_id,))
|
||||||
return v
|
|
||||||
|
|
||||||
def get_monster_rewards(self, monster_id, rank=None):
|
def get_monster_rewards(self, monster_id, rank=None):
|
||||||
q = """
|
q = """
|
||||||
@@ -136,19 +222,18 @@ class MHDB(object):
|
|||||||
"""
|
"""
|
||||||
if rank is not None:
|
if rank is not None:
|
||||||
q += "AND rank=?"
|
q += "AND rank=?"
|
||||||
v = self._get_memoized("monster_rewards", q, monster_id, rank)
|
args = (monster_id, rank)
|
||||||
else:
|
else:
|
||||||
v = self._get_memoized("monster_rewards", q, monster_id)
|
args = (monster_id,)
|
||||||
return v
|
return self._query_all("monster_rewards", q, args)
|
||||||
|
|
||||||
def get_quest_monsters(self, quest_id):
|
def get_quest_monsters(self, quest_id):
|
||||||
v = self._get_memoized("quest_monsters", """
|
return self._query_all("quest_monsters", """
|
||||||
SELECT monster_id, unstable FROM monster_to_quest
|
SELECT monster_id, unstable FROM monster_to_quest
|
||||||
WHERE quest_id=?
|
WHERE quest_id=?
|
||||||
""", quest_id)
|
""", (quest_id,))
|
||||||
return v
|
|
||||||
|
|
||||||
def get_item_quest_objects(self, item_id):
|
def get_item_quests(self, item_id):
|
||||||
"""
|
"""
|
||||||
Get a list of quests that provide the specified item in quest
|
Get a list of quests that provide the specified item in quest
|
||||||
reqards. Returns a list of quest objects, which encapsulate the
|
reqards. Returns a list of quest objects, which encapsulate the
|
||||||
@@ -163,79 +248,70 @@ class MHDB(object):
|
|||||||
|
|
||||||
quests = []
|
quests = []
|
||||||
for r in rows:
|
for r in rows:
|
||||||
quest_row = self.get_quest(r["quest_id"])
|
quest_id = r["quest_id"]
|
||||||
rewards_rows = self.get_quest_rewards(r["quest_id"])
|
quest = self.get_quest(quest_id)
|
||||||
quests.append(model.Quest(quest_row, rewards_rows))
|
quest.rewards = self.get_quest_rewards(quest_id)
|
||||||
|
quests.append(quest)
|
||||||
|
|
||||||
return quests
|
return quests
|
||||||
|
|
||||||
def get_item_monsters(self, item_id):
|
def get_item_monsters(self, item_id):
|
||||||
v = self._get_memoized("item_monsters", """
|
return self._query_all("item_monsters", """
|
||||||
SELECT DISTINCT monster_id, rank FROM hunting_rewards
|
SELECT DISTINCT monster_id, rank FROM hunting_rewards
|
||||||
WHERE item_id=?
|
WHERE item_id=?
|
||||||
""", item_id)
|
""", (item_id,))
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_item_gathering(self, item_id):
|
def get_item_gathering(self, item_id):
|
||||||
v = self._get_memoized("item_gathering", """
|
return self._query_all("item_gathering", """
|
||||||
SELECT * FROM gathering
|
SELECT * FROM gathering
|
||||||
WHERE item_id=?
|
WHERE item_id=?
|
||||||
""", item_id)
|
""", (item_id,))
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_location(self, location_id):
|
def get_location(self, location_id):
|
||||||
v = self._get_memoized("location", """
|
self._query_one("location", """
|
||||||
SELECT * FROM locations
|
SELECT * FROM locations
|
||||||
WHERE _id=?
|
WHERE _id=?
|
||||||
""", location_id)
|
""", (location_id,), model_cls=model.Location)
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_locations(self):
|
def get_locations(self):
|
||||||
v = self._get_memoized("locations", """
|
return self._query_all("locations", """
|
||||||
SELECT * FROM locations
|
SELECT * FROM locations
|
||||||
""")
|
""", model_cls=model.Location)
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_monster_damage(self, monster_id):
|
def get_monster_damage(self, monster_id):
|
||||||
v = self._get_memoized("monster_damage", """
|
return self._query_all("monster_damage", """
|
||||||
SELECT * FROM monster_damage
|
SELECT * FROM monster_damage
|
||||||
WHERE monster_id=?
|
WHERE monster_id=?
|
||||||
""", monster_id)
|
""", (monster_id,), collection_cls=model.MonsterDamage)
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_weapons(self):
|
def get_weapons(self):
|
||||||
v = self._get_memoized("weapons", """
|
return self._query_all("weapons", """
|
||||||
SELECT * FROM weapons
|
SELECT * FROM weapons
|
||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
""")
|
""", model_cls=model.Weapon)
|
||||||
|
|
||||||
return v
|
|
||||||
|
|
||||||
def get_weapon(self, weapon_id):
|
def get_weapon(self, weapon_id):
|
||||||
v = self._get_memoized("weapon", """
|
return self._query_one("weapon", """
|
||||||
SELECT * FROM weapons
|
SELECT * FROM weapons
|
||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
WHERE weapons._id=?
|
WHERE weapons._id=?
|
||||||
""", weapon_id)
|
""", (weapon_id,), model_cls=model.Weapon)
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def get_weapon_by_name(self, name):
|
def get_weapon_by_name(self, name):
|
||||||
v = self._get_memoized("weapon", """
|
return self._query_one("weapon", """
|
||||||
SELECT * FROM weapons
|
SELECT * FROM weapons
|
||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
WHERE items.name=?
|
WHERE items.name=?
|
||||||
""", name)
|
""", (name,), model_cls=model.Weapon)
|
||||||
return v[0] if v else None
|
|
||||||
|
|
||||||
def get_monster_breaks(self, monster_id):
|
def get_monster_breaks(self, monster_id):
|
||||||
v = self._get_memoized("monster_breaks", """
|
"""
|
||||||
|
List of strings.
|
||||||
|
"""
|
||||||
|
def model(row):
|
||||||
|
return row["condition"][len("Break "):]
|
||||||
|
|
||||||
|
return self._query_all("monster_breaks", """
|
||||||
SELECT DISTINCT condition FROM hunting_rewards
|
SELECT DISTINCT condition FROM hunting_rewards
|
||||||
WHERE monster_id=? AND condition LIKE 'Break %'
|
WHERE monster_id=? AND condition LIKE 'Break %'
|
||||||
""", monster_id)
|
""", (monster_id,), model_cls=model)
|
||||||
|
|
||||||
return [row["condition"][len("Break "):] for row in v]
|
|
||||||
|
|||||||
257
mhapi/model.py
257
mhapi/model.py
@@ -1,28 +1,92 @@
|
|||||||
import string
|
import string
|
||||||
import json
|
import json
|
||||||
|
import urllib
|
||||||
|
from collections import defaultdict
|
||||||
|
import re
|
||||||
|
|
||||||
|
from mhapi.util import EnumBase
|
||||||
|
|
||||||
|
|
||||||
class RowModel(object):
|
class ModelJSONEncoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if hasattr(o, "as_data"):
|
||||||
|
return o.as_data()
|
||||||
|
return json.JSONEncoder.default(self, o)
|
||||||
|
|
||||||
|
|
||||||
|
class ModelBase(object):
|
||||||
|
def as_data(self):
|
||||||
|
raise NotImplemented()
|
||||||
|
|
||||||
|
def as_list_data(self):
|
||||||
|
raise NotImplemented()
|
||||||
|
|
||||||
|
def json_dumps(self, indent=2):
|
||||||
|
data = self.as_data()
|
||||||
|
return json.dumps(data, cls=ModelJSONEncoder, indent=indent)
|
||||||
|
|
||||||
|
def json_dump(self, fp, indent=2):
|
||||||
|
json.dump(self, fp, cls=ModelJSONEncoder, indent=indent)
|
||||||
|
|
||||||
|
|
||||||
|
class RowModel(ModelBase):
|
||||||
|
_list_fields = ["id", "name"]
|
||||||
|
_exclude_fields = []
|
||||||
|
_indexes = { "name": ["id"] }
|
||||||
|
|
||||||
def __init__(self, row):
|
def __init__(self, row):
|
||||||
self._row = row
|
|
||||||
self.id = row["_id"]
|
self.id = row["_id"]
|
||||||
|
self._row = row
|
||||||
|
self._data = dict(row)
|
||||||
|
del self._data["_id"]
|
||||||
|
self._data["id"] = self.id
|
||||||
|
for f in self._exclude_fields:
|
||||||
|
del self._data[f]
|
||||||
|
|
||||||
def __getattr__(self, name):
|
def __getattr__(self, name):
|
||||||
try:
|
try:
|
||||||
return self._row[name]
|
return self._data[name]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise AttributeError("'%s' object has no attribute '%s'"
|
raise AttributeError("'%s' object has no attribute '%s'"
|
||||||
% (self.__class__.__name__, name))
|
% (self.__class__.__name__, name))
|
||||||
|
|
||||||
def as_dict(self):
|
def __getitem__(self, key):
|
||||||
d = dict(self._row)
|
return self._data[key]
|
||||||
d["id"] = d["_id"]
|
|
||||||
del d["_id"]
|
|
||||||
return d
|
|
||||||
|
|
||||||
def as_json(self):
|
def fields(self):
|
||||||
data = self.as_dict()
|
return self._data.keys()
|
||||||
return json.dumps(data)
|
|
||||||
|
def as_data(self):
|
||||||
|
return self._data
|
||||||
|
|
||||||
|
def as_list_data(self):
|
||||||
|
list_data = {}
|
||||||
|
for key in self._list_fields:
|
||||||
|
list_data[key] = self[key]
|
||||||
|
return list_data
|
||||||
|
|
||||||
|
def update_indexes(self, data):
|
||||||
|
for key_field, value_fields in self._indexes.iteritems():
|
||||||
|
if key_field not in data:
|
||||||
|
data[key_field] = {}
|
||||||
|
self.update_index(key_field, value_fields, data[key_field])
|
||||||
|
|
||||||
|
def update_index(self, key_field, value_fields, data):
|
||||||
|
item = dict((k, self[k]) for k in value_fields)
|
||||||
|
key_value = self[key_field]
|
||||||
|
if key_value not in data:
|
||||||
|
data[key_value] = []
|
||||||
|
data[key_value].append(item)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if "name" in self._data:
|
||||||
|
name = urllib.quote(self.name, safe=" ")
|
||||||
|
else:
|
||||||
|
name = str(self.id)
|
||||||
|
return "%s '%s'" % (self.__class__.__name__, name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<mhapi.model.%s %d>" % (self.__class__.__name__, self.id)
|
||||||
|
|
||||||
|
|
||||||
class Quest(RowModel):
|
class Quest(RowModel):
|
||||||
@@ -47,11 +111,178 @@ class Quest(RowModel):
|
|||||||
or " all " in self.goal)
|
or " all " in self.goal)
|
||||||
|
|
||||||
def one_line_u(self):
|
def one_line_u(self):
|
||||||
return self._one_line_template.substitute(self.as_dict())
|
return self._one_line_template.substitute(self.as_data())
|
||||||
|
|
||||||
def full_u(self):
|
def full_u(self):
|
||||||
return self._full_template.substitute(self.as_dict())
|
return self._full_template.substitute(self.as_data())
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.full_u()
|
return self.full_u()
|
||||||
|
|
||||||
|
|
||||||
|
class SharpnessLevel(EnumBase):
|
||||||
|
"""
|
||||||
|
Enumeration for weapon sharpness levels.
|
||||||
|
"""
|
||||||
|
|
||||||
|
RED = 0
|
||||||
|
ORANGE = 1
|
||||||
|
YELLOW = 2
|
||||||
|
GREEN = 3
|
||||||
|
BLUE = 4
|
||||||
|
WHITE = 5
|
||||||
|
PURPLE = 6
|
||||||
|
|
||||||
|
ALL = range(0, PURPLE + 1)
|
||||||
|
|
||||||
|
_names = {
|
||||||
|
RED: "Red",
|
||||||
|
ORANGE: "Orange",
|
||||||
|
YELLOW: "Yellow",
|
||||||
|
GREEN: "Green",
|
||||||
|
BLUE: "Blue",
|
||||||
|
WHITE: "White",
|
||||||
|
PURPLE: "Purple",
|
||||||
|
}
|
||||||
|
|
||||||
|
_modifier = {
|
||||||
|
RED: (0.50, 0.25),
|
||||||
|
ORANGE: (0.75, 0.50),
|
||||||
|
YELLOW: (1.00, 0.75),
|
||||||
|
GREEN: (1.05, 1.00),
|
||||||
|
BLUE: (1.20, 1.06),
|
||||||
|
WHITE: (1.32, 1.12),
|
||||||
|
PURPLE: (1.44, 1.20),
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def raw_modifier(cls, sharpness):
|
||||||
|
return cls._modifier[sharpness][0]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def element_modifier(cls, sharpness):
|
||||||
|
return cls._modifier[sharpness][1]
|
||||||
|
|
||||||
|
|
||||||
|
class WeaponSharpness(ModelBase):
|
||||||
|
"""
|
||||||
|
Representation of the sharpness of a weapon, as a list of sharpness
|
||||||
|
points at each level. E.g. the 0th item in the list is the amount of
|
||||||
|
RED sharpness, the 1st item is ORANGE, etc.
|
||||||
|
"""
|
||||||
|
def __init__(self, db_string):
|
||||||
|
self.value_list = [int(s) for s in db_string.split(".")]
|
||||||
|
self._max = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max(self):
|
||||||
|
if self._max is None:
|
||||||
|
self._max = SharpnessLevel.RED
|
||||||
|
for i in xrange(SharpnessLevel.PURPLE+1):
|
||||||
|
if self.value_list[i] == 0:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
self._max = i
|
||||||
|
return self._max
|
||||||
|
|
||||||
|
def as_data(self):
|
||||||
|
return self.value_list
|
||||||
|
|
||||||
|
|
||||||
|
class Weapon(RowModel):
|
||||||
|
_list_fields = ["id", "wtype", "name"]
|
||||||
|
_indexes = { "name": ["id"],
|
||||||
|
"wtype": ["id", "name"] }
|
||||||
|
|
||||||
|
def __init__(self, weapon_item_row):
|
||||||
|
super(Weapon, self).__init__(weapon_item_row)
|
||||||
|
|
||||||
|
self._parse_sharpness()
|
||||||
|
|
||||||
|
def _parse_sharpness(self):
|
||||||
|
"""
|
||||||
|
Replace the sharpness field with parsed models for the normal
|
||||||
|
sharpness and the sharpness with Sharpness+1 skill.
|
||||||
|
"""
|
||||||
|
if self.wtype in ("Light Bowgun", "Heavy Bowgun", "Bow"):
|
||||||
|
self._data["sharpness"] = self._data["sharpness_plus"] = None
|
||||||
|
return
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class Monster(RowModel):
|
||||||
|
_list_fields = ["id", "class", "name"]
|
||||||
|
|
||||||
|
|
||||||
|
class Item(RowModel):
|
||||||
|
_list_fields = ["id", "type", "name"]
|
||||||
|
_indexes = { "name": ["id"],
|
||||||
|
"type": ["id", "name"] }
|
||||||
|
|
||||||
|
|
||||||
|
class Location(RowModel):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MonsterPartStateDamage(RowModel):
|
||||||
|
"""
|
||||||
|
Model for the damage to the monster on a particular hitbox and in
|
||||||
|
a particulare state.
|
||||||
|
"""
|
||||||
|
_exclude_fields = ["monster_id", "body_part"]
|
||||||
|
|
||||||
|
def __init__(self, part, state, row):
|
||||||
|
super(MonsterPartStateDamage, self).__init__(row)
|
||||||
|
self._data["part"] = part
|
||||||
|
self._data["state"] = state
|
||||||
|
|
||||||
|
|
||||||
|
class MonsterPartDamage(ModelBase):
|
||||||
|
"""
|
||||||
|
Model for collecting the damage to the monster on a particular hitbox
|
||||||
|
across different states.
|
||||||
|
"""
|
||||||
|
def __init__(self, part):
|
||||||
|
self.part = part
|
||||||
|
self.states = {}
|
||||||
|
|
||||||
|
def add_state(self, state, damage_row):
|
||||||
|
self.states[state] = MonsterPartStateDamage(self.part, state,
|
||||||
|
damage_row)
|
||||||
|
|
||||||
|
def as_data(self):
|
||||||
|
return self.states
|
||||||
|
|
||||||
|
|
||||||
|
class MonsterDamage(ModelBase):
|
||||||
|
"""
|
||||||
|
Model for the damage weakness to the monster in all the
|
||||||
|
different states and all the different hitboxes.
|
||||||
|
"""
|
||||||
|
def __init__(self, damage_rows):
|
||||||
|
self._rows = damage_rows
|
||||||
|
self.parts = {}
|
||||||
|
self.states = set()
|
||||||
|
for row in damage_rows:
|
||||||
|
part = row["body_part"]
|
||||||
|
state = "Normal"
|
||||||
|
m = re.match(r"([^(]+) \(([^)]+)\)", part)
|
||||||
|
if m:
|
||||||
|
part = m.group(1)
|
||||||
|
state = m.group(2)
|
||||||
|
self.states.add(state)
|
||||||
|
if part not in self.parts:
|
||||||
|
self.parts[part] = MonsterPartDamage(part)
|
||||||
|
self.parts[part].add_state(state, row)
|
||||||
|
|
||||||
|
def as_data(self):
|
||||||
|
return dict(
|
||||||
|
states=list(self.states),
|
||||||
|
parts=self.parts
|
||||||
|
)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ class GatherLocation(object):
|
|||||||
Track total expected value for an item in one location/rank.
|
Track total expected value for an item in one location/rank.
|
||||||
"""
|
"""
|
||||||
def __init__(self, location_row, rank, gathering_rows):
|
def __init__(self, location_row, rank, gathering_rows):
|
||||||
self.location_id = location_row["_id"]
|
self.location_id = location_row.id
|
||||||
self.location_name = location_row["name"]
|
self.location_name = location_row["name"]
|
||||||
self.rank = rank
|
self.rank = rank
|
||||||
self._rewards = []
|
self._rewards = []
|
||||||
@@ -675,12 +675,11 @@ class ItemRewards(object):
|
|||||||
def __init__(self, db, item_row):
|
def __init__(self, db, item_row):
|
||||||
self.db = db
|
self.db = db
|
||||||
self.item_row = item_row
|
self.item_row = item_row
|
||||||
self.item_id = item_row["_id"]
|
self.item_id = item_row.id
|
||||||
|
|
||||||
wyp_row = db.get_wyporium_trade(self.item_id)
|
wyp_row = db.get_wyporium_trade(self.item_id)
|
||||||
if wyp_row is not None:
|
if wyp_row is not None:
|
||||||
self.trade_unlock_quest = Quest(
|
self.trade_unlock_quest = db.get_quest(wyp_row["unlock_quest_id"])
|
||||||
db.get_quest(wyp_row["unlock_quest_id"]))
|
|
||||||
self.trade_item_row = self.item_row
|
self.trade_item_row = self.item_row
|
||||||
self.trade_item_id = self.item_id
|
self.trade_item_id = self.item_id
|
||||||
self.item_id = wyp_row["item_out_id"]
|
self.item_id = wyp_row["item_out_id"]
|
||||||
@@ -728,7 +727,7 @@ class ItemRewards(object):
|
|||||||
for rank in "LR HR G".split():
|
for rank in "LR HR G".split():
|
||||||
gl = GatherLocation(loc, rank, gathering_rows)
|
gl = GatherLocation(loc, rank, gathering_rows)
|
||||||
if gl:
|
if gl:
|
||||||
key = (loc["_id"], rank)
|
key = (loc.id, rank)
|
||||||
self._gather_items[key] = gl
|
self._gather_items[key] = gl
|
||||||
|
|
||||||
def _find_hunt_items(self):
|
def _find_hunt_items(self):
|
||||||
@@ -759,7 +758,7 @@ class ItemRewards(object):
|
|||||||
Get a list of the quests for acquiring a given item and the probability
|
Get a list of the quests for acquiring a given item and the probability
|
||||||
of getting the item, depending on cap or kill and luck skills.
|
of getting the item, depending on cap or kill and luck skills.
|
||||||
"""
|
"""
|
||||||
quests = self.db.get_item_quest_objects(self.item_id)
|
quests = self.db.get_item_quests(self.item_id)
|
||||||
if not quests:
|
if not quests:
|
||||||
return
|
return
|
||||||
for q in quests:
|
for q in quests:
|
||||||
|
|||||||
104
mhapi/skills.py
104
mhapi/skills.py
@@ -1,14 +1,8 @@
|
|||||||
|
|
||||||
|
from mhapi.util import EnumBase
|
||||||
|
|
||||||
|
|
||||||
class SkillEnum(object):
|
class CapSkill(EnumBase):
|
||||||
_names = dict()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def name(cls, skill_id):
|
|
||||||
return cls._names[skill_id]
|
|
||||||
|
|
||||||
|
|
||||||
class CapSkill(SkillEnum):
|
|
||||||
NONE = 0
|
NONE = 0
|
||||||
EXPERT = 1
|
EXPERT = 1
|
||||||
MASTER = 2
|
MASTER = 2
|
||||||
@@ -20,7 +14,7 @@ class CapSkill(SkillEnum):
|
|||||||
GOD: "Capture God" }
|
GOD: "Capture God" }
|
||||||
|
|
||||||
|
|
||||||
class LuckSkill(SkillEnum):
|
class LuckSkill(EnumBase):
|
||||||
NONE = 0
|
NONE = 0
|
||||||
GOOD = 1
|
GOOD = 1
|
||||||
GREAT = 2
|
GREAT = 2
|
||||||
@@ -32,7 +26,7 @@ class LuckSkill(SkillEnum):
|
|||||||
AMAZING: "Magnificent Luck" }
|
AMAZING: "Magnificent Luck" }
|
||||||
|
|
||||||
|
|
||||||
class CarvingSkill(SkillEnum):
|
class CarvingSkill(EnumBase):
|
||||||
NONE = 0
|
NONE = 0
|
||||||
PRO = 0 # prevent knockbacks but no extra carves
|
PRO = 0 # prevent knockbacks but no extra carves
|
||||||
FELYNE_LOW = 1
|
FELYNE_LOW = 1
|
||||||
@@ -51,3 +45,91 @@ QUEST_A = "A"
|
|||||||
QUEST_B = "B"
|
QUEST_B = "B"
|
||||||
QUEST_SUB = "Sub"
|
QUEST_SUB = "Sub"
|
||||||
|
|
||||||
|
|
||||||
|
class CriticalEye(EnumBase):
|
||||||
|
NONE = 0
|
||||||
|
ONE = 1
|
||||||
|
TWO = 2
|
||||||
|
THREE = 3
|
||||||
|
GOD = 4
|
||||||
|
|
||||||
|
_names = { NONE: "",
|
||||||
|
ONE: "Critical Eye +1",
|
||||||
|
TWO: "Critical Eye +2",
|
||||||
|
THREE: "Critical Eye +3",
|
||||||
|
GOD: "Critical God" }
|
||||||
|
|
||||||
|
_modifier = { NONE: 0,
|
||||||
|
ONE: 10,
|
||||||
|
TWO: 15,
|
||||||
|
THREE: 20,
|
||||||
|
GOD: 30 }
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def affinity_modifier(cls, skill):
|
||||||
|
return cls._modifier[skill]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def modified(cls, skill, affinity):
|
||||||
|
return affinity + cls.affinity_modifier(skill)
|
||||||
|
|
||||||
|
|
||||||
|
class AttackUp(EnumBase):
|
||||||
|
NONE = 0
|
||||||
|
SMALL = 1
|
||||||
|
MEDIUM = 2
|
||||||
|
LARGE = 3
|
||||||
|
XLARGE = 4
|
||||||
|
|
||||||
|
_names = { NONE: "",
|
||||||
|
SMALL: "Attack Up (S)",
|
||||||
|
MEDIUM: "Attack Up (M)",
|
||||||
|
LARGE: "Attack Up (L)",
|
||||||
|
XLARGE: "Attack Up (XL)" }
|
||||||
|
|
||||||
|
_modifier = { NONE: 0,
|
||||||
|
SMALL: 10,
|
||||||
|
MEDIUM: 15,
|
||||||
|
LARGE: 20,
|
||||||
|
XLARGE: 25 }
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def true_attack_modifier(cls, skill):
|
||||||
|
return cls._modifier[skill]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def modified(cls, skill, true_attack):
|
||||||
|
return true_attack + cls.true_attack_modifier(skill)
|
||||||
|
|
||||||
|
|
||||||
|
class ElementAttackUp(EnumBase):
|
||||||
|
NONE = 0
|
||||||
|
ONE = 1
|
||||||
|
TWO = 2
|
||||||
|
THREE = 3
|
||||||
|
ELEMENT = 4
|
||||||
|
|
||||||
|
_names = { NONE: "",
|
||||||
|
ONE: "(element) Atk +1",
|
||||||
|
TWO: "(element) Atk +2",
|
||||||
|
THREE: "(element) Atk +3",
|
||||||
|
ELEMENT: "Element Atk Up" }
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def modified(cls, skill, element):
|
||||||
|
if skill == cls.NONE:
|
||||||
|
return element
|
||||||
|
elif skill in (cls.ONE, cls.TWO, cls.THREE):
|
||||||
|
element = element * (1 + .05 * skill)
|
||||||
|
if skill == cls.ONE:
|
||||||
|
element += 40
|
||||||
|
elif skill == cls.TWO:
|
||||||
|
element += 60
|
||||||
|
elif skill == cls.THREE:
|
||||||
|
element += 90
|
||||||
|
elif skill == cls.ELEMENT:
|
||||||
|
element *= 1.1
|
||||||
|
else:
|
||||||
|
raise ValueError("Unknown element skill %s" % skill)
|
||||||
|
return element
|
||||||
|
|||||||
11
mhapi/util.py
Normal file
11
mhapi/util.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
"""
|
||||||
|
Shared utility classes and functions.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class EnumBase(object):
|
||||||
|
_names = dict()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def name(cls, enum_value):
|
||||||
|
return cls._names[enum_value]
|
||||||
Reference in New Issue
Block a user