4u stars filter, static damage gen, rise updates

main
Bryce Allen 3 years ago
parent 6b57d498b6
commit 228c594ca9

4
.gitignore vendored

@ -2,3 +2,7 @@
README.html
RECOMMENDATIONS.html
tmp/
*.bak
web/jsonapi/
web/*/rewards/
web/*/damage/

@ -4,14 +4,18 @@ import sys
import argparse
import shlex
import copy
import codecs
import os
import os.path
from collections import defaultdict
import _pathfix
from mhapi.db import MHDB, MHDBX
from mhapi.damage import MotionValueDB, WeaponMonsterDamage
from mhapi.damage import MotionValueDB, WeaponMonsterDamage, WeaponType
from mhapi.model import SharpnessLevel, Weapon, ItemStars
from mhapi import skills
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR, DAMAGE_TYPES
def weapon_match_tuple(arg):
@ -65,7 +69,7 @@ def _make_db_sharpness_string(level_string):
def weapon_stats_tuple(arg):
parts = arg.split(",")
#print "parts %r" % parts
#print("parts %r" % parts)
if len(parts) < 4:
print("not enough parts")
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
@ -160,8 +164,8 @@ def _add_skill_args(parser):
type=int, choices=list(range(0, 5)), default=0,
help="1-4 for CE+1, +2, +3 and Critical God")
parser.add_argument("-e", "--element-up",
type=int, choices=list(range(0, 5)), default=0,
help="1-4 for (element) Atk +1, +2, +3 and"
type=int, choices=list(range(0, 6)), default=0,
help="1-5 for (element) Atk +1, +2, +3 and"
" Element Attack Up")
parser.add_argument("-t", "--artillery",
type=int, choices=[0,1,2], default=0,
@ -206,6 +210,9 @@ def parse_args(argv):
parser.add_argument("--mhw", "--monster-hunter-world", action="store_true",
default=False,
help="Adjusted attack, use MHWorld values")
parser.add_argument("--mhr", "--monster-hunter-rise", action="store_true",
default=False,
help="True attack, use MHRise values")
parser.add_argument("-m", "--match", nargs="*",
help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW"
+" Include all matching weapons in their final form."
@ -216,19 +223,27 @@ def parse_args(argv):
+" Examples: 'Great Sword,Raw'"
+" 'Sword and Shield,Para'"
+" 'HH,Blast' 'Hammer'",
type=weapon_match_tuple, default=[])
type=weapon_match_tuple, default=[],
action="append")
parser.add_argument("-w", "--weapon-custom", nargs="*",
help="NAME,WEAPON_TYPE,TRUE_RAW,AFFINITY,SHARPNESS"
+"ELEMENT_TYPE,ELEMENT_ATTACK"
+" Add weapon based on stats."
+" Examples: 'DinoSnS,SnS,190,0,Blue,Fire,30'"
+" 'AkantorHam,Hammer,240,25,Green'",
type=weapon_stats_tuple, default=[])
type=weapon_stats_tuple, default=[],
action="append")
parser.add_argument("-q", "--quest-level",
help="village,guild[,permit[,arena]]",
type=quest_level_tuple)
parser.add_argument("monster",
help="Full name of monster")
parser.add_argument("-r", "--rarity",
help="include weapons of given type with max rarity",
type=int, nargs="?")
parser.add_argument("--html-out",
help="Write table of values as HTML and save to path")
parser.add_argument("--html-site",
help="Write entire site of all monster & quest levels")
parser.add_argument("-n", "--monster", help="Full name of monster")
parser.add_argument("weapon", nargs="*",
help="One or more weapons of same class to compare,"
" full names")
@ -273,6 +288,99 @@ def _print_headers(parts, damage_map_base):
print(" | ".join(cols))
def write_damage_html(path, monster, monster_damage, quest_level, names,
damage_map_base, weapon_damage_map, parts,
part_max_damage, monster_breaks, monster_stars):
print(path)
def uniform_average(weapon):
return weapon_damage_map[weapon].averages["uniform"]
names_sorted = list(names)
names_sorted.sort(key=uniform_average, reverse=True)
from mako.lookup import TemplateLookup
from mako.runtime import Context
tlookup = TemplateLookup(directories=["templates/damage"],
output_encoding="utf-8",
input_encoding="utf-8")
damage_template = tlookup.get_template("/monster_damage.html")
wtype = damage_map_base.weapon.wtype
weapon_damage_type = WeaponType.damage_type(wtype)
damage_types = list(DAMAGE_TYPES)
weapon_types = list(WEAPON_TYPES)
weapon_types.remove("Bow")
weapon_types.remove("Light Bowgun")
weapon_types.remove("Heavy Bowgun")
with codecs.open(path, "w", "utf8") as f:
template_args = dict(
monster=monster.name,
monster_damage=monster_damage,
damage_types=DAMAGE_TYPES,
weapon_types=weapon_types,
weapon_type=wtype,
weapon_damage_type=weapon_damage_type,
village_stars=quest_level[0],
guild_stars=quest_level[1],
part_names=parts,
part_max_damage=part_max_damage,
weapon_names=names_sorted,
weapon_damage_map=weapon_damage_map,
monster_breaks=set(monster_breaks),
monster_stars=monster_stars
)
ctx = Context(f, **template_args)
damage_template.render_context(ctx)
def write_damage_html_by_rarity(path, rarity, monster, monster_damage, names,
damage_map_base, weapon_damage_map, parts,
part_max_damage):
print(path)
def uniform_average(weapon):
return weapon_damage_map[weapon].averages["uniform"]
names_sorted = list(names)
names_sorted.sort(key=uniform_average, reverse=True)
from mako.lookup import TemplateLookup
from mako.runtime import Context
tlookup = TemplateLookup(directories=["templates/damage"],
output_encoding="utf-8",
input_encoding="utf-8")
damage_template = tlookup.get_template("/monster_damage_by_rarity.html")
wtype = damage_map_base.weapon.wtype
weapon_damage_type = WeaponType.damage_type(wtype)
damage_types = list(DAMAGE_TYPES)
weapon_types = list(WEAPON_TYPES)
weapon_types.remove("Bow")
weapon_types.remove("Light Bowgun")
weapon_types.remove("Heavy Bowgun")
with codecs.open(path, "w", "utf8") as f:
template_args = dict(
monster=monster.name,
monster_damage=monster_damage,
rarity=rarity,
damage_types=DAMAGE_TYPES,
weapon_types=weapon_types,
weapon_type=wtype,
weapon_damage_type=weapon_damage_type,
part_names=parts,
part_max_damage=part_max_damage,
weapon_names=names_sorted,
weapon_damage_map=weapon_damage_map,
)
ctx = Context(f, **template_args)
damage_template.render_context(ctx)
def print_sorted_damage(names, damage_map_base, weapon_damage_map, parts):
def uniform_average(weapon):
return weapon_damage_map[weapon].averages["uniform"]
@ -296,7 +404,8 @@ def print_sorted_damage(names, damage_map_base, weapon_damage_map, parts):
print("% 2d" % part_damage.average(), end=' ')
print()
if len(names) > 1:
# this is super buggy
if False and len(names) > 1:
w1 = weapon_damage_map[names_sorted[0]]
w2 = weapon_damage_map[names_sorted[1]]
m, ratio = w1.compare_break_even(w2)
@ -387,33 +496,19 @@ def match_quest_level(match_level, weapon_level):
return True
def main():
args = parse_args(None)
game_uses_true_raw = False
if args.quest_level:
comps = True
else:
comps = False
if args.monster_hunter_cross:
db = MHDBX()
game_uses_true_raw = True
elif args.monster_hunter_gen:
db = MHDB(game="gu", include_item_components=comps)
game_uses_true_raw = True
elif args.mhw:
db = MHDBX(game="mhw")
game_uses_true_raw = False
else:
db = MHDB(game="4u", include_item_components=comps)
motiondb = MotionValueDB(_pathfix.motion_values_path)
def run_comparison(args, db, motiondb, game_uses_true_raw, item_stars=None):
monster = db.get_monster_by_name(args.monster)
if not monster:
raise ValueError("Monster '%s' not found" % args.monster)
monster_damage = db.get_monster_damage(monster.id)
if not monster_damage.is_valid():
print("WARN: invalid damage data for monster '%s'" % args.monster)
return
if item_stars is None:
item_stars = ItemStars(db)
weapons = []
weapon_type = None
names_set = set()
@ -430,10 +525,14 @@ def main():
if skill_args:
skill_args_map[name] = skill_args
#print("args match", args.match)
for match_tuple in args.match:
# TODO: better validation
if isinstance(match_tuple, list):
# TODO: is this a bug in argparse!!!????
match_tuple = match_tuple[0]
wtype, element = match_tuple
if args.quest_level:
if args.quest_level or args.rarity:
final=None
else:
final=1
@ -448,7 +547,7 @@ def main():
names_set.add(w.name)
if args.weapon_custom:
weapons.extend(args.weapon_custom)
weapons.extend([w[0] for w in args.weapon_custom])
if not weapons:
print("Err: no matching weapons")
@ -457,6 +556,29 @@ def main():
names = [w.name for w in weapons]
monster_breaks = db.get_monster_breaks(monster.id)
part_names = monster_damage.keys()
for i in range(len(monster_breaks)):
if monster_breaks[i] not in monster_damage:
plural = monster_breaks[i] + "s"
if plural in monster_damage:
monster_breaks[i] = plural
states = monster_damage.state_names()
print("States:", states)
print("%-20s" % monster.name, " | ".join(monster_damage.keys()))
for dtype in DAMAGE_TYPES:
print(dtype[:2], ":", end=" ")
for part_name, part_damage in monster_damage.items():
if part_damage.state_diff(dtype):
print("% 2d (% 2d)"
% (part_damage[dtype],
part_damage.get_alt_state(dtype)),
end=" ")
else:
print("% 2d" % part_damage[dtype], end=" ")
print()
weapon_type = weapons[0]["wtype"]
if args.phial and weapon_type != "Charge Blade":
print("ERROR: phial option is only supported for Charge Blade")
@ -477,7 +599,6 @@ def main():
limit_parts = None
if args.quest_level:
item_stars = ItemStars(db)
village, guild, permit, arena = args.quest_level
print("Filter by Quest Levels:", args.quest_level)
weapons2 = dict()
@ -504,6 +625,48 @@ def main():
weapons = list(weapons2.values())
names = [w.name for w in weapons]
if args.rarity:
print("Filter by max rarity:", args.rarity)
weapons2 = dict()
by_name = dict()
for w in weapons:
if w.rarity <= args.rarity:
weapons2[w.id] = w
by_name[w.name] = w
# TODO: don't have parent ids for mhrise yet
if False and args.mhr:
# hack to remove most common dups
for wname in list(by_name.keys()):
if wname.endswith("+"):
base = wname[:-1]
suffix = "+"
else:
parts = wname.rsplit(" ", maxsplit=1)
if len(parts) == 1:
continue
base, suffix = parts
parent_name = None
if suffix == "+" or base.endswith("+"):
parent_name = base.rstrip("+")
elif suffix in ("II", "III", "VI", "VII"):
parent_name = wname[:-1]
elif suffix == "IV":
parent_name = base + " III"
elif suffix == "V":
parent_name = base + " IV"
if parent_name:
print("parent", parent_name)
if parent_name in by_name:
del weapons2[by_name[parent_name].id]
else:
parent_ids = set(w.parent_id for w in weapons2.values())
for wid in list(weapons2.keys()):
if wid in parent_ids:
del weapons2[wid]
weapons = list(weapons2.values())
names = [w.name for w in weapons]
part_max_damage = defaultdict(int)
weapon_damage_map = dict()
for row in weapons:
name = row["name"]
@ -512,6 +675,7 @@ def main():
raise ValueError(
"Weapon '%s' is different type, got '%s' expected '%s'"
% (name, row_type, weapon_type))
#print(name, row)
try:
skill_args = skill_args_map.get(name, args)
wd = WeaponMonsterDamage(row,
@ -526,7 +690,8 @@ def main():
limit_parts=args.parts,
frenzy_bonus=skill_args.frenzy,
is_true_attack=game_uses_true_raw,
blunt_power=skill_args.blunt_power)
blunt_power=skill_args.blunt_power,
game=db.game)
print("%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity), end=' ')
if wd.etype:
if wd.etype2:
@ -534,13 +699,16 @@ def main():
% (wd.eattack, wd.etype, wd.eattack2, wd.etype2), end=' ')
else:
print("(%4.0f %s)" % (wd.eattack, wd.etype), end=' ')
print(SharpnessLevel.name(wd.sharpness), end=' ')
print(SharpnessLevel.name(wd.sharpness), wd.sharpness_points, end=' ')
if skill_args != args:
print("{%s}" % ",".join(sn
for sn in get_skill_names(skill_args)
if sn))
else:
print()
for part in wd.parts:
if wd[part].average() > part_max_damage[part]:
part_max_damage[part] = wd[part].average()
weapon_damage_map[name] = wd
except ValueError as e:
print(str(e))
@ -564,6 +732,162 @@ def main():
print_sorted_damage(names, damage_map_base,
weapon_damage_map, parts)
if args.html_out:
if args.mhr:
if not args.rarity:
print("Error: --html-out with --mrh requires --rarity")
sys.exit(1)
write_damage_html_by_rarity(args.html_out, args.rarity,
monster, monster_damage,
names, damage_map_base,
weapon_damage_map, parts,
part_max_damage)
else:
if not args.quest_level:
print("Error: --html-out requires quest level (-q)")
sys.exit(1)
monster_stars = item_stars.get_monster_stars(monster.id)
write_damage_html(args.html_out, monster, monster_damage,
args.quest_level, names,
damage_map_base, weapon_damage_map, parts,
part_max_damage, monster_breaks,
monster_stars)
def write_html_site(args, db, motiondb, game_uses_true_raw):
if db.game == "4u":
village_max = 5
guild_max = 2
else:
raise ValueError("Not implemented")
monsters = db.get_monsters("Boss")
monster_names = [monster.name for monster in monsters]
weapon_types = db.get_weapon_types()
item_stars = ItemStars(db)
monster_stars = {}
for monster in monsters:
monster_stars[monster.id] = item_stars.get_monster_stars(monster.id)
n = 0
base_dir = args.html_site
for monster in monsters:
stars = monster_stars[monster.id]
if stars["Village"] is None:
vrange = [0]
else:
if stars["Village"] > 2:
start = stars["Village"] - 1
else:
start = stars["Village"]
vrange = list(range(start, 11))
if start > 2:
vrange = [0] + vrange
if stars["Guild"] is None:
grange = [0]
else:
if stars["Guild"] > 1:
start = stars["Guild"] - 1
else:
start = stars["Guild"]
grange = list(range(start, 11))
if start > 1:
grange = [0] + grange
for v in vrange:
for g in grange:
if v == 0:
if g == 0:
continue
v = 1
if g == 0:
g = 1
quest_dir = "v{}g{}".format(v, g)
quest_path = os.path.join(base_dir, monster.name, quest_dir)
if not os.path.isdir(quest_path):
os.makedirs(quest_path)
for wtype in weapon_types:
if "Bowgun" in wtype or wtype == "Bow":
continue
args.html_out = os.path.join(quest_path, wtype + ".html")
if os.path.isfile(args.html_out):
print(args.html_out, " exists, skipping")
continue
args.html_site = None
n += 1
args.monster = monster.name
args.quest_level = (v if v else 1, g if g else 1,
None, None)
args.match = [(wtype, None)]
run_comparison(args, db, motiondb, game_uses_true_raw,
item_stars=item_stars)
print("n =", n)
def write_html_site_rise(args, db, motiondb, game_uses_true_raw=True):
monsters = db.get_monsters()
weapon_types = db.get_weapon_types()
n = 0
base_dir = args.html_site
for monster in monsters:
for rarity in range(1, 11):
args.rarity = rarity
rarity_dir = "r{}".format(rarity)
mpath = os.path.join(base_dir, monster.name, rarity_dir)
if not os.path.isdir(mpath):
os.makedirs(mpath)
for wtype in weapon_types:
if "Bowgun" in wtype or wtype == "Bow":
continue
args.html_out = os.path.join(mpath, wtype + ".html")
if os.path.isfile(args.html_out):
print(args.html_out, " exists, skipping")
continue
args.html_site = None
n += 1
args.monster = monster.name
args.match = [(wtype, None)]
print(rarity, wtype)
run_comparison(args, db, motiondb, game_uses_true_raw)
print("n =", n)
def main():
args = parse_args(None)
game_uses_true_raw = False
if args.quest_level or args.html_site:
comps = True
else:
comps = False
if args.monster_hunter_cross:
db = MHDBX()
game_uses_true_raw = True
elif args.monster_hunter_gen:
db = MHDB(game="gu", include_item_components=comps)
game_uses_true_raw = True
elif args.mhw:
db = MHDBX(game="mhw")
game_uses_true_raw = False
SharpnessLevel._modifier = SharpnessLevel._modifier_mhw
elif args.mhr:
db = MHDBX(game="mhr")
game_uses_true_raw = True
SharpnessLevel._modifier = SharpnessLevel._modifier_mhw
else:
db = MHDB(game="4u", include_item_components=comps)
motiondb = MotionValueDB(_pathfix.motion_values_path)
if args.html_site:
if args.mhr:
write_html_site_rise(args, db, motiondb, game_uses_true_raw)
else:
write_html_site(args, db, motiondb, game_uses_true_raw)
else:
run_comparison(args, db, motiondb, game_uses_true_raw)
if __name__ == '__main__':
main()

