From b96bed0bc49d9d5c34125a2467c7a5f290105d62 Mon Sep 17 00:00:00 2001 From: Bryce Allen Date: Thu, 23 Apr 2015 00:26:58 -0500 Subject: [PATCH] break improvements --- bin/mkjsonapi.py | 1 + mhapi/damage.py | 29 ++++----------------- mhapi/db.py | 9 +++++-- mhapi/model.py | 68 ++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 79 insertions(+), 28 deletions(-) diff --git a/bin/mkjsonapi.py b/bin/mkjsonapi.py index ad61869..7de04f7 100755 --- a/bin/mkjsonapi.py +++ b/bin/mkjsonapi.py @@ -43,6 +43,7 @@ def monster_json(db, path): m.update_indexes(indexes) data = m.as_data() damage = db.get_monster_damage(m.id) + damage.set_breakable(db.get_monster_breaks(m.id)) data["damage"] = damage.as_data() with open(monster_path, "w") as f: json.dump(data, f, cls=model.ModelJSONEncoder, indent=2) diff --git a/mhapi/damage.py b/mhapi/damage.py index 56fedcd..7099e7e 100644 --- a/mhapi/damage.py +++ b/mhapi/damage.py @@ -5,7 +5,7 @@ import difflib import re from mhapi import skills -from mhapi.model import SharpnessLevel +from mhapi.model import SharpnessLevel, _break_find WEAKPART_WEIGHT = 0.5 @@ -160,7 +160,7 @@ class WeaponMonsterDamage(object): Class for calculating how much damage a weapon does to a monster. 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, motion, sharp_plus=False, breakable_parts=None, attack_skill=skills.AttackUp.NONE, critical_eye_skill=skills.CriticalEye.NONE, @@ -168,7 +168,7 @@ class WeaponMonsterDamage(object): awaken=False): self.weapon = weapon_row self.monster = monster_row - self.monster_damage = monster_damage_rows + self.monster_damage = monster_damage self.motion = motion self.sharp_plus = sharp_plus self.breakable_parts = breakable_parts @@ -259,7 +259,8 @@ class WeaponMonsterDamage(object): part_damage.part = part if alt is None: if (self.breakable_parts - and _part_find(part, self.breakable_parts)): + and _break_find(part, self.monster_damage.parts.keys(), + self.breakable_parts)): part_damage.breakable = True if hitbox > self.max_raw_part[1]: self.max_raw_part = (part, hitbox) @@ -505,26 +506,6 @@ class PartDamage(object): hitbox, ehitbox, state) -def _part_find(part, breaks): - if (part == "Wing" and "Wing" not in breaks - and "Talon" in breaks): - # for Teostra - return "Talon" - if (part == "Head" and "Head" not in breaks - and "Horn" in breaks): - # for Fatalis - return "Horn" - if (part == "Winglegs" and "Winglegs" not in breaks - and "Wing Leg" in breaks): - # for Gore - return "Wing Leg" - #print "part_find", part, breaks - matches = difflib.get_close_matches(part, breaks, 1, 0.8) - if matches: - return matches[0] - return None - - def element_attack_up(value): return value * 1.1 diff --git a/mhapi/db.py b/mhapi/db.py index 04b5f1b..016dd43 100644 --- a/mhapi/db.py +++ b/mhapi/db.py @@ -309,9 +309,14 @@ class MHDB(object): List of strings. """ def model(row): - return row["condition"][len("Break "):] + condition = row["condition"] + if condition == "Tail Carve": + return "Tail" + else: + return condition[len("Break "):] return self._query_all("monster_breaks", """ SELECT DISTINCT condition FROM hunting_rewards - WHERE monster_id=? AND condition LIKE 'Break %' + WHERE monster_id=? + AND (condition LIKE 'Break %' OR condition = 'Tail Carve') """, (monster_id,), model_cls=model) diff --git a/mhapi/model.py b/mhapi/model.py index ef24fc7..5ee78f2 100644 --- a/mhapi/model.py +++ b/mhapi/model.py @@ -3,6 +3,7 @@ import json import urllib from collections import defaultdict import re +import difflib from mhapi.util import EnumBase @@ -242,6 +243,11 @@ class MonsterPartStateDamage(RowModel): self._data["part"] = part self._data["state"] = state + def __eq__(self, other): + for col in "impact cut shot ko ice dragon water fire thunder".split(): + if self[col] != other[col]: + return False + class MonsterPartDamage(ModelBase): """ @@ -250,14 +256,30 @@ class MonsterPartDamage(ModelBase): """ def __init__(self, part): self.part = part + self.breakable = False self.states = {} def add_state(self, state, damage_row): self.states[state] = MonsterPartStateDamage(self.part, state, damage_row) + # TODO: what about state 'Without Hide' for S.Nerscylla, which + # appears like it might be the same as Break Part, or might + # affect across hitzones. + if state == "Break Part": + # the default damage should be sorted before the alternate + # state damage + assert "Default" in self.states + if self.states[state] != self.states["Default"]: + # if the damage is different for break state, the part + # must be breakable, even if we couldn't find a match + # when searching break rewards + self.breakable = True def as_data(self): - return self.states + return dict( + breakable=self.breakable, + damage=self.states + ) class MonsterDamage(ModelBase): @@ -270,8 +292,11 @@ class MonsterDamage(ModelBase): self.parts = {} self.states = set() for row in damage_rows: + if row["cut"] == -1: + # -1 indicates missing data + continue part = row["body_part"] - state = "Normal" + state = "Default" m = re.match(r"([^(]+) \(([^)]+)\)", part) if m: part = m.group(1) @@ -286,3 +311,42 @@ class MonsterDamage(ModelBase): states=list(self.states), parts=self.parts ) + + def set_breakable(self, breakable_list): + """ + Set breakable flag on parts based on the breakable list from + rewards (use MHDB.get_monster_breaks). + """ + for name, part_damage in self.parts.iteritems(): + if _break_find(name, self.parts, breakable_list): + part_damage.breakable = True + + +def _break_find(part, parts, breaks): + # favor 'Tail Tip' over Tail for Basarios + if part == "Tail" and "Tail Tip" in parts: + return None + if part == "Tail Tip" and "Tail" in breaks: + return "Tail" + if part == "Neck/Tail" and "Tail" in breaks: + return "Tail" + if part == "Wing" and "Wing" not in breaks: + if "Talon" in breaks and "Talon" not in parts: + # for Teostra + return "Talon" + if part == "Head" and "Head" not in breaks: + if "Horn" in breaks and "Horn" not in parts: + # for Fatalis + return "Horn" + if "Ear" in breaks and "Ear" not in parts: + # Kecha Wacha + return "Ear" + if part == "Winglegs" and "Winglegs" not in breaks: + if "Wing Leg" in breaks and "Wing Leg" not in parts: + # for Gore + return "Wing Leg" + #print "part_find", part, breaks + matches = difflib.get_close_matches(part, breaks, 1, 0.8) + if matches: + return matches[0] + return None