@ -9,7 +9,7 @@ import argparse
import _pathfix
from mhapi.db import MHDB
from mhapi.db import MHDB, MHDBX
from mhapi import model
ENTITIES = """item weapon monster armor
@ -202,11 +202,12 @@ def weapon_json(db, path):
]
data["horn_melodies"] = melodies[w.horn_notes]
stars = item_stars.get_weapon_stars(w)
data["village_stars"] = stars["Village"]
data["guild_stars"] = stars["Guild"]
data["permit_stars"] = stars["Permit"]
data["arena_stars"] = stars["Arena"]
if db.game == "4u":
stars = item_stars.get_weapon_stars(w)
data["village_stars"] = stars["Village"]
data["guild_stars"] = stars["Guild"]
data["permit_stars"] = stars["Permit"]
data["arena_stars"] = stars["Arena"]
all_data.append(data)
@ -274,10 +275,13 @@ def horn_melody_json(db, path):
def main():
args = parse_args()
db = MHDB(game=args.game, include_item_components=True)
if args.game in ("mhx", "mhr"):
db = MHDBX(game=args.game)
else:
db = MHDB(game=args.game, include_item_components=True)
if not args.outpath:
args.outpath = os.path.join(_pathfix.web_path, "jsonapi")
args.outpath = os.path.join(_pathfix.web_path, "jsonapi", args.game)
if args.entities:
for entity in args.entities:
@ -288,7 +292,10 @@ def main():
args.entities = ENTITIES
if db.game != "4u":
args.entities.remove("wyporium")
try:
args.entities.remove("wyporium")
except:
pass
for entity in args.entities:
fn = globals()["%s_json" % entity]

@ -18,6 +18,7 @@ Returns list of dict, e.g.:
import sys
import re
import json
import lxml.etree
import requests
@ -34,14 +35,27 @@ MONSTER_RE = re.compile(
'(?:</td>)?<td style="[^"]*background-color:#EBEBEB;[^"]*">\s*'
'<a href="([^"]*)" [^>]* title="([^"]*)"')
# Old, MHX
"""
MONSTER_LINK_RE = re.compile(
'<a href="(/wiki/[^/"]*)"\s+class="image image-thumbnail link-internal"\s+'
'title="([^"]*)"\s+>')
JAPANESE_NAME_STR = '<h3 class="pi-data-label pi-secondary-font">Japanese:</h3>'
JAPANESE_NAME_RE = re.compile(
'<div class="pi-data-value pi-font">(.*)</div>')
"""
MONSTER_LINK_RE = re.compile(
'<a href="(/wiki/[^/"]*)" title="([^"]*)">([^<>]+)</a>')
"""
<h2 class="pi-item pi-item-spacing pi-title" data-source="Japanese Name"><ruby lang="ja"><rb lang="ja-Hani">ドスフロギィ<br/>(Dosufurogi)</rb></ruby></h2>
"""
JAPANESE_NAME_RE = re.compile('<h2 class="pi-item pi-item-spacing pi-title" data-source="Japanese Name"><ruby lang="ja"><rb lang="[^"]*">([^<>]*)<br/>.*</rb></ruby></h2>')
JAPANESE_TITLE_RE = re.compile(
'<div class="pi-data-value pi-font">([^<>]*)</div>')
def parse_wikia_monsters(f):
section = None
@ -72,20 +86,17 @@ def parse_wikia_monsters(f):
def get_jp_names(monster_path):
url = "http://monsterhunter.wikia.com" + monster_path
r = requests.get(url)
lines = r.text.split("\n")
root = lxml.etree.HTML(r.text)
names = []
while lines:
line = lines.pop(0).strip()
if JAPANESE_NAME_STR not in line:
continue
line = lines.pop(0).strip()
while line == "":
line = lines.pop(0).strip()
m = JAPANESE_NAME_RE.match(line)
assert m, "No match: " + line
names.append(parse_japanese_name(m.group(1)))
if len(names) == 2:
break
rbs = root.xpath('//h2[@data-source="Japanese Name"]//rb')
names.append(rbs[0].text)
divs = root.xpath('//div[@data-source="Japanese Title"]//div')
if divs:
names.append(divs[0].text)
return names

Binary file not shown.

File diff suppressed because it is too large Load Diff

@ -0,0 +1,254 @@
[
{
"name": "Rathian",
"link": "/monster/001_00.html"
},
{
"name": "Apex Rathian",
"link": "/monster/001_07.html"
},
{
"name": "Rathalos",
"link": "/monster/002_00.html"
},
{
"name": "Apex Rathalos",
"link": "/monster/002_07.html"
},
{
"name": "Khezu",
"link": "/monster/003_00.html"
},
{
"name": "Basarios",
"link": "/monster/004_00.html"
},
{
"name": "Diablos",
"link": "/monster/007_00.html"
},
{
"name": "Apex Diablos",
"link": "/monster/007_07.html"
},
{
"name": "Daimyo Hermitaur",
"link": "/monster/019_00.html"
},
{
"name": "Shogun Ceanataur",
"link": "/monster/020_00.html"
},
{
"name": "Rajang",
"link": "/monster/023_00.html"
},
{
"name": "Furious Rajang",
"link": "/monster/023_05.html"
},
{
"name": "Kushala Daora",
"link": "/monster/024_00.html"
},
{
"name": "Chameleos",
"link": "/monster/025_00.html"
},
{
"name": "Teostra",
"link": "/monster/027_00.html"
},
{
"name": "Tigrex",
"link": "/monster/032_00.html"
},
{
"name": "Nargacuga",
"link": "/monster/037_00.html"
},
{
"name": "Barioth",
"link": "/monster/042_00.html"
},
{
"name": "Barroth",
"link": "/monster/044_00.html"
},
{
"name": "Royal Ludroth",
"link": "/monster/047_00.html"
},
{
"name": "Great Baggi",
"link": "/monster/054_00.html"
},
{
"name": "Zinogre",
"link": "/monster/057_00.html"
},
{
"name": "Apex Zinogre",
"link": "/monster/057_07.html"
},
{
"name": "Great Wroggi",
"link": "/monster/059_00.html"
},
{
"name": "Arzuros",
"link": "/monster/060_00.html"
},
{
"name": "Apex Arzuros",
"link": "/monster/060_07.html"
},
{
"name": "Lagombi",
"link": "/monster/061_00.html"
},
{
"name": "Volvidon",
"link": "/monster/062_00.html"
},
{
"name": "Gore Magala",
"link": "/monster/071_00.html"
},
{
"name": "Shagaru Magala",
"link": "/monster/072_00.html"
},
{
"name": "Seregios",
"link": "/monster/077_00.html"
},
{
"name": "Astalos",
"link": "/monster/081_00.html"
},
{
"name": "Mizutsune",
"link": "/monster/082_00.html"
},
{
"name": "Apex Mizutsune",
"link": "/monster/082_07.html"
},
{
"name": "Crimson Glow Valstrax",
"link": "/monster/086_05.html"
},
{
"name": "Magnamalo",
"link": "/monster/089_00.html"
},
{
"name": "Scorned Magnamalo",
"link": "/monster/089_05.html"
},
{
"name": "Bishaten",
"link": "/monster/090_00.html"
},
{
"name": "Blood Orange Bishaten",
"link": "/monster/090_01.html"
},
{
"name": "Aknosom",
"link": "/monster/091_00.html"
},
{
"name": "Tetranadon",
"link": "/monster/092_00.html"
},
{
"name": "Somnacanth",
"link": "/monster/093_00.html"
},
{
"name": "Aurora Somnacanth",
"link": "/monster/093_01.html"
},
{
"name": "Rakna-Kadaki",
"link": "/monster/094_00.html"
},
{
"name": "Pyre Rakna-Kadaki",
"link": "/monster/094_01.html"
},
{
"name": "Almudron",
"link": "/monster/095_00.html"
},
{
"name": "Magma Almudron",
"link": "/monster/095_01.html"
},
{
"name": "Wind Serpent Ibushi",
"link": "/monster/096_00.html"
},
{
"name": "Goss Harag",
"link": "/monster/097_00.html"
},
{
"name": "Great Izuchi",
"link": "/monster/098_00.html"
},
{
"name": "Thunder Serpent Narwa",
"link": "/monster/099_00.html"
},
{
"name": "Narwa the Allmother",
"link": "/monster/099_05.html"
},
{
"name": "Anjanath",
"link": "/monster/100_00.html"
},
{
"name": "Pukei-Pukei",
"link": "/monster/102_00.html"
},
{
"name": "Kulu-Ya-Ku",
"link": "/monster/107_00.html"
},
{
"name": "Jyuratodus",
"link": "/monster/108_00.html"
},
{
"name": "Tobi-Kadachi",
"link": "/monster/109_00.html"
},
{
"name": "Bazelgeuse",
"link": "/monster/118_00.html"
},
{
"name": "Malzeno",
"link": "/monster/132_00.html"
},
{
"name": "Lunagaron",
"link": "/monster/133_00.html"
},
{
"name": "Garangolm",
"link": "/monster/134_00.html"
},
{
"name": "Gaismagorm",
"link": "/monster/135_00.html"
},
{
"name": "Espinas",
"link": "/monster/136_00.html"
}
]

File diff suppressed because it is too large Load Diff

@ -187,7 +187,8 @@ class WeaponMonsterDamage(object):
critical_eye_skill=skills.CriticalEye.NONE,
element_skill=skills.ElementAttackUp.NONE,
awaken=False, artillery_level=0, limit_parts=None,
frenzy_bonus=0, blunt_power=False, is_true_attack=False):
frenzy_bonus=0, blunt_power=False, is_true_attack=False,
game="4u"):
self.weapon = weapon_row
self.monster = monster_row
self.monster_damage = monster_damage
@ -222,13 +223,21 @@ class WeaponMonsterDamage(object):
else:
self.true_raw = (self.weapon["attack"]
/ WeaponType.multiplier(self.weapon_type))
if sharp_plus == 1:
self.sharpness = self.weapon.sharpness_plus.max
elif sharp_plus == 2:
self.sharpness = self.weapon.sharpness_plus2.max
if sharp_plus:
if game == "mhr":
sharp_n = self.weapon.sharpness_plus.get_rise_handicraft(sharp_plus)
self.sharpness, self.sharpness_points = sharp_n.get_max_points()
else:
if sharp_plus == 1:
self.sharpness, self.sharpness_points = self.weapon.sharpness_plus.get_max_points()
elif sharp_plus == 2:
self.sharpness, self.sharpness_points = self.weapon.sharpness_plus2.get_max_points()
else:
self.sharpness = self.weapon.sharpness.max
#print "sharpness=", self.sharpness
self.sharpness, self.sharpness_points = self.weapon.sharpness.get_max_points()
self.sharpness_name = SharpnessLevel.name(self.sharpness)
if self.weapon["affinity"]:
if (isinstance(self.weapon["affinity"], str)
and "/" in self.weapon["affinity"]):
@ -403,10 +412,10 @@ class WeaponMonsterDamage(object):
artillery_level=self.artillery_level)
self.cb_phial_damage[part][level] = damage_tuple
def uniform(self):
def uniform(self, break_weight=0.25):
average = 0.0
for part, damage in self.damage_map.items():
average += damage.average()
average += damage.average(break_weight)
return average / len(self.damage_map)
def weighted_raw(self):
@ -554,7 +563,12 @@ class WeaponMonsterDamage(object):
part = row["body_part"]
hitbox = int(row[raw_type])
if self.etype:
ehitbox = int(row[str(self.etype.lower())])
try:
ehitbox = int(row[str(self.etype.lower())])
except IndexError:
ehitbox = 0
print("WARN: bad etype {}, row = {}".format(
self.etype, row))
else:
ehitbox = 0
@ -618,6 +632,20 @@ class PartDamage(object):
def ehitbox(self):
return self.states[None].ehitbox
@property
def break_hitbox(self):
if "Break Part" in self.states:
return self.states["Break Part"].hitbox
else:
return self.hitbox
@property
def break_ehitbox(self):
if "Break Part" in self.states:
return self.states["Break Part"].ehitbox
else:
return self.ehitbox
@property
def break_raw(self):
if "Break Part" in self.states:
@ -668,7 +696,8 @@ class PartDamage(object):
# If the part has a hitbox with different damage in the break
# rows from the db, or if it's explicitly marked as breakable
# (done by checking hunt rewards for breaks).
return self.break_diff() > 0 or self.breakable
return (self.breakable or self.raw != self.break_raw
or self.element != self.break_element)
def average(self, break_weight=0.25, rage_weight=0.5):
if self.break_diff():
@ -688,7 +717,8 @@ class PartDamage(object):
+ self.total * (1 - rage_weight))
def set_damage(self, raw, element, hitbox, ehitbox, state=None):
if state == "Without Hide":
if state in ("Without Hide", "Charged", "Tail Inflated", "Savaged",
"Enraged", "Ice Shield"):
state = "Break Part"
self.states[state] = PartDamageState(raw, element,
hitbox, ehitbox, state)

@ -7,6 +7,7 @@ import sqlite3
import json
from mhapi import model
from mhapi.util import WEAPON_TYPES
def field_model(key):
@ -257,10 +258,15 @@ class MHDB(object):
return self._query_all("search_item", query, tuple(args),
no_cache=True, model_cls=model.Item)
def get_monsters(self):
return self._query_all("monsters", """
SELECT * FROM monsters
""", model_cls=model.Monster)
def get_monsters(self, monster_class=None):
args = []
where = []
if monster_class is not None:
where.append("WHERE class = ?")
args.append(monster_class)
args = tuple(args)
q = "SELECT * FROM monsters " + "\n".join(where)
return self._query_all("monsters", q, args, model_cls=model.Monster)
def get_monster_names(self):
"""
@ -317,12 +323,17 @@ class MHDB(object):
WHERE quest_id=?
""", (quest_id,))
def get_monster_quests(self, monster_id, rank):
return self._query_all("monster_quests", """
SELECT DISTINCT quests.* FROM quests, monster_to_quest
WHERE monster_to_quest.quest_id = quests._id
AND monster_to_quest.monster_id=? AND rank=?
""", (monster_id, rank), model_cls=model.Quest)
def get_monster_quests(self, monster_id, rank=None):
query = """SELECT DISTINCT quests.* FROM quests, monster_to_quest
WHERE monster_to_quest.quest_id = quests._id
AND monster_to_quest.monster_id=?"""
args = [monster_id]
if rank is not None:
query += " AND rank=?"
args += [rank]
return self._query_all("monster_quests", query,
tuple(args), model_cls=model.Quest)
def get_item_quests(self, item_id):
"""
@ -381,6 +392,14 @@ class MHDB(object):
WHERE monster_id=?
""", (monster_id,), collection_cls=model.MonsterDamage)
def get_weapon_types(self):
"""
List of strings.
"""
return self._query_all("weapon_types", """
SELECT DISTINCT wtype FROM weapons
""", model_cls=field_model("wtype"))
def get_weapons(self):
# Note: weapons only available via JP DLC have no localized
# name, filter them out.
@ -613,6 +632,13 @@ class MHDB(object):
item_data.set_components(ccomps, ucomps)
def _get_rise_num_slots(slot_list):
num_slots = 0
nslots = len(slot_list)
for i in range(nslots - 1, -1, -1):
num_slots += 10**(nslots - i - 1) * slot_list[i]
return num_slots
class MHDBX(object):
"""
@ -626,12 +652,14 @@ class MHDBX(object):
"""
Loads JSON data, keeps in memory.
"""
self.game = game
module_path = os.path.dirname(__file__)
self._mhx_db_path = os.path.abspath(os.path.join(module_path, "..",
"db", game))
self._4udb = MHDB()
self._4udb = MHDB(game="gu")
self._weapon_list = []
self._weapons_by_name = {}
self._weapons_by_id = {}
self._monsters_by_name = {}
self._monster_damage = {}
@ -644,10 +672,23 @@ class MHDBX(object):
with open(os.path.join(self._mhx_db_path, "weapon_list.json")) as f:
wlist = json.load(f)
for i, wdata in enumerate(wlist):
wdata["_id"] = i
if "_id" not in wdata:
wdata["_id"] = i
keys = ["awaken", "horn_notes",
"element", "element_attack",
"element_2", "element_2_attack",
"bug_level", "phial", "phial_value",
"shelling_type", "shelling_level",
"buy"]
for k in keys:
if k not in wdata:
wdata[k] = None
if self.game == "mhr":
wdata["num_slots"] = _get_rise_num_slots(wdata["slots"])
weapon = model.Weapon(wdata)
self._weapon_list.append(weapon)
self._weapons_by_name[weapon.name_jp] = weapon
self._weapons_by_id[weapon.id] = weapon
def _load_monsters(self):
names_path = os.path.join(self._mhx_db_path,
@ -675,7 +716,26 @@ class MHDBX(object):
row["monster_id"] = mid
damage_rows.append(row)
self._monster_damage[mid] = model.MonsterDamage(damage_rows)
self._monster_breaks[mid] = damage["_breaks"]
self._monster_breaks[mid] = damage.get("_breaks", [])
def get_monsters(self):
return list(self._monsters_by_name.values())
def get_weapons(self):
return list(self._weapon_list)
def get_weapon(self, weapon_id):
return self._weapons_by_id[weapon_id]
def get_weapons_by_parent(self, parent_id):
result = []
for w in self._weapon_list:
if w.parent_id == parent_id:
result.append(w)
return result
def get_weapon_types(self):
return WEAPON_TYPES
def get_weapon_by_name(self, name):
return self._weapons_by_name.get(name)
@ -705,9 +765,10 @@ class MHDBX(object):
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 None or string '1' or '0'
"""
final = int(final)
if final is not None:
final = int(final)
results = []
for w in self._weapon_list:
if wtype is not None and w.wtype != wtype:

@ -1,8 +1,11 @@
import os
import string
import json
from typing import NamedTuple
import urllib.request, urllib.parse, urllib.error
import re
import difflib
from collections import namedtuple
from mhapi.util import EnumBase
@ -162,7 +165,7 @@ class SharpnessLevel(EnumBase):
PURPLE: (1.44, 1.20),
}
# for mhx, mhgen, mhxx, and likely mhw
# for mhx, mhgen, mhxx
_modifier_mhx = {
RED: (0.50, 0.25),
ORANGE: (0.75, 0.50),
@ -172,14 +175,48 @@ class SharpnessLevel(EnumBase):
WHITE: (1.32, 1.125),
}
# world, rise
# https://twitter.com/AsteriskAmpers1/status/1372886666940137479
# https://monsterhunterworld.wiki.fextralife.com/Sharpness
# https://monsterhunterrise.wiki.fextralife.com/Sharpness
_modifier_mhw = {
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.15),
PURPLE: (1.39, 1.25),
}
@classmethod
def raw_modifier(cls, sharpness):
return cls._modifier[sharpness][0]
if _game() == "4u":
d = cls._modifier
elif _game() in ["gen", "gu", "mhx"]:
d = cls._modifier_mhx
else:
d = cls._modifier_mhw
return d[sharpness][0]
@classmethod
def element_modifier(cls, sharpness):
return cls._modifier[sharpness][1]
if _game() == "4u":
d = cls._modifier
elif _game() in ["gen", "gu", "mhx"]:
d = cls._modifier_mhx
else:
d = cls._modifier_mhw
return d[sharpness][1]
_GAME = None
def _game():
global _GAME
if _GAME is None:
_GAME = os.environ.get("MHAPI_GAME", "4u")
assert _GAME in ("4u", "gen", "gu", "mhx", "mhw", "mhr")
return _GAME
class WeaponSharpness(ModelBase):
@ -195,32 +232,54 @@ class WeaponSharpness(ModelBase):
self.value_list = [int(s) for s in db_string_or_list.split(".")]
# For MHX, Gen, no purple sharpness, but keep model the same for
# simplicity
if len(self.value_list) < SharpnessLevel.PURPLE + 1:
while len(self.value_list) < SharpnessLevel.PURPLE + 1:
self.value_list.append(0)
self._max = None
@property
def max(self):
if self._max is None:
self._max = SharpnessLevel.RED
for i in range(SharpnessLevel.PURPLE+1):
if self.value_list[i] == 0:
break
else:
for i in range(SharpnessLevel.PURPLE, -1, -1):
if self.value_list[i] > 0:
self._max = i
break
return self._max
def get_max_points(self):
return (self.max, self.value_list[self.max])
def get_rise_handicraft(self, n):
"""In Rise, there are 5 levels of Handicraft, each give 5 extra points; this
can be used to subtract off from sharpness_plus row"""
alt_values = list(self.value_list)
assert n >= 0 and n <= 5
minus_points = 25 - n * 5
for i in range(SharpnessLevel.PURPLE, -1, -1):
val = alt_values[i]
if minus_points <= val:
alt_values[i] -= minus_points
break
else:
alt_values[i] = 0
minus_points -= val
return WeaponSharpness(alt_values)
def as_data(self):
return self.value_list
def __str__(self):
return ",".join(str(v) for v in self.value_list)
class ItemCraftable(RowModel):
_list_fields = ["id", "name"]
def __init__(self, item_row):
super(ItemCraftable, self).__init__(item_row)
self.create_components = None
self.upgrade_components = None
if "create_components" not in item_row:
self.create_components = None
if "upgrade_components" not in item_row:
self.upgrade_components = None
def set_components(self, create_components, upgrade_components):
self.create_components = create_components
@ -230,10 +289,10 @@ class ItemCraftable(RowModel):
data = super(ItemCraftable, self).as_data()
if self.create_components is not None:
data["create_components"] = dict((item.name, item.quantity)
for item in self.create_components)
for item in _item_list(self.create_components))
if self.upgrade_components is not None:
data["upgrade_components"] = dict((item.name, item.quantity)
for item in self.upgrade_components)
for item in _item_list(self.upgrade_components))
return data
@ -373,6 +432,8 @@ class Weapon(ItemCraftable):
def __init__(self, weapon_item_row):
super(Weapon, self).__init__(weapon_item_row)
self._parse_sharpness()
if "name_jp" not in self._data:
self._data["name_jp"] = self._data["name"]
def _parse_sharpness(self):
"""
@ -385,11 +446,12 @@ class Weapon(ItemCraftable):
if isinstance(self._row["sharpness"], list):
# MHX JSON data, already desired format, but doesn't have
# purple so we append 0
self.sharpness = WeaponSharpness(self._row["sharpness"] + [0])
self.sharpness_plus = WeaponSharpness(
self._row["sharpness_plus"] + [0])
self.sharpness_plus2 = WeaponSharpness(
self._row["sharpness_plus2"] + [0])
row_sharpness = self._row["sharpness"]
row_sharpness_plus = self._row.get("sharpness_plus", row_sharpness)
row_sharpness_plus2 = self._row.get("sharpness_plus2", row_sharpness)
self.sharpness = WeaponSharpness(row_sharpness)
self.sharpness_plus = WeaponSharpness(row_sharpness_plus)
self.sharpness_plus2 = WeaponSharpness(row_sharpness_plus2)
else:
# 4U or gen data from db
parts = self._row["sharpness"].split(" ")
@ -410,6 +472,10 @@ class Weapon(ItemCraftable):
# english weapons, and not for Japanese DLC weapons.
return ord(self.name[0]) < 128
@property
def sharpness_name(self):
return SharpnessLevel.name(self.sharpness)
class Monster(RowModel):
_list_fields = ["id", "class", "name"]
@ -459,6 +525,12 @@ class MonsterPartStateDamage(RowModel):
def __ne__(self, other):
return not self.__eq__(other)
def __str__(self):
return str(self.as_data())
def __repr__(self):
return repr(self.as_data())
class MonsterPartDamage(ModelBase):
"""
@ -493,6 +565,49 @@ class MonsterPartDamage(ModelBase):
damage=self.states
)
def __str__(self):
return str(self.as_data())
def __repr__(self):
return repr(self.as_data())
def state_names(self):
return list(self.states.keys())
def get(self, damage_type):
return self.get_state(damage_type)
def get_break(self, damage_type):
self.get_state(damage_type, "Break Part")
def get_alt_state(self, damage_type):
return self.get_state(damage_type, self.alt_state)
def get_state(self, damage_type, state="Default"):
if state not in self.states:
state = "Default"
return self.states[state][damage_type]
def __getitem__(self, damage_type):
return self.get(damage_type)
@property
def alt_state(self):
if "Break Part" in self.states:
return "Break Part"
elif len(self.states) > 1:
alt_states = list(self.states.keys())
if "Default" in alt_states:
alt_states.remove("Default")
return alt_states[0]
else:
return "Default"
def state_diff(self, damage_type, state=None):
if state is None:
state = self.alt_state
return self.get_state(damage_type, state) - self.get(damage_type)
class MonsterDamage(ModelBase):
"""
@ -518,6 +633,10 @@ class MonsterDamage(ModelBase):
self.parts[part] = MonsterPartDamage(part)
self.parts[part].add_state(state, row)
def is_valid(self):
# TODO: more validation
return (len(self.states) > 0 and len(self.parts) > 0)
def as_data(self):
return dict(
states=list(self.states),
@ -534,6 +653,49 @@ class MonsterDamage(ModelBase):
#print "part %s is breakable [by rewards]" % name
part_damage.breakable = True
def state_names(self):
names = list(self.states)
names.sort()
if "Default" in names:
names.remove("Default")
names.insert(0, "Default")
return names
def keys(self):
return self.parts.keys()
def values(self):
return self.parts.values()
def items(self):
return self.parts.items()
def __len__(self):
return len(self.parts)
def __iter__(self):
return iter(self.parts)
@property
def alt_state(self):
alt_states = set(self.states)
alt_states.remove("Default")
if alt_states:
return alt_states.pop()
return "Default"
def avg(self, damage_type, state="Default"):
return sum(part.get_state(damage_type, state)
for part in self.values()) / len(self)
def alt_avg(self, damage_type):
return sum(part.get_alt_state(damage_type)
for part in self.values()) / len(self)
def max(self, damage_type, state="Default"):
return max(part.get_state(damage_type, state)
for part in self.values())
def get_decoration_values(skill_id, decorations):
"""
@ -622,7 +784,7 @@ def get_costs(db, weapon):
for cost in costs:
cost["zenny"] += upgrade_cost
cost["path"] += [weapon]
for item in weapon.upgrade_components:
for item in _item_list(weapon.upgrade_components):
if item.type == "Weapon":
continue
if item.name not in cost["components"]:
@ -638,7 +800,7 @@ def get_costs(db, weapon):
create_cost = dict(zenny=zenny,
path=[weapon],
components={})
for item in weapon.create_components:
for item in _item_list(weapon.create_components):
create_cost["components"][item.name] = item.quantity
costs = [create_cost] + costs
if weapon.buy:
@ -649,16 +811,48 @@ def get_costs(db, weapon):
return costs
CompItem = namedtuple("CompItem", "name quantity type")
def _item_list(comps):
if comps is None or isinstance(comps, list):
return comps
elif isinstance(comps, dict):
items = []
for k, v in comps.items():
items.append(CompItem(k, v, "material"))
return items
else:
raise ValueError("Unknown component type")
def rank_quest_level(game, hub, rank):
if game != "4u":
raise NotImplementedError()
if hub == "Village":
if rank == "G":
return 10
elif rank == "HR":
return 7
else:
return 1
elif hub == "Guild":
if rank == "G":
return 8
elif rank == "HR":
return 4
else:
return 1
class ItemStars(object):
"""
Get the game progress (in hub stars) required to make an item. Caches
values.
"""
def __init__(self, db):
self.db = db
self._item_stars = {} # item id -> stars dict
self._weapon_stars = {} # weapon id -> stars dict
self._monster_stars = {} # monster id -> stars dict
self._wyporium_trades = {}
if self.db.game == "4u":
@ -804,7 +998,26 @@ class ItemStars(object):
else:
print("Error: unknown hub", quest.hub)
# if available guild or village, then null out permit/arena values,
if stars["Village"] is None and stars["Guild"] is None:
# not available from quests or gathering, may be an
# Everwood only monster (4u). Set stars based on rank. Note
# that this is imperfect, because some monsters have special
# quests and unlock them appearing in the Everwood, but at least
# will prevent totally broken G-rank weapons showing up in
# low rank comparisons.
min_village = 10
min_guild = 10
for _, rank in monster_ranks:
v = rank_quest_level(self.db.game, "Village", rank)
if v < min_village:
min_village = v
g = rank_quest_level(self.db.game, "Guild", rank)
if g < min_guild:
min_guild = g
stars["Village"] = min_village
stars["Guild"] = min_guild
# If available guild or village, then null out permit/arena values,
# because they are more useful for filtering if limited to items
# exclusively available from permit or arena. Allows matching
# on based on meeting specified critera for
@ -816,3 +1029,21 @@ class ItemStars(object):
self._item_stars[item_id] = stars
return stars
def get_monster_stars(self, monster_id):
stars = self._monster_stars.get(monster_id)
if stars is not None:
return stars
stars = dict(Village=None, Guild=None, Permit=None, Arena=None,
Event=None)
quests = self.db.get_monster_quests(monster_id)
for quest in quests:
if quest.hub == "Caravan":
quest.hub = "Village"
if stars[quest.hub] is None or quest.stars < stars[quest.hub]:
stars[quest.hub] = quest.stars
self._monster_stars[monster_id] = stars
return stars

@ -53,6 +53,18 @@ WTYPE_ABBR = dict(
)
DAMAGE_TYPES = """
cut
impact
shot
fire
water
thunder
ice
dragon
""".split()
class EnumBase(object):
_names = dict()

@ -0,0 +1,268 @@
#!/usr/bin/env python3
import os.path
import sys
import re
import json
import lxml.etree
import requests
#WTYPES = ["Great Sword", "Long Sword", "Sword and Shield", "Dual Blades", "Lance", "Gunlance", "Hammer"]
WTYPES = ["Great Sword", "Lance", "Hammer"]
WIDTH_RE = re.compile(r'width: *(\d+)%;')
PART_RE = re.compile(r'(.*) x(\d+)( Points)?')
# MR Bone 20 pts.
PART_RE_MR = re.compile(r'(.*) (\d+) +pts\.?')
"""
<div class="progress" style="max-width: 100%; min-width: 100px;">
<div class="progress-bar danger-color-dark" style="width: 11%;">
&nbsp;
</div>
<div class="progress-bar warning-color-dark" style="width: 20%;">
&nbsp;
</div>
<div class="progress-bar warning-color" style="width: 12%;">
&nbsp;
</div>
<div class="progress-bar success-color" style="width: 0%;">
&nbsp;
</div>
<div class="progress-bar primary-color-dark" style="width: 0%;">
&nbsp;
</div>
<div class="progress-bar white" style="width: 0%;">
&nbsp;
</div>
</div>
"""
def parse_sharpness(div):
values = []
divs = div.xpath('div')
for div in divs:
style = div.get("style")
m = WIDTH_RE.match(style)
if m:
values.append(int(m.group(1)))
return values
def parse_rampage(td):
return td.xpath('ul/li/a/text()')
def parse_crafting(td):
materials = {}
for li in td.xpath('ul/li'):
atext = li.xpath('a/text()')
litext = li.xpath('text()')
if litext:
litext = litext[0].strip()
else:
print("Unknown format: ", lxml.etree.tostring(td))
return {}
if litext.endswith('\xa0'):
litext = litext.rstrip('\xa0')
if litext.endswith('.'):
litext = litext.rstrip('.')
if litext.endswith('l'):
litext = litext[:-1] + '1'
if litext.startswith('+ '):
atext += '+'
litext = litext[2:]
if litext.startswith('x'):
litext = litext[1:]
if atext:
atext = atext[0].strip()
if litext.endswith(" Points"):
litext = litext.rstrip(" Points")
atext += " Points"
#print("atext '" + atext + "' '" + litext + "'")
try:
materials[atext] = clean_int(litext)
except Exception as e:
print("WARN: failed parsing ", atext, litext)
if litext == 'l':
materials[atext] = 1
elif litext.isdigit():
materials['zenny'] = clean_int(litext)
else:
m = PART_RE.match(litext)
if not m:
m = PART_RE_MR.match(litext)
if m:
materials[m.group(1) + ' Points'] = int(m.group(2))
elif m.group(2):
materials[m.group(1) + ' Points'] = int(m.group(2))
else:
materials[m.group(1)] = int(m.group(2))
return materials
def clean_text(t):
t = t.strip()
t = t.rstrip('\xa0')
return t
def clean_int(s):
s = clean_text(s)
if not s:
return 0
return int(s)
def parse_element(td):
#pp("td", td)
etype = td.xpath('a/text()')
if etype:
values = td.xpath('./text()')
if values:
value = clean_int(values[0].strip())
return dict(type=etype[0], attack=value)
return dict(type=None, attack=None)
def parse_rarity(td):
text = td.xpath('.//text()')
if text:
parts = text[0].split()
if len(parts) > 1:
return clean_int(text[0].split()[1])
return 8
def parse_slots(td):
slots = []
for img in td.xpath('.//img'):
title = img.get("title")
if title and title.startswith('gem_'):
parts = title.split("_")
level = int(parts[2])
slots.append(level)
return slots
def adjust_slots_rampage(data):
if data['rarity'] >= 8:
data['rampage_slot'] = data['slots'][-1]
data['slots'] = data['slots'][:-1]
else:
data['rampage_slot'] = 0
def gl_parse_tr(tr):
data = {}
cells = tr.xpath('td')
#print(lxml.etree.tostring(cells[9]))
# Name
name = cells[0]
#print(name)
data['name'] = name.xpath('a/text()')[0]
data['slots'] = parse_slots(name)
data['sharpness'] = parse_sharpness(name.xpath('div')[0])
data['attack'] = clean_int(cells[1].text)
element = parse_element(cells[2])
data['element'] = element['type']
data['element_attack'] = element['attack']
data['element_2'] = None
data['element_2_attack'] = None
data['affinity'] = clean_int(cells[3].text.rstrip('%'))
data['defense'] = clean_int(cells[4].text)
data['shot_type'] = cells[5].text
data['level'] = clean_int(cells[6].text.split()[1])
data['rarity'] = parse_rarity(cells[7])
data['rampage_skills'] = parse_rampage(cells[8])
data['crafting'] = parse_crafting(cells[9])
adjust_slots_rampage(data)
return data
def default_parse_tr(tr):
data = {}
cells = tr.xpath('td')
#print(lxml.etree.tostring(cells[9]))
if len(cells) == 10:
return gl_parse_tr(tr)
#print("cels", [c.text for c in cells])
# Name
name = cells[0]
data['name'] = name.xpath('a/text()')[0]
data['slots'] = parse_slots(name)
data['sharpness'] = parse_sharpness(name.xpath('div')[0])
data['attack'] = clean_int(cells[1].text)
element = parse_element(cells[2])
data['element'] = element['type']
data['element_attack'] = element['attack']
data['element_2'] = None
data['element_2_attack'] = None
data['affinity'] = clean_int(cells[3].text.rstrip('%'))
data['defense'] = clean_int(cells[4].text)
data['rarity'] = parse_rarity(cells[5])
data['rampage_skills'] = parse_rampage(cells[6])
data['crafting'] = parse_crafting(cells[7])
adjust_slots_rampage(data)
return data
def parse_fextralife_weapons(text):
root = lxml.etree.HTML(text)
weapons = []
table = root.xpath('//div[@id="wiki-content-block"]//table')[0]
rows = table.xpath('tbody/tr')
#print("nrows", len(rows))
for tr in rows:
data = default_parse_tr(tr)
weapons.append(data)
return weapons
def pp(name, e):
if isinstance(e, list):
for i, ei in enumerate(e):
pp(name + "[" + str(i) + "]", ei)
else:
print(name, e.tag)
print(lxml.etree.tostring(e, pretty_print=True))
def _main():
indir = sys.argv[1]
outpath = sys.argv[2]
weapon_list_all = []
for wtype in WTYPES:
print(wtype)
fpath = os.path.join(indir, wtype + ".html")
with open(fpath) as f:
text = f.read()
weapon_list = parse_fextralife_weapons(text)
for w in weapon_list:
w["wtype"] = wtype
weapon_list_all.extend(weapon_list)
with open(outpath, "w") as f:
json.dump(weapon_list_all, f, indent=2)
if __name__ == '__main__':
_main()

@ -0,0 +1,159 @@
#!/usr/bin/env python3
import sys
import os.path
import time
import re
import json
import lxml.etree
import requests
PART_HEADER_MAP = dict(Slash="Cut",
Impact="Impact",
Shot="Shot",
Fire="Fire",
Water="Water",
Ice="Ice",
Thunder="Thunder",
Dragon="Dragon")
def _td_part_id(td):
s = td.xpath('.//text()')[0].strip()
if s.startswith("["):
s = s[1:2]
return int(s)
def _td_part_break(td):
text = td.text or ""
text = text.strip()
if text:
m = re.match(r"\(x(\d+)\) (\d+)", text)
print(text, m, m.group(1), m.group(2))
return dict(count=int(m.group(1)), damage=int(m.group(2)))
return dict(count=0, damage=0)
def _td_part_sever(td):
text = td.text or ""
text = text.strip()
if text:
m = re.match(r"\((\w+)\) (\d+)", text)
return dict(type=m.group(1), damage=int(m.group(2)))
return dict(type="", damage=0)
def get_monster_data(link):
hit_data = {}
base = "https://mhrise.mhrice.info"
url = base + link
result = requests.get(url)
root = lxml.etree.HTML(result.content)
sections = root.xpath("//section")
hit_table = None
parts_table = None
for section in sections:
h2 = section.xpath('h2')
if h2 and h2[0].text:
if hit_table is None and h2[0].text.lower().startswith("hitzone"):
hit_table = section.xpath('.//table')[0]
elif parts_table is None and h2[0].text.lower().startswith("parts"):
parts_table = section.xpath('.//table')[0]
#pp("hit_table", hit_table)
#pp("tr", hit_table.xpath('thead/tr'))
header_cells = hit_table.xpath('thead/tr/th')
header_names = [th.text for th in header_cells]
#print("names", header_names)
rows = hit_table.xpath('tbody/tr')
part_id_name_map = {}
for row in rows:
if 'invalid' in row.attrib.get('class', ""):
continue
#pp("tr", row)
cols = dict(zip(header_names, row.xpath('td')))
name_td = cols["Name"]
#pp("name_td", name_td)
name_en_span = name_td.xpath('.//span[@lang="en"]/span')
if not name_en_span:
continue
name = name_en_span[0].text
#pp("part", cols["Part"].xpath('.//text()'))
part_id = _td_part_id(cols["Part"])
part_id_name_map[part_id] = name
hit_data[name] = {}
for k in PART_HEADER_MAP.keys():
hit_data[name][PART_HEADER_MAP[k]] = int(cols[k].text)
#print(hit_data)
return hit_data
# add break/sever data
header_cells = parts_table.xpath('thead/tr/th')
header_names = [th.text for th in header_cells]
#print(header_names)
rows = parts_table.xpath('tbody/tr')
breaks = []
for row in rows:
if 'invalid' in row.attrib.get('class', ""):
continue
cols = dict(zip(header_names, row.xpath('td')))
part_id = _td_part_id(cols["Part"])
part_name = part_id_name_map[part_id]
hit_data[part_name]["_stagger"] = int(cols["Stagger"].text)
part_break = cols["Break"].text or ""
part_sever = cols["Sever"].text or ""
part_break = part_break.strip()
part_sever = part_sever.strip()
hit_data[part_name]["_break"] = _td_part_break(cols["Break"])
hit_data[part_name]["_sever"] = _td_part_sever(cols["Sever"])
if part_break or part_sever:
breaks.append(part_name)
hit_data["_breaks"] = breaks
return hit_data
def pp(name, e):
if isinstance(e, list):
for i, ei in enumerate(e):
pp(name + "[" + str(i) + "]", ei)
else:
print(name, e.tag)
print(lxml.etree.tostring(e, pretty_print=True))
def get_monster_list():
result = requests.get("https://mhrise.mhrice.info/monster.html")
root = lxml.etree.HTML(result.content)
monster_li = root.xpath('//ul[@id="slist-monster"]//li')
monsters = []
for li in monster_li:
name = li.xpath('.//span[@lang="en"]/span')[0].text
link = li.xpath('a')[0].attrib['href']
monsters.append(dict(name=name, link=link))
return monsters
def _main():
outdir = sys.argv[1]
monster_list = get_monster_list()
with open(os.path.join(outdir, "monster_list.json"), "w") as f:
json.dump(monster_list, f, indent=2)
monster_hitboxes = {}
for m in monster_list:
print(m["name"])
try:
monster_hitboxes[m["name"]] = get_monster_data(m["link"])
except Exception as e:
print("ERR: failed to parse hitzones for ", m["name"])
print(repr(e), str(e))
time.sleep(0.5)
with open(os.path.join(outdir, "monster_hitboxes.json"), "w") as f:
json.dump(monster_hitboxes, f, indent=2)
if __name__ == '__main__':
_main()

@ -0,0 +1,352 @@
#!/usr/bin/env python3
import sys
import os.path
import time
import re
import json
from pprint import pprint
from collections import defaultdict
import lxml.etree
import requests
import _pathfix
from mhapi.util import WEAPON_TYPES
MAX_PER_TYPE = 100000
def pp(name, e):
if isinstance(e, list):
for i, ei in enumerate(e):
pp(name + "[" + str(i) + "]", ei)
else:
print(name, e.tag)
print(lxml.etree.tostring(e, pretty_print=True))
def parse_sharpness(value_span):
bar_span = value_span.xpath('.//span[@class="mh-sharpness-bar"]')[0]
sharp_spans = bar_span.xpath('.//span')
i = 0
last_color_num = -1
values = []
values_plus = []
for sharp_span in sharp_spans:
# <span class="mh-sharpness mh-sharpness-color-0" style="left:0%;width:47.5%;"></span>
attr_style = sharp_span.attrib["style"]
attr_class = sharp_span.attrib["class"]
classes = attr_class.split()
half = False
for class_name in classes:
if class_name.startswith("mh-sharpness-color-"):
color_num = int(class_name[-1])
if class_name == "mh-sharpness-half":
half = True
styles = attr_style.split(";")
for s in styles:
s = s.strip()
if not s:
continue
parts = s.split(":")
if parts[0] == "width":
value = int(2*float(parts[1].rstrip("%")))
break
if value == 0:
continue
if half:
if not values_plus:
values_plus = list(values)
if color_num == last_color_num:
values_plus[-1] += value
else:
values_plus.append(value)
else:
# fill in missing colors, if any
while i < color_num:
values.append(0)
i += 1
values.append(value)
i += 1
last_color_num = color_num
return values, values_plus
def _map_element(e):
if e == "Bomb":
return "Blast"
if e == "Paralyze":
return "Paralysis"
return e
def get_weapon_details(wtype, name, link):
data = dict(wtype=wtype, name=name)
url = "https://mhrise.mhrice.info" + link
result = requests.get(url)
root = lxml.etree.HTML(result.content)
icon_div = root.xpath('//div[@class="mh-title-icon"]/div[@class="mh-colored-icon"]/div')[0]
rarity_class = icon_div.attrib["class"]
data["rarity"] = int(rarity_class.split("-")[-1])
stat_div = root.xpath('//div[@class="mh-kvlist"]')[0]
kvlist = stat_div.xpath('.//p[@class="mh-kv"]')
for kv in kvlist:
spans = kv.xpath('span')
key = spans[0].text.strip().lower()
if key in set(["attack", "affinity", "defense"]):
value = spans[1].text
value = value.rstrip("%")
data[key.lower()] = int(value)
elif key == "element":
value_spans = spans[1].xpath("span")
value = value_spans[0].text.strip()
if value:
parts = value.split()
if parts[0] == "None":
data["element"] = None
data["element_attack"] = None
else:
data["element"] = _map_element(parts[0])
data["element_attack"] = int(parts[1])
if len(value_spans) > 1:
value = value_spans[1].text.strip()
parts = value.split()
data["element_2"] = _map_element(parts[0])
data["element_2_attack"] = int(parts[1])
else:
data["element_2"] = None
data["element_2_attack"] = None
elif key == "slot":
# <img alt="A level-2 slot" class="mh-slot" src="/resources/slot_1.png">
# <img alt="A level-4 slot" class="mh-slot-large" src="/resources/slot_3.png">
slots = []
value_span = spans[1]
slot_imgs = value_span.xpath('.//span[@class="mh-slot-outer"]/img')
for slot_img in slot_imgs:
src = slot_img.attrib["src"]
m = re.match(r".*/slot_(\d+)\.png", src)
if m:
svalue = int(m.group(1)) + 1
slots.append(svalue)
data["slots"] = slots
elif key == "rampage slot":
slots = []
value_span = spans[1]
slot_imgs = value_span.xpath('.//span[@class="mh-slot-outer"]/img')
for slot_img in slot_imgs:
src = slot_img.attrib["src"]
m = re.match(r".*/slot_(\d+).png", src)
if m:
svalue = int(m.group(1)) + 1
slots.append(svalue)
data["rampage_slots"] = slots
elif key == "sharpness":
value_span = spans[1]
sharp, sharp_plus = parse_sharpness(value_span)
data["sharpness"] = sharp
data["sharpness_plus"] = sharp_plus
elif key == "bottle":
value = spans[1].text.strip()
if wtype == "Charge Blade":
key = "phial"
if value == "Power":
value = "Impact"
if value == "StrongElement":
value = "Element"
if wtype == "Switch Axe":
key = "phial"
parts = value.split()
value = parts[0]
if value == "StrongElement":
value = "Element"
if value == "DownStamina":
value = "Exhaust"
phial_num = int(parts[1])
if phial_num > 0:
data["phial_value"] = phial_num
data[key] = value
elif key == "type":
value = spans[1].text.strip()
parts = value.split()
value = parts[0]
if len(parts) > 1:
level = int(parts[1])
data["shelling_level"] = level
if wtype == "Gunlance":
key = "shelling_type"
if value == "Radial":
value = "Long"
elif value == "Diffusion":
value = "Wide"
data[key] = value
elif key == "insect level":
value = spans[1].text.strip()
data["bug_level"] = int(value)
sections = root.xpath("//section")
craft_table = None
for section in sections:
h2 = section.xpath("h2/text()")
if h2 and h2[0] == "Crafting":
craft_table = section.xpath("div/table/tbody")[0]
break
if craft_table is not None:
rows = craft_table.xpath("tr")
for row in rows:
cells = row.findall("td")
craft_type = cells[0].text.strip()
if craft_type.startswith("Forge"):
zenny, comps = get_components(cells)
data["creation_cost"] = zenny
data["create_components"] = comps
elif craft_type.startswith("Upgrade"):
zenny, comps = get_components(cells)
data["upgrade_cost"] = zenny
data["upgrade_components"] = comps
return data
def get_components(cells):
zenny = int(cells[1].text)
cmat_text = cells[2].text
components = {}
if cmat_text != "-":
cmat_name = cells[2].xpath('.//span[@lang="en"]/span')[0].text
cmat_points_string = cells[2].xpath("span")[0].tail
cmat_points = int(cmat_points_string.split(" ")[0])
components[cmat_name] = cmat_points
li_mats = cells[3].xpath("ul/li")
for li in li_mats:
count = int(li.text.strip().rstrip("x"))
name = li.xpath('.//span[@lang="en"]/span')[0].text
components[name] = count
return (zenny, components)
def get_rice_id(link):
# /weapon/GreatSword_026.html
fname_base, _ = os.path.splitext(os.path.basename(link))
_, tail = fname_base.rsplit("_", maxsplit=1)
return int(tail)
def get_weapon_list(wtype, id_offset):
if wtype == "Sword and Shield":
ftype = "short_sword"
elif wtype == "Hunting Horn":
ftype = "horn"
elif wtype == "Gunlance":
ftype = "gun_lance"
elif wtype == "Switch Axe":
ftype = "slash_axe"
elif wtype == "Charge Blade":
ftype = "charge_axe"
else:
ftype = wtype.lower().replace(" ", "_")
list_fname = ftype + ".html"
result = requests.get("https://mhrise.mhrice.info/weapon/" + list_fname)
root = lxml.etree.HTML(result.content)
weapon_tree_li = root.xpath('//div[@class="mh-weapon-tree"]//li')
weapons = []
seen = set()
for li in weapon_tree_li:
listack = [li]
name_stack = [None]
while listack:
current_li = listack.pop()
parent_name = name_stack.pop()
a = current_li.xpath('a[@class="mh-icon-text"]')[0]
sublists = current_li.xpath('ul/li')
name = a.xpath('.//span[@lang="en"]/span')[0].text
link = a.attrib['href']
name_stack.extend([name] * len(sublists))
listack.extend(sublists)
if link in seen:
print("WARN: Duplicate ", name, link)
continue
seen.add(link)
id_ = get_rice_id(link) + id_offset
final = (len(sublists) == 0)
wdata = dict(name=name, link=link, _id=id_, parent_name=parent_name, final=final)
weapons.append(wdata)
return weapons
def test_details():
tests = [
("Great Sword", "Sinister Shadowblade+", "/weapon/GreatSword_403.html"),
("Great Sword", "Redwing Claymore I", "/weapon/GreatSword_068.html"),
("Great Sword", "Defender Great Sword I", "/weapon/GreatSword_132.html"),
("Great Sword", "Kamura Warrior Cleaver", "/weapon/GreatSword_300.html"),
("Dual Blades", "Blood Wind Skards+", "/weapon/DualBlades_319.html"),
("Switch Axe", "Arzuros Jubilax", "/weapon/SlashAxe_323.html"),
("Switch Axe", "Leave-Taker+", "/weapon/SlashAxe_307.html"),
("Insect Glaive", "Fine Kamura Glaive", "/weapon/InsectGlaive_302.html"),
]
for t in tests:
print(t)
d = get_weapon_details(*t)
pprint(d)
print()
def _main():
weapons_type_name_map = defaultdict(dict)
weapons_data = []
outdir = sys.argv[1]
outfile = os.path.join(outdir, "weapon_list.json")
if os.path.exists(outfile):
print("Loading existing data from ", outfile)
with open(outfile) as f:
old_data = json.load(f)
for d in old_data:
wtype_name_map = weapons_type_name_map[d["wtype"]]
if d["name"] in wtype_name_map:
print("Removing duplicate ", d["wtype"], d["name"])
continue
wtype_name_map[d["name"]] = d
for itype, wtype in enumerate(WEAPON_TYPES):
wtype_name_map = weapons_type_name_map[wtype]
weapons = get_weapon_list(wtype, (itype+1) * MAX_PER_TYPE)
if not weapons:
print("WARN: no weapons of type", wtype)
continue
name_id_map = {}
for w in weapons:
# always re-calculate IDs
name_id_map[w["name"]] = w["_id"]
if w["parent_name"]:
w["parent_id"] = name_id_map[w["parent_name"]]
else:
w["parent_id"] = None
data = wtype_name_map.get(w["name"])
if data is not None:
print("UP ", wtype, w["_id"], w["name"], w["link"])
data.update(w)
weapons_data.append(data)
continue
print("ADD", wtype, w["_id"], w["name"], w["link"])
data = get_weapon_details(wtype, w["name"], w["link"])
data.update(w)
weapons_data.append(data)
time.sleep(0.5)
with open(os.path.join(outdir, "weapon_list.json"), "w") as f:
json.dump(weapons_data, f, indent=2)
if __name__ == '__main__':
#test_details()
_main()

@ -0,0 +1,36 @@
## -*- coding: utf-8 -*-
<!DOCTYPE html>
<html>
<head>
<title>Poogie's Calculator - ${self.title()}</title>
<meta charset="utf-8" />
<!-- Include meta tag to ensure proper rendering and touch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/js/jquery.min.js"></script>
<!--link rel="stylesheet" href="/css/jquery-ui.min.css" /-->
<!--script src="/js/jquery-ui.min.js"></script-->
<script src="/js/common.js"></script>
</head>
<link rel="stylesheet" href="/css/poogie.css">
<style>
body {
font-size: 1em;
line-height: 1.3;
font-family: sans-serif;
}
img.icon { width: 20px; height: 20px; }
</style>
<body>
<h1><%block name="title"/></h1>
${self.body()}
</body>

@ -0,0 +1,67 @@
<%inherit file="base.html" />
<div id="poogie-header">
<div class="poogie-header-content">
<div class="poogie-header-title">Menu</div>
</div>
</div>
<div id="poogie-content">
<div class="pure-menu">
<ul class="pure-menu-list">
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="skilltrees-en.html">Skill Trees</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link" href="hunterarts.html">Hunter Arts</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link" href="monsters-en.html">Monsters</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="monster-titles.html">Monster Titles</a>
</li>
<li class="pure-menu-heading poogie-menu-heading">Items</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="items-en.html">Items (Usable, Gatherable)</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="items-carve-en.html">Monster Carves</a>
</li>
<li class="pure-menu-heading poogie-menu-heading">Old Sites (searchable but slow load)</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="site-jqueryui.html">Version 2 (home menu navigation)</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="site-jqueryui-tabbed.html">Version 1 (tabbed navigation)</a>
</li>
<li class="pure-menu-heading poogie-menu-heading">Acknowledgments / Sources</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="http://monsterhunter.wikia.com/wiki/MHX"
>Monster Hunter X Wiki (Wikia)</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="https://github.com/kamegami13/MonsterHunter4UDatabase">MonsterHunter4UDatabase</a>
</li>
<li class="pure-menu-item poogie-menu-item">
<a class="pure-menu-link"
href="http://creativecommons.org/licenses/by-sa/3.0/">License CC-BY-SA</a>
</li>
</ul>
</div>
</div>

@ -0,0 +1,312 @@
## -*- coding: utf-8 -*-
<!DOCTYPE html>
<html>
<head>
<title>Poogie's Calculator: ${monster} v${village_stars} g${guild_stars}</title>
<meta charset="utf-8" />
<!-- Include meta tag to ensure proper rendering and touch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/js/jquery.min.js"></script>
<link rel="stylesheet" href="/css/jquery-ui.min.css" />
<script src="/js/jquery-ui.min.js"></script>
<script src="/js/common.js"></script>
<link rel="stylesheet" href="/css/poogie.css">
<style>
body {
font-size: 1em;
line-height: 1.3;
font-family: sans-serif;
}
img.icon { width: 20px; height: 20px; }
.sharpness-bar {
border: 1px #a9a9a9 solid;
min-width: 82px;
height: 10px;
background-color: #a9a9a9;
float: left;
clear: both;
}
.sharpness-bar span {
display: inline-block;
height: 100%;
float: left;
}
.sharpness-bar .red {
background-color: #C00C38 !important;
}
.sharpness-bar .orange {
background-color: #E85018 !important;
}
.sharpness-bar .yellow {
background-color: #F0C830 !important;
}
.sharpness-bar .green {
background-color: #58D000 !important;
}
.sharpness-bar .blue {
background-color: #3068E8 !important;
}
.sharpness-bar .white {
background-color: #F0F0F0 !important;
}
.sharpness-bar .purple {
background-color: #c3c !important;
}
#sharpness_popup {
position: absolute;
display: none;
border: 1px solid;
background: rgba(204, 204, 204, 0.9);
z-index: 10;
}
.sharpness-box {
float: left;
width: 10px;
height: 10px;
margin: 1px;
border: 1px solid rgba(0, 0, 0, .2);
}
.Red {
background-color: #C00C38 !important;
}
.Orange {
background-color: #E85018 !important;
}
.Yellow {
background-color: #F0C830 !important;
}
.Green {
background-color: #58D000 !important;
}
.Blue {
background-color: #3068E8 !important;
}
.White {
background-color: #F0F0F0 !important;
}
.Purple {
background-color: #c3c !important;
}
</style>
<script type="text/javascript">
function load_monster_damage(form) {
var url = "/mh4u/damage/" + $("#monster").val()
+ "/v" + $("#village_stars").val()
+ "g" + $("#guild_stars").val()
+ "/" + $("#weapon_type").val() + ".html";
window.location.href = url;
}
$(document).ready(function(){
setup_monster_autocomplete("mh4u", "#monster");
$( "#village_stars" ).spinner({
spin: function(event, ui) {
if (${monster_stars["Village"]} == 0) {
if (ui.value != 0) {
return false;
}
} else if (ui.value < ${monster_stars["Village"]}) {
return false;
} else if (ui.value > 10) {
return false;
}
}
});
$( "#guild_stars" ).spinner({
spin: function(event, ui) {
if (${monster_stars["Guild"]} == 0) {
if (ui.value != 0) {
return false;
}
} else if (ui.value < ${monster_stars["Guild"]}) {
return false;
} else if (ui.value > 10) {
return false;
}
}
});
});
</script>
</head>
<body>
<form action="javascript:void(0);" onsubmit="load_monster_damage(this)">
<span>
<input id="monster" name="monster" type="text" size="20"
value="${monster}" /></td>
<select id="weapon_type" name="weapon_type">
% for wname in weapon_types:
% if wname == weapon_type:
<option value="${wname}" selected="selected">${wname}</option>
% else:
<option value="${wname}">${wname}</option>
% endif
% endfor
</select>
</span>
<span>
<label for="village_stars">Village:</label>
<input id="village_stars" name="village_stars" value="${village_stars}"
style="width: 18px">
<label for="guild_stars">Guild:</label>
<input id="guild_stars" name="guild_stars" value="${guild_stars}"
style="width: 18px">
<input type="submit" value="Go" />
</span>
</form>
<p>Breaks: ${", ".join(monster_breaks)}</p>
% if monster_damage.alt_state != "Default":
<p>Alternate state: ${monster_damage.alt_state}</p>
% endif
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<th colspan="6">Weapon</th>
<th>Avg</th>
% for part in part_names:
% if part in monster_breaks:
<th style="background: pink;">${part}</th>
% else:
<th>${part}</th>
% endif
% endfor
</tr>
% for dtype in damage_types:
<% max_damage = monster_damage.max(dtype) %>
<% avg_damage = monster_damage.avg(dtype) %>
<% alt_avg_damage = monster_damage.alt_avg(dtype) %>
<tr style="border: 0px;">
<td colspan="6" align="left">
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
${dtype}
</td>
<td align="right">
% if avg_damage > 0 or alt_avg_damage > 0:
${round(avg_damage, 1)}
% if alt_avg_damage != avg_damage:
(${round(alt_avg_damage, 1)})
% endif
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
% endif
</td>
% for part_name, part in monster_damage.items():
<% bgcolor = "lightyellow" if (part[dtype] > 0 and part[dtype] == max_damage) else "white" %>
% if part.state_diff(dtype) != 0:
<td align="right" style="background: ${bgcolor}">
% if part[dtype] > 0 or part.get_alt_state(dtype) > 0:
${part[dtype]} (${part.get_alt_state(dtype)})
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
% endif
</td>
% else:
<td align="right" style="background: ${bgcolor}">
% if part[dtype] > 0:
${part[dtype]}
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
%endif
</td>
% endif
% endfor
</tr>
% endfor
% for weapon in weapon_names:
<% damage = weapon_damage_map[weapon] %>
<% affinity = str(int(damage.affinity)) + "%" if damage.affinity else "&nbsp;" %>
<% avg = damage.uniform(0.0) %>
<% avg_break = damage.uniform(1.0) %>
<tr style="border: 0px;">
<td align="left">
<a href="/mh4u/weaponplanner.html?weapon=${weapon | u}">${weapon}</a>
</td>
<td align="right">${int(damage.attack)}</td>
<td align="right">${affinity}</td>
<td align="center" title="${damage.weapon.sharpness}">
<div class="sharpness-box ${damage.sharpness_name}" />
</td>
% if damage.eattack > 0:
<td align="right">
${int(damage.eattack)}
<img style="height:.8em"
title="${damage.etype}"
src="/img/${damage.etype}.png" />
</td>
% else:
<td>&nbsp;</td>
% endif
% if damage.eattack2 > 0:
<td align="right">
${int(damage.eattack2)}
<img style="height:.8em"
title="${damage.etype2}"
src="/img/${damage.etype2}.png" />
</td>
% else:
<td>&nbsp;</td>
% endif
<td align="right">
${round(avg, 1)}
% if avg != avg_break:
(${round(avg_break, 1)})
% endif
</td>
% for part in part_names:
<% bgcolor = "yellow" if damage[part].average() == part_max_damage[part] else "white" %>
<td align="right" style="background: ${bgcolor};">
${int(damage[part].total)}
% if damage[part].total_break != damage[part].total:
(${int(damage[part].total_break)})
% endif
</td>
% endfor
</tr>
% endfor
</table>
<div id="sharpness_popup"></div>
</body>

@ -0,0 +1,279 @@
## -*- coding: utf-8 -*-
<!DOCTYPE html>
<html>
<head>
<title>Poogie's Calculator: ${monster} r${rarity}</title>
<meta charset="utf-8" />
<!-- Include meta tag to ensure proper rendering and touch zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="/js/jquery.min.js"></script>
<link rel="stylesheet" href="/css/jquery-ui.min.css" />
<script src="/js/jquery-ui.min.js"></script>
<script src="/js/common.js"></script>
<link rel="stylesheet" href="/css/poogie.css">
<style>
body {
font-size: 1em;
line-height: 1.3;
font-family: sans-serif;
}
img.icon { width: 20px; height: 20px; }
.sharpness-bar {
border: 1px #a9a9a9 solid;
min-width: 82px;
height: 10px;
background-color: #a9a9a9;
float: left;
clear: both;
}
.sharpness-bar span {
display: inline-block;
height: 100%;
float: left;
}
.sharpness-bar .red {
background-color: #C00C38 !important;
}
.sharpness-bar .orange {
background-color: #E85018 !important;
}
.sharpness-bar .yellow {
background-color: #F0C830 !important;
}
.sharpness-bar .green {
background-color: #58D000 !important;
}
.sharpness-bar .blue {
background-color: #3068E8 !important;
}
.sharpness-bar .white {
background-color: #F0F0F0 !important;
}
.sharpness-bar .purple {
background-color: #c3c !important;
}
#sharpness_popup {
position: absolute;
display: none;
border: 1px solid;
background: rgba(204, 204, 204, 0.9);
z-index: 10;
}
.sharpness-box {
float: left;
width: 10px;
height: 10px;
margin: 1px;
border: 1px solid rgba(0, 0, 0, .2);
}
.Red {
background-color: #C00C38 !important;
}
.Orange {
background-color: #E85018 !important;
}
.Yellow {
background-color: #F0C830 !important;
}
.Green {
background-color: #58D000 !important;
}
.Blue {
background-color: #3068E8 !important;
}
.White {
background-color: #F0F0F0 !important;
}
.Purple {
background-color: #c3c !important;
}
</style>
<script type="text/javascript">
function load_monster_damage(form) {
var url = "/mhr/damage/" + $("#monster").val()
+ "/r" + $("#rarity").val()
+ "/" + $("#weapon_type").val() + ".html";
window.location.href = url;
}
$(document).ready(function(){
setup_monster_autocomplete("mhr", "#monster");
$( "#rarity" ).spinner({
spin: function(event, ui) {
if (ui.value > 10) {
return false;
}
}
});
});
</script>
</head>
<body>
<form action="javascript:void(0);" onsubmit="load_monster_damage(this)">
<span>
<input id="monster" name="monster" type="text" size="20"
value="${monster}" /></td>
<select id="weapon_type" name="weapon_type">
% for wname in weapon_types:
% if wname == weapon_type:
<option value="${wname}" selected="selected">${wname}</option>
% else:
<option value="${wname}">${wname}</option>
% endif
% endfor
</select>
</span>
<span>
<label for="rarity">Rarity:</label>
<input id="rarity" name="rarity" value="${rarity}"
style="width: 18px">
<input type="submit" value="Go" />
</span>
</form>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<th colspan="6">Weapon</th>
<th>Avg</th>
% for part in part_names:
<th>${part}</th>
% endfor
</tr>
% for dtype in damage_types:
<% max_damage = monster_damage.max(dtype) %>
<% avg_damage = monster_damage.avg(dtype) %>
<% alt_avg_damage = monster_damage.alt_avg(dtype) %>
<tr style="border: 0px;">
<td colspan="6" align="left">
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
${dtype}
</td>
<td align="right">
% if avg_damage > 0 or alt_avg_damage > 0:
${round(avg_damage, 1)}
% if alt_avg_damage != avg_damage:
(${round(alt_avg_damage, 1)})
% endif
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
% endif
</td>
% for part_name, part in monster_damage.items():
<% bgcolor = "lightyellow" if (part[dtype] > 0 and part[dtype] == max_damage) else "white" %>
% if part.state_diff(dtype) != 0:
<td align="right" style="background: ${bgcolor}">
% if part[dtype] > 0 or part.get_alt_state(dtype) > 0:
${part[dtype]} (${part.get_alt_state(dtype)})
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
% endif
</td>
% else:
<td align="right" style="background: ${bgcolor}">
% if part[dtype] > 0:
${part[dtype]}
<img style="height:.8em"
title="dtype"
src="/img/${dtype.capitalize()}.png" />
% else:
&nbsp;
%endif
</td>
% endif
% endfor
</tr>
% endfor
% for weapon in weapon_names:
<% damage = weapon_damage_map[weapon] %>
<% affinity = str(int(damage.affinity)) + "%" if damage.affinity else "&nbsp;" %>
<% avg = damage.uniform(0.0) %>
<% avg_break = damage.uniform(1.0) %>
<tr style="border: 0px;">
<td align="left">
<a href="/mhr/weaponplanner.html?weapon=${weapon | u}">${weapon}</a>
</td>
<td align="right">${int(damage.attack)}</td>
<td align="right">${affinity}</td>
<td align="center" title="${damage.weapon.sharpness}">
<div class="sharpness-box ${damage.sharpness_name}" />
</td>
% if damage.eattack > 0:
<td align="right">
${int(damage.eattack)}
<img style="height:.8em"
title="${damage.etype}"
src="/img/${damage.etype}.png" />
</td>
% else:
<td>&nbsp;</td>
% endif
% if damage.eattack2 > 0:
<td align="right">
${int(damage.eattack2)}
<img style="height:.8em"
title="${damage.etype2}"
src="/img/${damage.etype2}.png" />
</td>
% else:
<td>&nbsp;</td>
% endif
<td align="right">
${round(avg, 1)}
% if avg != avg_break:
(${round(avg_break, 1)})
% endif
</td>
% for part in part_names:
<% bgcolor = "yellow" if damage[part].average() == part_max_damage[part] else "white" %>
<td align="right" style="background: ${bgcolor};">
${int(damage[part].total)}
% if damage[part].total_break != damage[part].total:
(${int(damage[part].total_break)})
% endif
</td>
% endfor
</tr>
% endfor
</table>
<div id="sharpness_popup"></div>
</body>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@ -135,7 +135,7 @@ function normalize_name(s) {
function setup_item_autocomplete(selector) {
var DATA_PATH = get_base_path() + "/rewards/";
var DATA_PATH = get_base_path() + "/mh4u/rewards/";
$.getJSON(DATA_PATH + "items.json",
function(data) {
$(selector).autocomplete({ source: data });
@ -143,6 +143,16 @@ function setup_item_autocomplete(selector) {
}
function setup_monster_autocomplete(game, selector) {
var DATA_PATH = "/jsonapi/" + game + "/";
$.getJSON(DATA_PATH + "monster/_list.json",
function(data) {
var boss = data.filter(a => (a["class"] == "Boss" || a["class"] == "Large"));
var boss_names = boss.map(a => a["name"]);
$(selector).autocomplete({ source: boss_names });
});
}
function load_weapon_data(ready_fn) {
if (typeof DATA_PATH == "undefined") {
DATA_PATH = get_base_path() + "/jsonapi/";

@ -0,0 +1,345 @@
<html>
<head>
<title>Poogie's Weapon List (Rise)</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript" src="../js/ejs_production.js"></script>
<script type="text/javascript" src="../js/common.js"></script>
<style>
label {
font-weight: bold;
}
body {
font-family: sans, sans-serif;
}
td.plus {
background-color: LightCyan;
}
td.minus {
background-color: LightPink;
}
td.num {
text-align: right;
}
/*@media (max-width: 600) {*/
.flexbox {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.sharpness-bar {
border: 1px #a9a9a9 solid;
min-width: 90px;
height: 7px;
background-color: #a9a9a9;
float: left;
clear: both;
}
.sharpness-bar span {
display: inline-block;
height: 100%;
float: left;
}
.sharpness-bar .red {
background-color: #C00C38 !important;
}
.sharpness-bar .orange {
background-color: #E85018 !important;
}
.sharpness-bar .yellow {
background-color: #F0C830 !important;
}
.sharpness-bar .green {
background-color: #58D000 !important;
}
.sharpness-bar .blue {
background-color: #3068E8 !important;
}
.sharpness-bar .white {
background-color: #F0F0F0 !important;
}
.sharpness-bar .purple {
background-color: #c3c !important;
}
#sharpness_popup {
position: absolute;
display: none;
border: 1px solid;
background: rgba(204, 204, 204, 0.9);
z-index: 10;
}
#cp_div {
display: none;
}
</style>
<script type="text/javascript">
var WEAPON_LIST = null;
$.ajax({
url: "../jsonapi/mhr/weapon/_all.json",
async: false,
dataType: "json",
success: function (data) {
WEAPON_LIST = data;
console.log("weapon count " + WEAPON_LIST.length);
}
});
var template_row = new EJS({ url: "/templates/weaponrow-rise.ejs" });
$(document).ready(function(){
init_page();
$("#sharpness_popup").on("click", function(evt) {
$(this).html("").offset({top:0, left:0}).hide();
});
$("#weapon_table").on("click", "#sharpness_td", function(evt) {
var td_obj = $(evt.currentTarget);
var offset = td_obj.offset();
var sharpness = td_obj.data("sharpness");
$("#sharpness_popup").html(sharpness).offset(offset).show();
});
});
function init_page() {
var qs = load_qs();
$(window).on("popstate", function(e) {
var oe = e.originalEvent;
if (oe.state !== null) {
console.log("popState:" + JSON.stringify(oe.state));
update_weapon_list(oe.state);
}
});
$("#search").click(function(evt) {
var state = get_ui_state();
save_state(state);
update_weapon_list(state);
});
if (qs) {
update_weapon_list(qs);
}
}
function load_qs() {
if ($.QueryString["weapon_type"]) {
load_state($.QueryString);
return $.QueryString;
}
return null;
}
function get_ui_state() {
return { "weapon_type": $("#weapon_type").val(),
"weapon_element": $("#weapon_element").val(),
"weapon_final": $("#weapon_final").is(":checked"),
"weapon_name_text": $("#weapon_name_text").val(),
"weapon_rarity": $("#weapon_rarity").val() };
}
function load_state(state) {
$("#weapon_type").val(state["weapon_type"]);
$("#weapon_element").val(state["weapon_element"]);
if (typeof final == "string") {
final = final.toLowerCase();
state["weapon_final"] = (final == "true" || final == "1");
}
$("#weapon_final").prop("checked", state["weapon_final"]);
$("#weapon_name_text").val(state["weapon_name_text"]);
$("#weapon_rarity").val(state["weapon_rarity"]);
}
function save_state(state, replace) {
var url = "weaponlist.html?" + encode_qs(state);
if (replace) {
window.history.replaceState(state, "", url);
} else {
window.history.pushState(state, "", url);
}
}
function weapon_predicate(state, weapon_data) {
var weapon_type = state["weapon_type"];
var weapon_element = state["weapon_element"];
var final_only = state["weapon_final"];
var rarity = state["weapon_rarity"];
var weapon_names = state["weapon_name_text"].split("|");
if (final_only && !Boolean(weapon_data["final"])) {
return false;
}
if (rarity != "Any" && weapon_data["rarity"] > rarity) {
return false;
}
if (weapon_type != "All" && weapon_type != weapon_data["wtype"]) {
return false;
}
if (weapon_element != "All"
&& weapon_element != weapon_data["element"]
&& weapon_element != weapon_data["element_2"]
&& weapon_element != weapon_data["awaken"]
&& weapon_element != weapon_data["phial"]) {
if (weapon_element != "None"
|| weapon_data["element"] != null
|| weapon_data["awaken"] != null) {
return false;
}
}
if (weapon_names && !list_match(weapon_names, [weapon_data["name"]])) {
return false;
}
return true;
}
function list_match(needles, string_list) {
var found = false;
for (var i=0; i<string_list.length; i++) {
for (var j=0; j<needles.length; j++) {
if (string_list[i].search(needles[j]) >= 0) {
found = true;
break;
}
if (found) {
break;
}
}
}
return found;
}
function update_weapon_list(state) {
var match_count = 0;
console.log("updating weapon list: " + JSON.stringify(state));
var results = [];
$.each(WEAPON_LIST, function(i, weapon_data) {
if (weapon_predicate(state, weapon_data)) {
weapon_data["id"] = i;
weapon_data["sharpness_width"] = 0.4;
match_count += 1;
set_sharpness_titles(weapon_data);
set_bow_values(weapon_data);
weapon_data["wtype_short"] =
WEAPON_TYPE_ABBR[weapon_data["wtype"]];
weapon_data["ELEMENT_ABBR"] = ELEMENT_ABBR;
var html = template_row.render(weapon_data);
results.push([weapon_data, html]);
}
});
results.sort(function (a, b) {
avals = get_weapon_sort_values(a[0]);
bvals = get_weapon_sort_values(b[0]);
return cmp_arrays(bvals, avals);
});
$("#weapon_table").empty();
$.each(results, function(i, pair) {
$("#weapon_table").append(pair[1]);
});
console.log("match count: " + match_count);
}
</script>
</head>
<body>
<div>
<table>
<tr>
<td><label for="weapon_type"
title="Only show weapons of this type"
>Type:</label></td>
<td><select id="weapon_type">
<option value="All">All</option>
<option value="Great Sword">Great Sword</option>
<option value="Long Sword">Long Sword</option>
<option value="Sword and Shield">Sword and Shield</option>
<option value="Dual Blades">Dual Blades</option>
<option value="Hammer">Hammer</option>
<option value="Hunting Horn">Hunting Horn</option>
<option value="Lance">Lance</option>
<option value="Gunlance">Gunlance</option>
<option value="Switch Axe">Switch Axe</option>
<option value="Charge Blade">Charge Blade</option>
<option value="Insect Glaive">Insect Glaive</option>
<!--option value="Light Bowgun">Light Bowgun</option-->
<!--option value="Heavy Bowgun">Heavy Bowgun</option-->
<option value="Bow">Bow</option>
</select></td>
<td><label for="weapon_element"
title="Only show weapons with this element (native or requiring awaken)"
>Element:</label></td>
<td><select id="weapon_element">
<option value="All">All</option>
<option value="None">None</option>
<option value="Fire">Fire</option>
<option value="Water">Water</option>
<option value="Thunder">Thunder</option>
<option value="Ice">Ice</option>
<option value="Dragon">Dragon</option>
<option value="Poison">Poison</option>
<option value="Paralysis">Paralysis</option>
<option value="Sleep">Sleep</option>
<option value="Blast">Blast</option>
</select></td>
<td><label>Rarity:</label>
<select id="weapon_rarity">
<option value="Any">*</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
</select></td>
<td><label for="weapon_final"
title="Only show weapons with no furthur upgrades"
>Final?</label></td>
<td><input id="weapon_final" type="checkbox" /></td>
<td><button id="search">Search</button></td>
</tr>
<tr>
<td colspan="8">
<label for="weapon_name_text"
title="Show only weapons with a match in the name. List of strings separated by '|' (vertical bar)."
>Name:</label>
<input id="weapon_name_text" size="15" />
</td>
</tr>
</table>
</div>
<table id="weapon_table">
</table>
<div id="sharpness_popup"></div>
</body>

@ -0,0 +1,284 @@
<html>
<head>
<title>Poogie's Weapon Planner (Rise)</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
<script type="text/javascript" src="/js/ejs_production.js"></script>
<script type="text/javascript" src="/js/common.js"></script>
<style>
label {
font-weight: bold;
}
body {
font-family: sans, sans-serif;
}
td.plus {
background-color: LightCyan;
}
td.minus {
background-color: LightPink;
}
td.num {
text-align: right;
}
/*@media (max-width: 600) {*/
.flexbox {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.sharpness-bar {
border: 1px #d3d3d3 solid;
min-width: 92px;
height: 10px;
background-color: #d3d3d3;
float: left;
clear: both;
}
.sharpness-bar span {
display: inline-block;
height: 100%;
float: left;
}
.sharpness-bar .red {
background-color: #C00C38 !important;
}
.sharpness-bar .orange {
background-color: #E85018 !important;
}
.sharpness-bar .yellow {
background-color: #F0C830 !important;
}
.sharpness-bar .green {
background-color: #58D000 !important;
}
.sharpness-bar .blue {
background-color: #3068E8 !important;
}
.sharpness-bar .white {
background-color: #F0F0F0 !important;
}
.sharpness-bar .purple {
background-color: #c3c !important;
}
</style>
<script type="text/javascript">
var DATA_PATH = "/jsonapi/mhr/";
var template_path = new EJS({ url: "/templates/weaponpath.ejs" });
var template_stats = new EJS({ url: "/templates/weaponstats.ejs" });
$(document).ready(function(){
setup_weapon_autocomplete("#weapon", autocomplete_predicate,
init_page, update_search);
});
function init_page() {
load_qs();
$("#search").click(update_search);
$(window).on("popstate", function(e) {
var oe = e.originalEvent;
if (oe.state !== null) {
console.log("popState:" + JSON.stringify(oe.state));
$("#weapon_type").val(oe.state["weapon_type"]);
$("#weapon_type").change();
show_trees(oe.state["weapon"]);
}
});
$("#weapon_type").change(function(evt) {
update_weapon_autocomplete("#weapon", autocomplete_predicate,
update_search);
$("#weapon").val("");
});
}
function load_qs() {
var wtype = $.QueryString["weapon_type"];
var weapon = $.QueryString["weapon"];
if (!wtype) {
wtype = "All";
}
$("#weapon_type").val(wtype);
$("#weapon_type").change();
if (weapon) {
show_trees(weapon);
console.log("replaceState: " + weapon);
save_state(get_state(), true);
}
}
function get_state() {
return { "weapon": $("#weapon").val(),
"weapon_type": $("#weapon_type").val() };
}
function save_state(state, replace) {
var url = "/mhr/weaponplanner.html?" + encode_qs(state);
if (replace) {
window.history.replaceState(state, "", url);
} else {
window.history.pushState(state, "", url);
}
}
function autocomplete_predicate(weapon_data) {
var weapon_type = $("#weapon_type").val();
if (weapon_type != "All" && weapon_type != weapon_data["wtype"]) {
return false;
}
return true;
}
function update_search() {
var weapon_name = $("#weapon").val();
if (!weapon_name) return;
if (window.history.state
&& window.history.state["weapon"] == weapon_name) {
console.log("weapon not changed, skipping update");
return;
}
show_trees(weapon_name);
console.log("pushState: " + weapon_name);
save_state(get_state(), false);
}
function show_trees(weapon_name) {
console.log("show_trees '" + weapon_name + "'");
if (!weapon_name) return;
weapon_id = WEAPON_NAME_IDX[weapon_name][0];
console.log("show_trees(" + weapon_name + "): " + weapon_id);
$("#weapon").val(weapon_name);
$("#results").html("");
$("#weapon_stats").html("");
$.getJSON(DATA_PATH + "weapon/" + weapon_id + ".json",
function(data) {
set_sharpness_titles(data);
//set_horn_melodies_title(data);
if (data["parent_id"]) {
var parent_obj = WEAPON_ID_IDX[data["parent_id"]][0];
data["parent_name"] = parent_obj["name"];
} else {
data["parent_name"] = null;
}
data["sharpness_width"] = 0.4;
data["sharpness_plus2"] = null;
data["village_stars"] = 0
data["guild_stars"] = 0
var html = template_stats.render(data);
$("#weapon_stats").html(html);
});
$.getJSON(DATA_PATH + "weapon/" + weapon_id + "_tree.json",
function(data) {
// first pass: collect all components and sort them
var all_dict = {};
for (i=0; i<data.length; i++) {
var components = Object.keys(data[i]["components"]);
for (j=0; j<components.length; j++) {
all_dict[components[j]] = 0;
}
}
var all_components = Object.keys(all_dict);
all_components.sort();
// second pass: generate the fieldset for each weapon
// path. Note that the template uses all components
// to order the components and make them line up
for (i=0; i<data.length; i++) {
delta = {};
path = data[i];
components = path["components"]
path_string = "";
for (j=0; j<path["path"].length; j++) {
if (j != 0) {
path_string += " -&gt; ";
}
path_string += path["path"][j]["name"];
}
path["path_string"] = path_string.replace(/"/g,
'&quot;');
path["all_components"] = all_components;
path["component_list"] = Object.keys(components);
if (i > 0) {
prev_comps = data[i-1]["components"];
$.each(components, function(name, quantity) {
if (name in prev_comps) {
delta[name] = components[name]
- prev_comps[name];
}
});
}
path["delta"] = delta;
path["component_list"].sort();
path["trade_names"] = [];
for (j=0; j<all_components.length; j++) {
var name = all_components[j];
path["trade_names"][j] = "";
}
var html = template_path.render(path);
$("#results").append(html);
}
});
}
</script>
</head>
<body>
<div>
<table>
<tr>
<td><label for="weapon_type">Type:</label></td>
<td><select id="weapon_type">
<option value="All">All</option>
<option value="Great Sword">Great Sword</option>
<option value="Long Sword">Long Sword</option>
<option value="Sword and Shield">Sword and Shield</option>
<option value="Dual Blades">Dual Blades</option>
<option value="Hammer">Hammer</option>
<option value="Hunting Horn">Hunting Horn</option>
<option value="Lance">Lance</option>
<option value="Gunlance">Gunlance</option>
<option value="Switch Axe">Switch Axe</option>
<option value="Charge Blade">Charge Blade</option>
<option value="Insect Glaive">Insect Glaive</option>
<option value="Light Bowgun">Light Bowgun</option>
<option value="Heavy Bowgun">Heavy Bowgun</option>
<option value="Bow">Bow</option>
</select></td>
</tr>
<tr>
<td><label for="weapon">Weapon:</label></td>
<td><input id="weapon" name="weapon" size="20" />
<button id="search">Ask Poogie</button></td>
</tr>
</table>
</div>
<div id="weapon_stats"></div>
<div id="results" class="flexbox"></div>
</body>

@ -0,0 +1,63 @@
<tr title="id: <%= id %>">
<td><% if (final == 1) { %>
<strong>*</strong>
<% } else { %>
&nbsp;
<% } %>
</td>
<td>
<a href="weaponplanner.html?weapon=<%= encodeURIComponent(name) %>"><%= name %></a>
</td>
<td><%= wtype_short %></td>
<td style="text-align:right"><%= attack %></td>
<td style="text-align:right"><% if (affinity) { %><%= affinity %>%<% } %></td>
<td>
<% if (element) { %>
<img style="height:.8em" title="<%= element %>"
src="/img/<%= element %>.png" /> <%= element_attack %>
<%= ELEMENT_ABBR[element] %>
<% if (element_2) { %>
<img style="height:.8em" title="<%= element_2 %>"
src="/img/<%= element_2 %>.png" /> <%= element_2_attack %>
<%= ELEMENT_ABBR[element_2] %>
<% } %>
<% } %>
</td>
<td id="sharpness_td"
data-sharpness="<%= sharpness_all_title %>"
data-id="<%= id %>">
<% if (sharpness) { %>
<div class="sharpness-bar" title="<%= sharpness_all_title %>">
<span style="width:<%= sharpness[0] * sharpness_width %>px" class="red"></span>
<span style="width:<%= sharpness[1] * sharpness_width %>px" class="orange"></span>
<span style="width:<%= sharpness[2] * sharpness_width %>px" class="yellow"></span>
<span style="width:<%= sharpness[3] * sharpness_width %>px" class="green"></span>
<span style="width:<%= sharpness[4] * sharpness_width %>px" class="blue"></span>
<span style="width:<%= sharpness[5] * sharpness_width %>px" class="white"></span>
<% if (sharpness.length > 6) { %>
<span style="width:<%= sharpness[6] * sharpness_width %>px" class="purple"></span>
<% } %>
</div>
<% } %>
<% if (sharpness_plus) { %>
<div class="sharpness-bar" title="<%= sharpness_all_title %>">
<span style="width:<%= sharpness_plus[0] * sharpness_width %>px" class="red"></span>
<span style="width:<%= sharpness_plus[1] * sharpness_width %>px" class="orange"></span>
<span style="width:<%= sharpness_plus[2] * sharpness_width %>px" class="yellow"></span>
<span style="width:<%= sharpness_plus[3] * sharpness_width %>px" class="green"></span>
<span style="width:<%= sharpness_plus[4] * sharpness_width %>px" class="blue"></span>
<span style="width:<%= sharpness_plus[5] * sharpness_width %>px" class="white"></span>
<% if (sharpness_plus.length > 6) { %>
<span style="width:<%= sharpness_plus[6] * sharpness_width %>px" class="purple"></span>
<% } %>
</div>
<% } %>
</td>
<td style="text-align:right"><%= phial %>
<% if (phial_value) { %><%= phial_value %><% } %>
</td>
<td style="text-align:right"><%= shelling_type %>
<% if (shelling_level) { %><%= shelling_level %><% } %>
</td>
<td style="text-align:right"><%= bug_level %></td>
<td><%= defense ? "+" + defense + " Def" : "" %></td>

@ -81,17 +81,25 @@
title="<%= parent_name %>">(parent)</a>
<% } %>
</td>
<% if (village_stars) { %>
<td>
Village <%= village_stars %>
</td>
<% } %>
<% if (guild_stars) { %>
<td>
Guild <%= guild_stars %>
</td>
<% } %>
<% if (rarity) { %>
<td>
Rarity <%= rarity %>
</td>
<% } %>
</tr>
</table>
<% if (children.length) { %>

Loading…
Cancel
Save