Compare commits

...

5 Commits

Author SHA1 Message Date
Bryce Allen
228c594ca9 4u stars filter, static damage gen, rise updates 2022-07-31 12:12:03 -04:00
Bryce Allen
6b57d498b6 convert to python3 2021-03-12 10:51:35 -05:00
Bryce Allen
e9c710ca99 mhdamage: fixes for 4u quest level match 2021-03-12 10:24:18 -05:00
Bryce Allen
57f7060794 4u: add shop weapons to planner 2021-03-10 18:15:21 -05:00
Bryce Allen
c2d967b7f3 mh4u: fix outfitters for subdir 2021-03-04 17:00:52 -05:00
55 changed files with 162399 additions and 456 deletions

4
.gitignore vendored
View File

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

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# Set PYTHONPATH in lighttpd or other server config. # Set PYTHONPATH in lighttpd or other server config.

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
""" """
Script to generate static rewards files for all items. Script to generate static rewards files for all items.
""" """
import codecs import codecs
import urllib import urllib.request, urllib.parse, urllib.error
import os.path import os.path
import _pathfix import _pathfix
@@ -27,7 +27,7 @@ if __name__ == '__main__':
elif len(sys.argv) == 2: elif len(sys.argv) == 2:
outdir = sys.argv[1] outdir = sys.argv[1]
else: else:
print("Usage: %s [outdir]" % sys.argv[0]) print(("Usage: %s [outdir]" % sys.argv[0]))
sys.exit(os.EX_USAGE) sys.exit(os.EX_USAGE)
err_out = get_utf8_writer(sys.stderr) err_out = get_utf8_writer(sys.stderr)
@@ -46,7 +46,7 @@ if __name__ == '__main__':
# write all names json to /items.json # write all names json to /items.json
items_file = os.path.join(outdir, "items.json") items_file = os.path.join(outdir, "items.json")
print "Writing", items_file print("Writing", items_file)
with open(items_file, "w") as f: with open(items_file, "w") as f:
out = get_utf8_writer(f) out = get_utf8_writer(f)
out.write("[") out.write("[")
@@ -66,7 +66,7 @@ if __name__ == '__main__':
item_id = item.id item_id = item.id
encoded_name = name.encode("utf8") encoded_name = name.encode("utf8")
item_file = os.path.join(outdir, encoded_name + ".txt") item_file = os.path.join(outdir, encoded_name + ".txt")
print "Writing", item_id, item_file print("Writing", item_id, item_file)
with open(item_file, "w") as f: with open(item_file, "w") as f:
out = get_utf8_writer(f) out = get_utf8_writer(f)
item_row = rewards.find_item(db, name, err_out) item_row = rewards.find_item(db, name, err_out)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
import argparse import argparse
@@ -57,7 +57,7 @@ def find_armors(args):
matches = difflib.get_close_matches(skill_name, skill_tree_names, matches = difflib.get_close_matches(skill_name, skill_tree_names,
1, 0.5) 1, 0.5)
if matches: if matches:
print "Fuzzy Match:", matches[0] print("Fuzzy Match:", matches[0])
sid = skill_tree_id_map.get(matches[0]) sid = skill_tree_id_map.get(matches[0])
skill_name = matches[0] skill_name = matches[0]
args.skills[i] = skill_name args.skills[i] = skill_name
@@ -71,8 +71,8 @@ def find_armors(args):
d.set_skills(db.get_item_skills(d.id)) d.set_skills(db.get_item_skills(d.id))
decoration_values = get_decoration_values(sid, ds)[1] decoration_values = get_decoration_values(sid, ds)[1]
decorations[sid] = (ds, decoration_values) decorations[sid] = (ds, decoration_values)
print "%s[%s]:" % (skill_name, sid), ", ".join(d.name for d in ds), \ print("%s[%s]:" % (skill_name, sid), ", ".join(d.name for d in ds), \
decoration_values decoration_values)
htype = "Gunner" if args.gunner else "Blade" htype = "Gunner" if args.gunner else "Blade"
@@ -82,7 +82,7 @@ def find_armors(args):
for a in armors: for a in armors:
skills = db.get_item_skills(a.id) skills = db.get_item_skills(a.id)
if not skills: if not skills:
print "Error getting skills for '%s' (%d)" % (a.name, a.id) print("Error getting skills for '%s' (%d)" % (a.name, a.id))
sys.exit(1) sys.exit(1)
a.set_skills(skills) a.set_skills(skills)
# calculate total using decorations for first skill only. This # calculate total using decorations for first skill only. This
@@ -113,12 +113,12 @@ def find_armors(args):
if args.type and a.slot != args.type: if args.type and a.slot != args.type:
continue continue
total = skill_totals[a.id] total = skill_totals[a.id]
print skill_totals[a.id], a.one_line_u(), print(skill_totals[a.id], a.one_line_u(), end=' ')
if args.resist: if args.resist:
print args.resist.title(), a[args.resist + "_res"] print(args.resist.title(), a[args.resist + "_res"])
else: else:
print print()
print " ", a.one_line_skills_u(args.skills) print(" ", a.one_line_skills_u(args.skills))
def str_lower(x): def str_lower(x):

View File

@@ -1,17 +1,21 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import sys import sys
import argparse import argparse
import shlex import shlex
import copy import copy
import codecs
import os
import os.path
from collections import defaultdict
import _pathfix import _pathfix
from mhapi.db import MHDB, MHDBX 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.model import SharpnessLevel, Weapon, ItemStars
from mhapi import skills from mhapi import skills
from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR from mhapi.util import ELEMENTS, WEAPON_TYPES, WTYPE_ABBR, DAMAGE_TYPES
def weapon_match_tuple(arg): def weapon_match_tuple(arg):
@@ -54,7 +58,7 @@ def _make_db_sharpness_string(level_string):
level_value = SharpnessLevel.__dict__[level_string.upper()] level_value = SharpnessLevel.__dict__[level_string.upper()]
#print "level value", level_value #print "level value", level_value
values = [] values = []
for i in xrange(SharpnessLevel.PURPLE+1): for i in range(SharpnessLevel.PURPLE+1):
if i <= level_value: if i <= level_value:
values.append("1") values.append("1")
else: else:
@@ -65,9 +69,9 @@ def _make_db_sharpness_string(level_string):
def weapon_stats_tuple(arg): def weapon_stats_tuple(arg):
parts = arg.split(",") parts = arg.split(",")
#print "parts %r" % parts #print("parts %r" % parts)
if len(parts) < 4: if len(parts) < 4:
print "not enough parts" print("not enough parts")
raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'") raise ValueError("Bad arg, use 'name,weapon_type,sharpness,raw'")
weapon = {} weapon = {}
weapon["name"] = parts[0] weapon["name"] = parts[0]
@@ -154,14 +158,14 @@ def _add_skill_args(parser):
default=False, default=False,
help="add Awaken (FreeElemnt), default off") help="add Awaken (FreeElemnt), default off")
parser.add_argument("-a", "--attack-up", parser.add_argument("-a", "--attack-up",
type=int, choices=range(0, 5), default=0, type=int, choices=list(range(0, 5)), default=0,
help="1-4 for AuS, M, L, XL") help="1-4 for AuS, M, L, XL")
parser.add_argument("-c", "--critical-eye", parser.add_argument("-c", "--critical-eye",
type=int, choices=range(0, 5), default=0, type=int, choices=list(range(0, 5)), default=0,
help="1-4 for CE+1, +2, +3 and Critical God") help="1-4 for CE+1, +2, +3 and Critical God")
parser.add_argument("-e", "--element-up", parser.add_argument("-e", "--element-up",
type=int, choices=range(0, 5), default=0, type=int, choices=list(range(0, 6)), default=0,
help="1-4 for (element) Atk +1, +2, +3 and" help="1-5 for (element) Atk +1, +2, +3 and"
" Element Attack Up") " Element Attack Up")
parser.add_argument("-t", "--artillery", parser.add_argument("-t", "--artillery",
type=int, choices=[0,1,2], default=0, 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", parser.add_argument("--mhw", "--monster-hunter-world", action="store_true",
default=False, default=False,
help="Adjusted attack, use MHWorld values") 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="*", parser.add_argument("-m", "--match", nargs="*",
help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW" help="WEAPON_TYPE,ELEMENT_OR_STATUS_OR_RAW"
+" Include all matching weapons in their final form." +" Include all matching weapons in their final form."
@@ -216,19 +223,27 @@ def parse_args(argv):
+" Examples: 'Great Sword,Raw'" +" Examples: 'Great Sword,Raw'"
+" 'Sword and Shield,Para'" +" 'Sword and Shield,Para'"
+" 'HH,Blast' 'Hammer'", +" 'HH,Blast' 'Hammer'",
type=weapon_match_tuple, default=[]) type=weapon_match_tuple, default=[],
action="append")
parser.add_argument("-w", "--weapon-custom", nargs="*", parser.add_argument("-w", "--weapon-custom", nargs="*",
help="NAME,WEAPON_TYPE,TRUE_RAW,AFFINITY,SHARPNESS" help="NAME,WEAPON_TYPE,TRUE_RAW,AFFINITY,SHARPNESS"
+"ELEMENT_TYPE,ELEMENT_ATTACK" +"ELEMENT_TYPE,ELEMENT_ATTACK"
+" Add weapon based on stats." +" Add weapon based on stats."
+" Examples: 'DinoSnS,SnS,190,0,Blue,Fire,30'" +" Examples: 'DinoSnS,SnS,190,0,Blue,Fire,30'"
+" 'AkantorHam,Hammer,240,25,Green'", +" 'AkantorHam,Hammer,240,25,Green'",
type=weapon_stats_tuple, default=[]) type=weapon_stats_tuple, default=[],
action="append")
parser.add_argument("-q", "--quest-level", parser.add_argument("-q", "--quest-level",
help="village,guild[,permit[,arena]]", help="village,guild[,permit[,arena]]",
type=quest_level_tuple) type=quest_level_tuple)
parser.add_argument("monster", parser.add_argument("-r", "--rarity",
help="Full name of monster") 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="*", parser.add_argument("weapon", nargs="*",
help="One or more weapons of same class to compare," help="One or more weapons of same class to compare,"
" full names") " full names")
@@ -253,24 +268,117 @@ def print_sorted_phial_damage(names, damage_map_base, weapon_damage_map, parts,
_print_headers(parts, damage_map_base) _print_headers(parts, damage_map_base)
for name in names_sorted: for name in names_sorted:
print "%-20s:" % name, print("%-20s:" % name, end=' ')
damage_map = weapon_damage_map[name] damage_map = weapon_damage_map[name]
print "%0.2f" % avg_phial(damage_map, level=level), print("%0.2f" % avg_phial(damage_map, level=level), end=' ')
for part in parts: for part in parts:
part_damage = damage_map[part] part_damage = damage_map[part]
#print "%0.2f" % sum(damage_map.cb_phial_damage[part][level]), #print "%0.2f" % sum(damage_map.cb_phial_damage[part][level]),
print "%0.2f:%0.2f:%0.2f" % damage_map.cb_phial_damage[part][level], print("%0.2f:%0.2f:%0.2f" % damage_map.cb_phial_damage[part][level], end=' ')
print print()
def _print_headers(parts, damage_map_base): def _print_headers(parts, damage_map_base):
print print()
avg_hitbox = (sum(damage_map_base[part].hitbox for part in parts) avg_hitbox = (sum(damage_map_base[part].hitbox for part in parts)
/ float(len(parts))) / float(len(parts)))
cols = ["%s (%d)" % (part, damage_map_base[part].hitbox) cols = ["%s (%d)" % (part, damage_map_base[part].hitbox)
for part in parts] for part in parts]
cols = ["%s (%d)" % ("Avg", avg_hitbox)] + cols cols = ["%s (%d)" % ("Avg", avg_hitbox)] + cols
print " | ".join(cols) 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 print_sorted_damage(names, damage_map_base, weapon_damage_map, parts):
@@ -288,31 +396,36 @@ def print_sorted_damage(names, damage_map_base, weapon_damage_map, parts):
# for part in parts]) # for part in parts])
for name in names_sorted: for name in names_sorted:
print "%-20s:" % name, print("%-20s:" % name, end=' ')
damage_map = weapon_damage_map[name] damage_map = weapon_damage_map[name]
print "%0.2f" % damage_map.averages["uniform"], print("%0.2f" % damage_map.averages["uniform"], end=' ')
for part in parts: for part in parts:
part_damage = damage_map[part] part_damage = damage_map[part]
print "% 2d" % part_damage.average(), print("% 2d" % part_damage.average(), end=' ')
print print()
if len(names) > 1: # this is super buggy
if False and len(names) > 1:
w1 = weapon_damage_map[names_sorted[0]] w1 = weapon_damage_map[names_sorted[0]]
w2 = weapon_damage_map[names_sorted[1]] w2 = weapon_damage_map[names_sorted[1]]
m, ratio = w1.compare_break_even(w2) m, ratio = w1.compare_break_even(w2)
print print()
print "Comparison of '%s' and '%s'" % ( print("Comparison of '%s' and '%s'" % (
names_sorted[0], names_sorted[1]) names_sorted[0], names_sorted[1]))
print "Hitbox ratio:", m, "%0.2f" % ratio print("Hitbox ratio:", m, "%0.2f" % ratio)
for line in w1.get_raw_element_ratios(): if w1.etype:
re_ratios = w1.get_raw_element_ratios()
else:
re_ratios = w2.get_raw_element_ratios()
for line in re_ratios:
line = list(line) line = list(line)
if m*line[3] > m*ratio: if m*line[3] > m*ratio:
line.append(names_sorted[0]) line.append(names_sorted[0])
else: else:
line.append(names_sorted[1]) line.append(names_sorted[1])
# (part, raw, element, ratio) # (part, raw, element, ratio)
print "%-22s %02d %02d %0.2f %s" % tuple(line) print("%-22s %02d %02d %0.2f %s" % tuple(line))
def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts): def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts):
@@ -336,7 +449,7 @@ def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts):
ediff_s = ",".join("%+0.1f%%" % i for i in ediffs) ediff_s = ",".join("%+0.1f%%" % i for i in ediffs)
bdiff_s = ",".join("%+0.1f%%" % i for i in bdiffs) bdiff_s = ",".join("%+0.1f%%" % i for i in bdiffs)
damage = damage_map_base[part] damage = damage_map_base[part]
print "%22s%s h%02d %0.2f (%s) h%02d %0.2f (%s) %+0.2f (%s)" \ print("%22s%s h%02d %0.2f (%s) h%02d %0.2f (%s) %+0.2f (%s)" \
% (part, "*" if damage.is_breakable() else " ", % (part, "*" if damage.is_breakable() else " ",
damage.hitbox, damage.hitbox,
damage.total, damage.total,
@@ -345,17 +458,17 @@ def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts):
damage.element, damage.element,
ediff_s, ediff_s,
damage.break_diff(), damage.break_diff(),
bdiff_s) bdiff_s))
if weapon_type == "Charge Blade": if weapon_type == "Charge Blade":
for level in (0, 1, 2, 3, 5): for level in (0, 1, 2, 3, 5):
print " " * 20, level, print(" " * 20, level, end=' ')
for wname in names: for wname in names:
wd = weapon_damage_map[wname] wd = weapon_damage_map[wname]
damage = wd.cb_phial_damage[part][level] damage = wd.cb_phial_damage[part][level]
print "(%0.f, %0.f, %0.f);" % damage, print("(%0.f, %0.f, %0.f);" % damage, end=' ')
print print()
print " --------------------" print(" --------------------")
for avg_type in "uniform raw weakpart_raw element weakpart_element break_raw break_element break_only".split(): for avg_type in "uniform raw weakpart_raw element weakpart_element break_raw break_element break_only".split():
base = damage_map_base.averages[avg_type] base = damage_map_base.averages[avg_type]
@@ -367,7 +480,7 @@ def print_damage_percent_diff(names, damage_map_base, weapon_damage_map, parts):
diff_s = ",".join("%+0.1f%%" % i for i in diffs) diff_s = ",".join("%+0.1f%%" % i for i in diffs)
print "%22s %0.2f (%s)" % (avg_type, base, diff_s) print("%22s %0.2f (%s)" % (avg_type, base, diff_s))
def match_quest_level(match_level, weapon_level): def match_quest_level(match_level, weapon_level):
@@ -383,32 +496,19 @@ def match_quest_level(match_level, weapon_level):
return True return True
def main(): def run_comparison(args, db, motiondb, game_uses_true_raw, item_stars=None):
args = parse_args(None)
game_uses_true_raw = False
if args.monster_hunter_cross:
db = MHDBX()
game_uses_true_raw = True
elif args.monster_hunter_gen:
if args.quest_level:
comps = True
else:
comps = False
db = MHDB(game="gu", include_item_components=comps)
game_uses_true_raw = True
elif args.monster_hunter_world:
db = MHDBX(game="mhw")
game_uses_true_raw = False
else:
db = MHDB(game="4u")
motiondb = MotionValueDB(_pathfix.motion_values_path)
monster = db.get_monster_by_name(args.monster) monster = db.get_monster_by_name(args.monster)
if not monster: if not monster:
raise ValueError("Monster '%s' not found" % args.monster) raise ValueError("Monster '%s' not found" % args.monster)
monster_damage = db.get_monster_damage(monster.id) monster_damage = db.get_monster_damage(monster.id)
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 = [] weapons = []
weapon_type = None weapon_type = None
names_set = set() names_set = set()
@@ -425,10 +525,14 @@ def main():
if skill_args: if skill_args:
skill_args_map[name] = skill_args skill_args_map[name] = skill_args
#print("args match", args.match)
for match_tuple in args.match: for match_tuple in args.match:
# TODO: better validation # TODO: better validation
if isinstance(match_tuple, list):
# TODO: is this a bug in argparse!!!????
match_tuple = match_tuple[0]
wtype, element = match_tuple wtype, element = match_tuple
if args.quest_level: if args.quest_level or args.rarity:
final=None final=None
else: else:
final=1 final=1
@@ -443,28 +547,51 @@ def main():
names_set.add(w.name) names_set.add(w.name)
if args.weapon_custom: if args.weapon_custom:
weapons.extend(args.weapon_custom) weapons.extend([w[0] for w in args.weapon_custom])
if not weapons: if not weapons:
print "Err: no matching weapons" print("Err: no matching weapons")
sys.exit(1) sys.exit(1)
names = [w.name for w in weapons] names = [w.name for w in weapons]
monster_breaks = db.get_monster_breaks(monster.id) 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"] weapon_type = weapons[0]["wtype"]
if args.phial and weapon_type != "Charge Blade": if args.phial and weapon_type != "Charge Blade":
print "ERROR: phial option is only supported for Charge Blade" print("ERROR: phial option is only supported for Charge Blade")
sys.exit(1) sys.exit(1)
motion = motiondb[weapon_type].average motion = motiondb[weapon_type].average
print "Weapon Type: %s" % weapon_type print("Weapon Type: %s" % weapon_type)
print "Average Motion: %0.1f" % motion print("Average Motion: %0.1f" % motion)
if args.motion: if args.motion:
motion = args.motion motion = args.motion
print "Specified Motion: %0.1f" % motion print("Specified Motion: %0.1f" % motion)
print "Monster Breaks: %s" % ", ".join(monster_breaks) print("Monster Breaks: %s" % ", ".join(monster_breaks))
skill_names = get_skill_names(args) skill_names = get_skill_names(args)
print "Common Skills:", ", ".join(skill for skill in skill_names if skill) print("Common Skills:", ", ".join(skill for skill in skill_names if skill))
if args.parts: if args.parts:
limit_parts = args.parts.split(",") limit_parts = args.parts.split(",")
@@ -473,24 +600,73 @@ def main():
if args.quest_level: if args.quest_level:
village, guild, permit, arena = args.quest_level village, guild, permit, arena = args.quest_level
print "Filter by Quest Levels:", args.quest_level print("Filter by Quest Levels:", args.quest_level)
weapons2 = dict() weapons2 = dict()
for w in weapons: for w in weapons:
if (not match_quest_level(village, w["village_stars"]) if "village_stars" in w:
and not match_quest_level(guild, w["guild_stars"])): stars = dict(Village=w["village_stars"],
Guild=w["guild_stars"],
Permit=w["permit_stars"],
Arena=w["arena_stars"])
else:
stars = item_stars.get_weapon_stars(w)
if (not match_quest_level(village, stars["Village"])
and not match_quest_level(guild, stars["Guild"])):
continue continue
if not match_quest_level(permit, w["permit_stars"]): if not match_quest_level(permit, stars["Permit"]):
continue continue
if not match_quest_level(arena, w["arena_stars"]): if not match_quest_level(arena, stars["Arena"]):
continue continue
weapons2[w.id] = w weapons2[w.id] = w
parent_ids = set(w.parent_id for w in weapons2.values()) parent_ids = set(w.parent_id for w in weapons2.values())
for wid in weapons2.keys(): for wid in list(weapons2.keys()):
if wid in parent_ids: if wid in parent_ids:
del weapons2[wid] del weapons2[wid]
weapons = weapons2.values() weapons = list(weapons2.values())
names = [w.name for w in weapons] 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() weapon_damage_map = dict()
for row in weapons: for row in weapons:
name = row["name"] name = row["name"]
@@ -499,6 +675,7 @@ def main():
raise ValueError( raise ValueError(
"Weapon '%s' is different type, got '%s' expected '%s'" "Weapon '%s' is different type, got '%s' expected '%s'"
% (name, row_type, weapon_type)) % (name, row_type, weapon_type))
#print(name, row)
try: try:
skill_args = skill_args_map.get(name, args) skill_args = skill_args_map.get(name, args)
wd = WeaponMonsterDamage(row, wd = WeaponMonsterDamage(row,
@@ -513,24 +690,28 @@ def main():
limit_parts=args.parts, limit_parts=args.parts,
frenzy_bonus=skill_args.frenzy, frenzy_bonus=skill_args.frenzy,
is_true_attack=game_uses_true_raw, is_true_attack=game_uses_true_raw,
blunt_power=skill_args.blunt_power) blunt_power=skill_args.blunt_power,
print "%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity), game=db.game)
print("%-20s: %4.0f %2.0f%%" % (name, wd.attack, wd.affinity), end=' ')
if wd.etype: if wd.etype:
if wd.etype2: if wd.etype2:
print "(%4.0f %s, %4.0f %s)" \ print("(%4.0f %s, %4.0f %s)" \
% (wd.eattack, wd.etype, wd.eattack2, wd.etype2), % (wd.eattack, wd.etype, wd.eattack2, wd.etype2), end=' ')
else: else:
print "(%4.0f %s)" % (wd.eattack, wd.etype), print("(%4.0f %s)" % (wd.eattack, wd.etype), end=' ')
print SharpnessLevel.name(wd.sharpness), print(SharpnessLevel.name(wd.sharpness), wd.sharpness_points, end=' ')
if skill_args != args: if skill_args != args:
print "{%s}" % ",".join(sn print("{%s}" % ",".join(sn
for sn in get_skill_names(skill_args) for sn in get_skill_names(skill_args)
if sn) if sn))
else: else:
print 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 weapon_damage_map[name] = wd
except ValueError as e: except ValueError as e:
print str(e) print(str(e))
sys.exit(1) sys.exit(1)
damage_map_base = weapon_damage_map[names[0]] damage_map_base = weapon_damage_map[names[0]]
@@ -551,6 +732,162 @@ def main():
print_sorted_damage(names, damage_map_base, print_sorted_damage(names, damage_map_base,
weapon_damage_map, parts) 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__': if __name__ == '__main__':
main() main()

View File

@@ -1,10 +1,10 @@
#!/usr/bin/env python #!/usr/bin/env python3
""" """
Script to find the most lucrative monster parts to farm for money. Script to find the most lucrative monster parts to farm for money.
""" """
import codecs import codecs
import urllib import urllib.request, urllib.parse, urllib.error
import os.path import os.path
import sys import sys
@@ -55,8 +55,8 @@ def print_top_items(db, rank="G"):
value = item_value(item) value = item_value(item)
if value < min_value: if value < min_value:
break break
print " %-20s % 7.f % 6d (% 5.f)" % \ print(" %-20s % 7.f % 6d (% 5.f)" % \
(item.name, value, int(item.sell), ev[item.id]) (item.name, value, int(item.sell), ev[item.id]))
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
""" """
Calculate probability of getting at least one of a monster part from one Calculate probability of getting at least one of a monster part from one
@@ -71,22 +71,22 @@ if __name__ == '__main__':
max_rewards = 8 - fixed_rewards max_rewards = 8 - fixed_rewards
if min_rewards < 0: if min_rewards < 0:
print "Error: fixed_rewards (%d) must be less than or equal to " \ print("Error: fixed_rewards (%d) must be less than or equal to " \
"guaranteeed_rewards (%d)" % (fixed_rewards, guarenteed_rewards) "guaranteeed_rewards (%d)" % (fixed_rewards, guarenteed_rewards))
sys.exit(1) sys.exit(1)
total_p = 0.0 total_p = 0.0
expected_attempts = 0.0 expected_attempts = 0.0
for reward_count in xrange(min_rewards, max_rewards + 1): for reward_count in range(min_rewards, max_rewards + 1):
p = stats._reward_count_p(reward_count, min_rewards, max_rewards, p = stats._reward_count_p(reward_count, min_rewards, max_rewards,
extend_percent) extend_percent)
expected_attempts += p * reward_count expected_attempts += p * reward_count
# probability of getting @reward_count rewards that could be the # probability of getting @reward_count rewards that could be the
# desired item # desired item
print "P(C = %d) = %0.4f" % (reward_count, p) print("P(C = %d) = %0.4f" % (reward_count, p))
total_p += p total_p += p
# expected value for number of rewards that could be the desired item # expected value for number of rewards that could be the desired item
print "E(C) = %0.2f" % expected_attempts print("E(C) = %0.2f" % expected_attempts)
# math check, make sure all possibilities add up to 1, allowing for # math check, make sure all possibilities add up to 1, allowing for
# some floating point precision loss. # some floating point precision loss.
@@ -96,5 +96,5 @@ if __name__ == '__main__':
max_rewards, extend_percent) max_rewards, extend_percent)
expected = expected_attempts * reward_percent / 100.0 expected = expected_attempts * reward_percent / 100.0
print "P(N > 0) = %0.2f%%" % p_at_least_one print("P(N > 0) = %0.2f%%" % p_at_least_one)
print "E(N) = %0.4f" % expected print("E(N) = %0.4f" % expected)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import _pathfix import _pathfix
@@ -75,7 +75,7 @@ if __name__ == '__main__':
import os.path import os.path
if len(sys.argv) != 2: if len(sys.argv) != 2:
print("Usage: %s 'item name'" % sys.argv[0]) print(("Usage: %s 'item name'" % sys.argv[0]))
sys.exit(os.EX_USAGE) sys.exit(os.EX_USAGE)
item_name = canonical_item_name(sys.argv[1]) item_name = canonical_item_name(sys.argv[1])

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
import argparse import argparse
@@ -40,8 +40,8 @@ if __name__ == '__main__':
else: else:
for cost in costs: for cost in costs:
components = cost["components"] components = cost["components"]
print "=", ", ".join([w.name for w in cost["path"]]) print("=", ", ".join([w.name for w in cost["path"]]))
print " Zenny", cost["zenny"] print(" Zenny", cost["zenny"])
for item_name in sorted(components.iterkeys()): for item_name in sorted(components.keys()):
print "%20s %2d" % (item_name, components[item_name]) print("%20s %2d" % (item_name, components[item_name]))
print print()

View File

@@ -1,15 +1,15 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import os import os
import json import json
import sys import sys
import errno import errno
import urllib import urllib.request, urllib.parse, urllib.error
import argparse import argparse
import _pathfix import _pathfix
from mhapi.db import MHDB from mhapi.db import MHDB, MHDBX
from mhapi import model from mhapi import model
ENTITIES = """item weapon monster armor ENTITIES = """item weapon monster armor
@@ -42,7 +42,7 @@ SAFE_CHARS = " &'+\""
def file_path(path, model_object, alt_name_field=None): def file_path(path, model_object, alt_name_field=None):
if alt_name_field: if alt_name_field:
key = urllib.quote(model_object[alt_name_field].encode("utf8"), key = urllib.parse.quote(model_object[alt_name_field].encode("utf8"),
SAFE_CHARS) SAFE_CHARS)
else: else:
key = str(model_object.id) key = str(model_object.id)
@@ -57,7 +57,7 @@ def write_list_file(path, model_list):
def write_index_file(path, indexes): def write_index_file(path, indexes):
for key, data in indexes.iteritems(): for key, data in indexes.items():
index_path = os.path.join(path, "_index_%s.json" % key) index_path = os.path.join(path, "_index_%s.json" % key)
with open(index_path, "w") as f: with open(index_path, "w") as f:
json.dump(data, f, cls=model.ModelJSONEncoder, indent=2) json.dump(data, f, cls=model.ModelJSONEncoder, indent=2)
@@ -106,7 +106,7 @@ def armor_json(db, path):
a.update_indexes(indexes) a.update_indexes(indexes)
skills = db.get_item_skills(a.id) skills = db.get_item_skills(a.id)
if not skills: if not skills:
print "WARN: armor '%s' (%d) has no skills" % (a.name, a.id) print("WARN: armor '%s' (%d) has no skills" % (a.name, a.id))
a.set_skills(skills) a.set_skills(skills)
all_data.append(a.as_data()) all_data.append(a.as_data())
@@ -130,7 +130,7 @@ def decoration_json(db, path):
a.update_indexes(indexes) a.update_indexes(indexes)
skills = db.get_item_skills(a.id) skills = db.get_item_skills(a.id)
if not skills: if not skills:
print "WARN: decoration '%s' (%d) has no skills" % (a.name, a.id) print("WARN: decoration '%s' (%d) has no skills" % (a.name, a.id))
a.set_skills(skills) a.set_skills(skills)
all_data.append(a.as_data()) all_data.append(a.as_data())
@@ -202,6 +202,7 @@ def weapon_json(db, path):
] ]
data["horn_melodies"] = melodies[w.horn_notes] data["horn_melodies"] = melodies[w.horn_notes]
if db.game == "4u":
stars = item_stars.get_weapon_stars(w) stars = item_stars.get_weapon_stars(w)
data["village_stars"] = stars["Village"] data["village_stars"] = stars["Village"]
data["guild_stars"] = stars["Guild"] data["guild_stars"] = stars["Guild"]
@@ -254,7 +255,7 @@ def wyporium_json(db, path):
if not k.startswith("wyporium"): if not k.startswith("wyporium"):
continue continue
trade_map[item.id][k] = all_data[k] trade_map[item.id][k] = all_data[k]
print trade_map print(trade_map)
mkdirs_p(path) mkdirs_p(path)
write_map_file(path, trade_map) write_map_file(path, trade_map)
@@ -274,21 +275,27 @@ def horn_melody_json(db, path):
def main(): def main():
args = parse_args() args = parse_args()
if args.game in ("mhx", "mhr"):
db = MHDBX(game=args.game)
else:
db = MHDB(game=args.game, include_item_components=True) db = MHDB(game=args.game, include_item_components=True)
if not args.outpath: 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: if args.entities:
for entity in args.entities: for entity in args.entities:
if entity not in ENTITIES: if entity not in ENTITIES:
print "Unknown entity: %s" % entity print("Unknown entity: %s" % entity)
sys.exit(1) sys.exit(1)
else: else:
args.entities = ENTITIES args.entities = ENTITIES
if db.game != "4u": if db.game != "4u":
try:
args.entities.remove("wyporium") args.entities.remove("wyporium")
except:
pass
for entity in args.entities: for entity in args.entities:
fn = globals()["%s_json" % entity] fn = globals()["%s_json" % entity]

View File

@@ -12,11 +12,11 @@ from mhapi.util import get_utf8_writer
def print_header_nav(title, pid): def print_header_nav(title, pid):
print """ print("""
<div data-role="header" data-position="fixed"> <div data-role="header" data-position="fixed">
<a href="#page-menu" class="ui-btn-left ui-btn ui-btn-inline ui-mini ui-corner-all ui-btn-icon-left ui-icon-bars">Menu</a> <a href="#page-menu" class="ui-btn-left ui-btn ui-btn-inline ui-mini ui-corner-all ui-btn-icon-left ui-icon-bars">Menu</a>
<h1>%s</h1> <h1>%s</h1>
""".strip() % title """.strip() % title)
alt_pid = None alt_pid = None
if pid.endswith("-en"): if pid.endswith("-en"):
@@ -27,11 +27,11 @@ def print_header_nav(title, pid):
alt_title = "en" alt_title = "en"
if alt_pid is not None: if alt_pid is not None:
print """ print("""
<a href="#%s" class="ui-btn-right ui-btn ui-btn-inline ui-mini">%s</a> <a href="#%s" class="ui-btn-right ui-btn ui-btn-inline ui-mini">%s</a>
""".strip() % (alt_pid, alt_title) """.strip() % (alt_pid, alt_title))
print " </div>" print(" </div>")
def mk_html_list(dict_list, keys, sort_keys, divider_fn="auto"): def mk_html_list(dict_list, keys, sort_keys, divider_fn="auto"):
@@ -39,7 +39,7 @@ def mk_html_list(dict_list, keys, sort_keys, divider_fn="auto"):
print ('<ul data-role="listview" data-filter="true"' print ('<ul data-role="listview" data-filter="true"'
' data-autodividers="true">') ' data-autodividers="true">')
else: else:
print '<ul data-role="listview" data-filter="true">' print('<ul data-role="listview" data-filter="true">')
if isinstance(sort_keys, types.FunctionType): if isinstance(sort_keys, types.FunctionType):
sort_fn = sort_keys sort_fn = sort_keys
@@ -55,26 +55,26 @@ def mk_html_list(dict_list, keys, sort_keys, divider_fn="auto"):
if divider_fn not in (None, "auto"): if divider_fn not in (None, "auto"):
divider_text = divider_fn(d, prev_d) divider_text = divider_fn(d, prev_d)
if divider_text is not None: if divider_text is not None:
print ' <li data-role="list-divider">%s</li>' % divider_text print(' <li data-role="list-divider">%s</li>' % divider_text)
print " <li>" print(" <li>")
for i, k in enumerate(keys): for i, k in enumerate(keys):
value = d[k] value = d[k]
if k in ("section", "description"): if k in ("section", "description"):
if value: if value:
print ' <p class="ui-li-desc">%s</p>' % value print(' <p class="ui-li-desc">%s</p>' % value)
continue continue
elif k == "title_jp" and i != 0: elif k == "title_jp" and i != 0:
# NB: for monster by title we want it to be a normal column # NB: for monster by title we want it to be a normal column
if value: if value:
print ' <p class="ui-li-desc">Title: %s</p>' % value print(' <p class="ui-li-desc">Title: %s</p>' % value)
continue continue
if value.endswith(".png"): if value.endswith(".png"):
value = ('<img class="icon" src="../img/icons_items/%s" />' value = ('<img class="icon" src="../img/icons_items/%s" />'
% value) % value)
print ' <span class="%s">%s</span>' % (k, value) print(' <span class="%s">%s</span>' % (k, value))
print " </li>" print(" </li>")
prev_d = d prev_d = d
print '</ul>' print('</ul>')
def _main(): def _main():
@@ -87,7 +87,7 @@ def _main():
carve_items = db.get_items(item_types=("Flesh",)) carve_items = db.get_items(item_types=("Flesh",))
print """<!DOCTYPE html> print("""<!DOCTYPE html>
<html> <html>
<head> <head>
<title>Poogie Translate</title> <title>Poogie Translate</title>
@@ -133,26 +133,26 @@ def _main():
</ul> </ul>
</div> </div>
</div> </div>
""" """)
stree_path = os.path.join(_pathfix.project_path, "db", stree_path = os.path.join(_pathfix.project_path, "db",
"mhx_skill_tree_list.json") "mhx_skill_tree_list.json")
with open(stree_path) as f: with open(stree_path) as f:
stree_list = json.load(f) stree_list = json.load(f)
print '<div data-role="page" id="page-skilltrees-en">' print('<div data-role="page" id="page-skilltrees-en">')
print_header_nav("Skill Trees (en)", "page-skilltrees-en") print_header_nav("Skill Trees (en)", "page-skilltrees-en")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(stree_list, ("name", "name_jp"), ("name",)) mk_html_list(stree_list, ("name", "name_jp"), ("name",))
print '</div>' print('</div>')
print '</div>' print('</div>')
print '<div data-role="page" id="page-skilltrees-jp">' print('<div data-role="page" id="page-skilltrees-jp">')
print_header_nav("Skill Trees (jp)", "page-skilltrees-jp") print_header_nav("Skill Trees (jp)", "page-skilltrees-jp")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(stree_list, ("name_jp", "name"), jplen_sort_fn, mk_html_list(stree_list, ("name_jp", "name"), jplen_sort_fn,
divider_fn=jplen_divider_fn) divider_fn=jplen_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
def item_divider_fn(d, prev_d): def item_divider_fn(d, prev_d):
prefix = _icon_prefix(d) prefix = _icon_prefix(d)
@@ -160,29 +160,29 @@ def _main():
if prefix != prev_prefix: if prefix != prev_prefix:
return prefix return prefix
return None return None
print '<div data-role="page" id="page-item-usable">' print('<div data-role="page" id="page-item-usable">')
print_header_nav("Items: Usable", "page-item-usable") print_header_nav("Items: Usable", "page-item-usable")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(items, ("icon_name", "name", "name_jp"), mk_html_list(items, ("icon_name", "name", "name_jp"),
("icon_name", "name"), divider_fn=item_divider_fn) ("icon_name", "name"), divider_fn=item_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
print '<div data-role="page" id="page-item-gather">' print('<div data-role="page" id="page-item-gather">')
print_header_nav("Items: Gatherable", "page-item-gather") print_header_nav("Items: Gatherable", "page-item-gather")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(gather_items, ("icon_name", "name", "name_jp"), mk_html_list(gather_items, ("icon_name", "name", "name_jp"),
("icon_name", "name"), divider_fn=item_divider_fn) ("icon_name", "name"), divider_fn=item_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
print '<div data-role="page" id="page-item-carve">' print('<div data-role="page" id="page-item-carve">')
print_header_nav("Items: Carve", "page-item-carve") print_header_nav("Items: Carve", "page-item-carve")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(carve_items, ("icon_name", "name", "name_jp"), mk_html_list(carve_items, ("icon_name", "name", "name_jp"),
("icon_name", "name"), divider_fn=item_divider_fn) ("icon_name", "name"), divider_fn=item_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
ha_path = os.path.join(_pathfix.project_path, "db", "hunter_arts.json") ha_path = os.path.join(_pathfix.project_path, "db", "hunter_arts.json")
with open(ha_path) as f: with open(ha_path) as f:
@@ -194,21 +194,21 @@ def _main():
elif d["section"] != prev_d["section"]: elif d["section"] != prev_d["section"]:
return d["section"] return d["section"]
return None return None
print '<div data-role="page" id="page-hunterarts-en">' print('<div data-role="page" id="page-hunterarts-en">')
print_header_nav("Hunter Arts (en)", "page-hunterarts-en") print_header_nav("Hunter Arts (en)", "page-hunterarts-en")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(ha_list, ("name", "name_jp", "description"), None, mk_html_list(ha_list, ("name", "name_jp", "description"), None,
divider_fn=ha_divider_fn) divider_fn=ha_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
print '<div data-role="page" id="page-hunterarts-jp">' print('<div data-role="page" id="page-hunterarts-jp">')
print_header_nav("Hunter Arts (jp)", "page-hunterarts-jp") print_header_nav("Hunter Arts (jp)", "page-hunterarts-jp")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(ha_list, ("name_jp", "name", "section", "description"), mk_html_list(ha_list, ("name_jp", "name", "section", "description"),
jplen_sort_fn, divider_fn=jplen_divider_fn) jplen_sort_fn, divider_fn=jplen_divider_fn)
print '</div>' print('</div>')
print '</div>' print('</div>')
monster_path = os.path.join(_pathfix.project_path, "db", monster_path = os.path.join(_pathfix.project_path, "db",
@@ -216,35 +216,35 @@ def _main():
with open(monster_path) as f: with open(monster_path) as f:
monster_list = json.load(f) monster_list = json.load(f)
print '<div data-role="page" id="page-monsters-en">' print('<div data-role="page" id="page-monsters-en">')
print_header_nav("Monsters (en)", "page-monsters-en") print_header_nav("Monsters (en)", "page-monsters-en")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(monster_list, ("name", "name_jp", "title_jp"), ("name",)) mk_html_list(monster_list, ("name", "name_jp", "title_jp"), ("name",))
print '</div>' print('</div>')
print '</div>' print('</div>')
print '<div data-role="page" id="page-monsters-jp">' print('<div data-role="page" id="page-monsters-jp">')
print_header_nav("Monsters (jp)", "page-monsters-jp") print_header_nav("Monsters (jp)", "page-monsters-jp")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(monster_list, ("name_jp", "name", "title_jp"), ("name_jp",)) mk_html_list(monster_list, ("name_jp", "name", "title_jp"), ("name_jp",))
print '</div>' print('</div>')
print '</div>' print('</div>')
titled_monster_list = [m for m in monster_list if m["title_jp"]] titled_monster_list = [m for m in monster_list if m["title_jp"]]
print '<div data-role="page" id="page-monsters-title">' print('<div data-role="page" id="page-monsters-title">')
print_header_nav("Monster Titles", "page-monsters-title") print_header_nav("Monster Titles", "page-monsters-title")
print '<div data-role="main" class="ui-content">' print('<div data-role="main" class="ui-content">')
mk_html_list(titled_monster_list, ("title_jp", "name"), ("title_jp",), mk_html_list(titled_monster_list, ("title_jp", "name"), ("title_jp",),
divider_fn=None) divider_fn=None)
print '</div>' print('</div>')
print '</div>' print('</div>')
print """ print("""
</body> </body>
""" """)
def _icon_prefix(d): def _icon_prefix(d):
if d is None: if d is None:

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys import sys
import json import json

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
""" """
Parse hunter arts name, name_jp, and description from wikia: Parse hunter arts name, name_jp, and description from wikia:
@@ -10,7 +10,7 @@ Returns list of dict, e.g.:
"section": "Heavy Bowgun", "section": "Heavy Bowgun",
"description": "", "description": "",
"name": "Acceleration Shower I", "name": "Acceleration Shower I",
"name_jp": "\u30a2\u30af\u30bb\u30eb\u30b7\u30e3\u30ef\u30fc I" "name_jp": "\\u30a2\\u30af\\u30bb\\u30eb\\u30b7\\u30e3\\u30ef\\u30fc I"
}, },
... ...
] ]
@@ -62,7 +62,7 @@ def parse_wikia_hunter_arts(f):
def _main(): def _main():
with open(sys.argv[1]) as f: with open(sys.argv[1]) as f:
data = parse_wikia_hunter_arts(f) data = parse_wikia_hunter_arts(f)
print json.dumps(data, indent=2) print(json.dumps(data, indent=2))
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
""" """
Parse monster names and jp names for monster hunter X. Parse monster names and jp names for monster hunter X.
@@ -18,6 +18,7 @@ Returns list of dict, e.g.:
import sys import sys
import re import re
import json import json
import lxml.etree
import requests import requests
@@ -34,14 +35,27 @@ MONSTER_RE = re.compile(
'(?:</td>)?<td style="[^"]*background-color:#EBEBEB;[^"]*">\s*' '(?:</td>)?<td style="[^"]*background-color:#EBEBEB;[^"]*">\s*'
'<a href="([^"]*)" [^>]* title="([^"]*)"') '<a href="([^"]*)" [^>]* title="([^"]*)"')
# Old, MHX
"""
MONSTER_LINK_RE = re.compile( MONSTER_LINK_RE = re.compile(
'<a href="(/wiki/[^/"]*)"\s+class="image image-thumbnail link-internal"\s+' '<a href="(/wiki/[^/"]*)"\s+class="image image-thumbnail link-internal"\s+'
'title="([^"]*)"\s+>') 'title="([^"]*)"\s+>')
JAPANESE_NAME_STR = '<h3 class="pi-data-label pi-secondary-font">Japanese:</h3>' JAPANESE_NAME_STR = '<h3 class="pi-data-label pi-secondary-font">Japanese:</h3>'
JAPANESE_NAME_RE = re.compile( JAPANESE_NAME_RE = re.compile(
'<div class="pi-data-value pi-font">(.*)</div>') '<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): def parse_wikia_monsters(f):
section = None section = None
@@ -55,7 +69,7 @@ def parse_wikia_monsters(f):
m = SECTION_RE.match(line) m = SECTION_RE.match(line)
if m: if m:
section = m.group(1) section = m.group(1)
print >>sys.stderr, "section", section print("section", section, file=sys.stderr)
continue continue
if section not in ["Large Monsters", "Small Monsters"]: if section not in ["Large Monsters", "Small Monsters"]:
continue continue
@@ -72,20 +86,17 @@ def parse_wikia_monsters(f):
def get_jp_names(monster_path): def get_jp_names(monster_path):
url = "http://monsterhunter.wikia.com" + monster_path url = "http://monsterhunter.wikia.com" + monster_path
r = requests.get(url) r = requests.get(url)
lines = r.text.split("\n") root = lxml.etree.HTML(r.text)
names = [] names = []
while lines:
line = lines.pop(0).strip() rbs = root.xpath('//h2[@data-source="Japanese Name"]//rb')
if JAPANESE_NAME_STR not in line: names.append(rbs[0].text)
continue
line = lines.pop(0).strip() divs = root.xpath('//div[@data-source="Japanese Title"]//div')
while line == "": if divs:
line = lines.pop(0).strip() names.append(divs[0].text)
m = JAPANESE_NAME_RE.match(line)
assert m, "No match: " + line
names.append(parse_japanese_name(m.group(1)))
if len(names) == 2:
break
return names return names
@@ -108,16 +119,16 @@ def _main():
name = m["name"] name = m["name"]
names = get_jp_names(m["href"]) names = get_jp_names(m["href"])
if len(names) == 0: if len(names) == 0:
print >>sys.stderr, "ERROR: no names for %s" % name print("ERROR: no names for %s" % name, file=sys.stderr)
names = ["", ""] names = ["", ""]
if len(names) == 1: if len(names) == 1:
print >>sys.stderr, "ERROR: no title for %s" % name print("ERROR: no title for %s" % name, file=sys.stderr)
names.append("") names.append("")
m["name_jp"] = names[0] m["name_jp"] = names[0]
m["title_jp"] = names[1] m["title_jp"] = names[1]
if m["title_jp"] in ("None", "N/A", "(?)"): if m["title_jp"] in ("None", "N/A", "(?)"):
m["title_jp"] = "" m["title_jp"] = ""
print json.dumps(monster_list, indent=2) print(json.dumps(monster_list, indent=2))
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf8 -*- # -*- coding: utf8 -*-
""" """
Parse skill tree names and jp names for monster hunter X. Parse skill tree names and jp names for monster hunter X.
@@ -81,7 +81,7 @@ def parse_wikia_skill_trees(f):
def _main(): def _main():
if len(sys.argv) != 4: if len(sys.argv) != 4:
print "Usage: %s infile out_strees.json out_skills.json" % sys.argv[0] print("Usage: %s infile out_strees.json out_skills.json" % sys.argv[0])
sys.exit(1) sys.exit(1)
with open(sys.argv[1]) as f: with open(sys.argv[1]) as f:
strees, skills = parse_wikia_skill_trees(f) strees, skills = parse_wikia_skill_trees(f)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
import _pathfix import _pathfix
@@ -15,4 +15,4 @@ if __name__ == '__main__':
try: try:
httpd.serve_forever() httpd.serve_forever()
except KeyboardInterrupt: except KeyboardInterrupt:
print "^C" print("^C")

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import sys import sys
import argparse import argparse
@@ -21,7 +21,7 @@ def main():
if args.item: if args.item:
item = db.get_item_by_name(args.item) item = db.get_item_by_name(args.item)
if item is None: if item is None:
print "Item '%s' not found" % args.item print("Item '%s' not found" % args.item)
sys.exit(1) sys.exit(1)
if item.type == "Materials": if item.type == "Materials":
stars = item_stars.get_material_stars(item.id) stars = item_stars.get_material_stars(item.id)
@@ -30,15 +30,15 @@ def main():
elif args.weapon: elif args.weapon:
weapon = db.get_weapon_by_name(args.weapon) weapon = db.get_weapon_by_name(args.weapon)
if weapon is None: if weapon is None:
print "Weapon '%s' not found" % args.weapon print("Weapon '%s' not found" % args.weapon)
sys.exit(1) sys.exit(1)
stars = item_stars.get_weapon_stars(weapon) stars = item_stars.get_weapon_stars(weapon)
else: else:
print "Specify -w or -i" print("Specify -w or -i")
sys.exit(1) sys.exit(1)
for k, v in stars.iteritems(): for k, v in stars.items():
print k, v print(k, v)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -12,7 +12,7 @@ from mhapi.db import MHDB
def apply_update(db, row): def apply_update(db, row):
quest = db.get_quest(row["id"]) quest = db.get_quest(row["id"])
if quest.goal == row["goal"]: if quest.goal == row["goal"]:
print "quest", row["id"], row["name"], "already updated, skipping" print("quest", row["id"], row["name"], "already updated, skipping")
return return
cur = db.cursor() cur = db.cursor()
cur.execute("""UPDATE quests SET cur.execute("""UPDATE quests SET
@@ -21,9 +21,9 @@ def apply_update(db, row):
AND name=?""", AND name=?""",
(row["goal"], row["id"], row["name"])) (row["goal"], row["id"], row["name"]))
if cur.rowcount == 1: if cur.rowcount == 1:
print "quest", row["id"], row["name"], "goal updated:", row["goal"] print("quest", row["id"], row["name"], "goal updated:", row["goal"])
else: else:
print "ERROR", "quest", row["id"], row["name"], "update failed" print("ERROR", "quest", row["id"], row["name"], "update failed")
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -123,8 +123,8 @@ def _parse_monster(name):
name = rstrip(name, "'s") name = rstrip(name, "'s")
name = rstrip(name, "'") name = rstrip(name, "'")
name = rstrip(name, u"") name = rstrip(name, "")
name = rstrip(name, u"s") name = rstrip(name, "s")
#print "=>", name #print "=>", name
@@ -202,7 +202,7 @@ def fuzzy_find(name):
def check_hunts(db, quest): def check_hunts(db, quest):
print ">", quest.id, quest.name, print(">", quest.id, quest.name, end=' ')
monsters_match = False monsters_match = False
@@ -240,30 +240,30 @@ def check_hunts(db, quest):
if monsters_match and not errors: if monsters_match and not errors:
# useful for doing grep -v on output # useful for doing grep -v on output
print " *OK*" print(" *OK*")
elif monsters_match: elif monsters_match:
print " *MISSPELLING*" print(" *MISSPELLING*")
print " goal:", quest.goal print(" goal:", quest.goal)
print " sub:", quest.sub_goal print(" sub:", quest.sub_goal)
for err in errors: for err in errors:
print " ", err print(" ", err)
else: else:
print " *MISMATCH*", print(" *MISMATCH*", end=' ')
if errors: if errors:
print " *MISSPELLING*", print(" *MISSPELLING*", end=' ')
print print()
for err in errors: for err in errors:
print " ", err print(" ", err)
print " goal:", quest.goal print(" goal:", quest.goal)
print " sub:", quest.sub_goal print(" sub:", quest.sub_goal)
print " parsed:", goal_expected print(" parsed:", goal_expected)
if sub_expected and not sub_expected < goal_expected: if sub_expected and not sub_expected < goal_expected:
# print if sub monster looks like it's not one of the # print if sub monster looks like it's not one of the
# main monsters. This will false positive when main quest # main monsters. This will false positive when main quest
# is hunt all large monsters. # is hunt all large monsters.
print " sub prsd:", sub_expected print(" sub prsd:", sub_expected)
print " db:", db_expected print(" db:", db_expected)
print " db unstb:", db_expected_unstable print(" db unstb:", db_expected_unstable)
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -12,7 +12,7 @@ stdout = get_utf8_writer(sys.stdout)
def set_weapon_final(db, weapon, value): def set_weapon_final(db, weapon, value):
print >>stdout, "weapon_final", weapon.id, weapon.name, value print("weapon_final", weapon.id, weapon.name, value, file=stdout)
cur = db.cursor() cur = db.cursor()
cur.execute("""UPDATE weapons SET cur.execute("""UPDATE weapons SET
final=? WHERE _id=?""", final=? WHERE _id=?""",

Binary file not shown.

38
db/mh4u/weapon_shop.csv Normal file
View File

@@ -0,0 +1,38 @@
Iron Sword,1500
Buster Sword,2550
Giant Jawblade,5250
Ravager Blade,5250
Iron Katana,1500
Canine Katana,3300
Eager Cleaver,10950
Hunter's Knife,1500
Assassin's Dagger,3300
Chief Kris,4200
Matched Slicers,1500
Chief's Scythes,3300
Dual Hatchets,4200
War Hammer,1500
War Mace,2550
Bone Bludgeon+,4200
Iron Striker+,5250
Metal Bagpipe,1500
Hunter's Horn,4200
Heavy Bagpipe+,5250
Iron Lance,1500
Knight Lance,2550
Rampart,5250
Spiked Javelin,6450
Iron Gunlance,1500
Jaggid Gunlance,4200
Defender's Gunlance,6450
Bone Axe,1950
Wild Axe,4200
Power Gasher,5250
Elite Blad,1950
Bone Staff,3300
Cross Bowgun,2550
Cross Bowgun+,4200
Bone Shooter,2100
Bone Shooter+,5400
Hunter's Bow I,1500
Hunter's Stoutbow I,4200
1 Iron Sword 1500
2 Buster Sword 2550
3 Giant Jawblade 5250
4 Ravager Blade 5250
5 Iron Katana 1500
6 Canine Katana 3300
7 Eager Cleaver 10950
8 Hunter's Knife 1500
9 Assassin's Dagger 3300
10 Chief Kris 4200
11 Matched Slicers 1500
12 Chief's Scythes 3300
13 Dual Hatchets 4200
14 War Hammer 1500
15 War Mace 2550
16 Bone Bludgeon+ 4200
17 Iron Striker+ 5250
18 Metal Bagpipe 1500
19 Hunter's Horn 4200
20 Heavy Bagpipe+ 5250
21 Iron Lance 1500
22 Knight Lance 2550
23 Rampart 5250
24 Spiked Javelin 6450
25 Iron Gunlance 1500
26 Jaggid Gunlance 4200
27 Defender's Gunlance 6450
28 Bone Axe 1950
29 Wild Axe 4200
30 Power Gasher 5250
31 Elite Blad 1950
32 Bone Staff 3300
33 Cross Bowgun 2550
34 Cross Bowgun+ 4200
35 Bone Shooter 2100
36 Bone Shooter+ 5400
37 Hunter's Bow I 1500
38 Hunter's Stoutbow I 4200

4408
db/mhr/monster_hitboxes.json Normal file

File diff suppressed because it is too large Load Diff

254
db/mhr/monster_list.json Normal file
View File

@@ -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"
}
]

154311
db/mhr/weapon_list.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,7 @@ def set_carve_counts(db, monster_carves):
for m in monsters: for m in monsters:
rewards = db.get_monster_rewards(m.id) rewards = db.get_monster_rewards(m.id)
mc = monster_carves.get(m.name) mc = monster_carves.get(m.name)
print "===", m.name print("===", m.name)
for r in rewards: for r in rewards:
condition = r["condition"] condition = r["condition"]
if "Carve" not in condition: if "Carve" not in condition:
@@ -34,13 +34,13 @@ def set_carve_counts(db, monster_carves):
elif condition == "Tail Carve": elif condition == "Tail Carve":
stack_size = 1 stack_size = 1
else: else:
print "WARN: unknown condition %s.%s" \ print("WARN: unknown condition %s.%s" \
% (m.name, condition) % (m.name, condition))
else: else:
assert False, "Unknown monster class: %s" % m["class"] assert False, "Unknown monster class: %s" % m["class"]
if r["stack_size"] == stack_size: if r["stack_size"] == stack_size:
continue continue
print " ", condition, r["stack_size"], "=>", stack_size print(" ", condition, r["stack_size"], "=>", stack_size)
cur = db.cursor() cur = db.cursor()
cur.execute("""UPDATE hunting_rewards cur.execute("""UPDATE hunting_rewards
SET stack_size=? WHERE _id=?""", SET stack_size=? WHERE _id=?""",

View File

@@ -23,7 +23,7 @@ def _add_column(cursor, table, column_spec):
def _set_stars(cursor, item_id, stars): def _set_stars(cursor, item_id, stars):
for k in stars.keys(): for k in list(stars.keys()):
col = k.lower() + "_stars" col = k.lower() + "_stars"
q = "UPDATE items SET %s=? WHERE _id=?" % col q = "UPDATE items SET %s=? WHERE _id=?" % col
cursor.execute(q, (stars[k], item_id)) cursor.execute(q, (stars[k], item_id))
@@ -45,7 +45,7 @@ def main():
items = db.get_items(exclude_types=["", "Armor", "Palico Weapon", items = db.get_items(exclude_types=["", "Armor", "Palico Weapon",
"Decoration"]) "Decoration"])
for item in items: for item in items:
print item.id, item.type, item.name print(item.id, item.type, item.name)
if item.type == "Materials": if item.type == "Materials":
stars = item_stars.get_material_stars(item.id) stars = item_stars.get_material_stars(item.id)
elif item.type == "Weapon": elif item.type == "Weapon":

View File

@@ -21,7 +21,7 @@ def set_quest_ranks(db):
for quest in quests: for quest in quests:
if not quest["name"]: if not quest["name"]:
assert quest["hub"] == "Event" assert quest["hub"] == "Event"
print "WARN: skipping non localized event quest: %d" % quest.id print("WARN: skipping non localized event quest: %d" % quest.id)
continue continue
set_quest_rank(db, quest) set_quest_rank(db, quest)
@@ -35,19 +35,19 @@ def set_quest_rank(db, quest):
rewards = db.get_quest_rewards(quest_id) rewards = db.get_quest_rewards(quest_id)
rank = guess_quest_rank_from_rewards(db, rewards) rank = guess_quest_rank_from_rewards(db, rewards)
if rank is None: if rank is None:
print "WARN: quest '%s' has no flesh rewards, assuming lower rank"\ print("WARN: quest '%s' has no flesh rewards, assuming lower rank"\
% (quest.name.encode("utf8"),) % (quest.name.encode("utf8"),))
rank = rank_stars_guess[0] rank = rank_stars_guess[0]
elif rank not in rank_stars_guess: elif rank not in rank_stars_guess:
print "ERROR: quest '%s' reward guess '%s' not in stars guess '%s'"\ print("ERROR: quest '%s' reward guess '%s' not in stars guess '%s'"\
% (quest.name, rank, rank_stars_guess) % (quest.name, rank, rank_stars_guess))
else: else:
rank = rank_stars_guess rank = rank_stars_guess
assert rank in "LR HR G".split() assert rank in "LR HR G".split()
quest.rank = rank quest.rank = rank
print quest.one_line_u() print(quest.one_line_u())
cur = db.cursor() cur = db.cursor()
cur.execute("UPDATE quests SET rank=? WHERE _id=?", (rank, quest_id)) cur.execute("UPDATE quests SET rank=? WHERE _id=?", (rank, quest_id))

39
db/set_weapon_buy.py Executable file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env python2
import os.path
import codecs
import csv
import _pathfix
from mhapi.db import MHDB
def set_buy(db, item_id, buy):
print("buy", item_id, buy)
cur = db.cursor()
cur.execute("""UPDATE items SET
buy=? WHERE _id=?""",
(buy, item_id))
def set_buy_by_name(db, name, buy):
cur = db.cursor()
cur.execute("""UPDATE items SET
buy=? WHERE name=?""",
(buy, name))
rowid = cur.lastrowid
print("buy", rowid, name, buy)
if __name__ == '__main__':
db = MHDB(game="4u")
delta_file_path = os.path.join(_pathfix.db_path, "mh4u", "weapon_shop.csv")
with open(delta_file_path) as f:
reader = csv.reader(f)
for row in reader:
name = row[0]
value = int(row[1])
set_buy_by_name(db, name, value)
db.commit()
db.close()

View File

@@ -10,7 +10,7 @@ from mhapi.db import MHDB
def set_upgrade_cost(db, item_id, upgrade_cost): def set_upgrade_cost(db, item_id, upgrade_cost):
print "upgrade_cost", item_id, upgrade_cost print("upgrade_cost", item_id, upgrade_cost)
cur = db.cursor() cur = db.cursor()
cur.execute("""UPDATE weapons SET cur.execute("""UPDATE weapons SET
upgrade_cost=? WHERE _id=?""", upgrade_cost=? WHERE _id=?""",
@@ -18,7 +18,7 @@ def set_upgrade_cost(db, item_id, upgrade_cost):
def set_creation_cost(db, item_id, creation_cost): def set_creation_cost(db, item_id, creation_cost):
print "creation_cost", item_id, creation_cost print("creation_cost", item_id, creation_cost)
cur = db.cursor() cur = db.cursor()
cur.execute("""UPDATE weapons SET cur.execute("""UPDATE weapons SET
creation_cost=? WHERE _id=?""", creation_cost=? WHERE _id=?""",

View File

@@ -78,14 +78,14 @@ class WeaponTypeMotionValues(object):
self.motion_values[name] = MotionValue(name, d["type"], d["power"]) self.motion_values[name] = MotionValue(name, d["type"], d["power"])
self.average = (sum(mv.average self.average = (sum(mv.average
for mv in self.motion_values.itervalues()) for mv in self.motion_values.values())
/ len(self)) / len(self))
def __len__(self): def __len__(self):
return len(self.motion_values) return len(self.motion_values)
def keys(self): def keys(self):
return self.motion_values.keys() return list(self.motion_values.keys())
def __getitem__(self, key): def __getitem__(self, key):
return self.motion_values[key] return self.motion_values[key]
@@ -109,7 +109,7 @@ class MotionValueDB(object):
return self.motion_values_map[weapon_type] return self.motion_values_map[weapon_type]
def keys(self): def keys(self):
return self.motion_values_map.keys() return list(self.motion_values_map.keys())
def __len__(self): def __len__(self):
return len(self.motion_values_map) return len(self.motion_values_map)
@@ -158,7 +158,7 @@ class WeaponType(object):
@classmethod @classmethod
def all(cls): def all(cls):
return cls._multiplier.keys() return list(cls._multiplier.keys())
@classmethod @classmethod
def damage_type(cls, weapon_type): def damage_type(cls, weapon_type):
@@ -187,7 +187,8 @@ class WeaponMonsterDamage(object):
critical_eye_skill=skills.CriticalEye.NONE, critical_eye_skill=skills.CriticalEye.NONE,
element_skill=skills.ElementAttackUp.NONE, element_skill=skills.ElementAttackUp.NONE,
awaken=False, artillery_level=0, limit_parts=None, awaken=False, artillery_level=0, limit_parts=None,
frenzy_bonus=0, blunt_power=False, is_true_attack=False): frenzy_bonus=0, blunt_power=False, is_true_attack=False,
game="4u"):
self.weapon = weapon_row self.weapon = weapon_row
self.monster = monster_row self.monster = monster_row
self.monster_damage = monster_damage self.monster_damage = monster_damage
@@ -222,15 +223,23 @@ class WeaponMonsterDamage(object):
else: else:
self.true_raw = (self.weapon["attack"] self.true_raw = (self.weapon["attack"]
/ WeaponType.multiplier(self.weapon_type)) / WeaponType.multiplier(self.weapon_type))
if sharp_plus == 1:
self.sharpness = self.weapon.sharpness_plus.max if sharp_plus:
elif sharp_plus == 2: if game == "mhr":
self.sharpness = self.weapon.sharpness_plus2.max sharp_n = self.weapon.sharpness_plus.get_rise_handicraft(sharp_plus)
self.sharpness, self.sharpness_points = sharp_n.get_max_points()
else: else:
self.sharpness = self.weapon.sharpness.max if sharp_plus == 1:
#print "sharpness=", self.sharpness 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.sharpness_points = self.weapon.sharpness.get_max_points()
self.sharpness_name = SharpnessLevel.name(self.sharpness)
if self.weapon["affinity"]: if self.weapon["affinity"]:
if (isinstance(self.weapon["affinity"], basestring) if (isinstance(self.weapon["affinity"], str)
and "/" in self.weapon["affinity"]): and "/" in self.weapon["affinity"]):
self.chaotic = True self.chaotic = True
# Handle chaotic gore affinity, e.g. -35/10. This means that # Handle chaotic gore affinity, e.g. -35/10. This means that
@@ -358,7 +367,7 @@ class WeaponMonsterDamage(object):
part_damage.part = part part_damage.part = part
if alt is None: if alt is None:
if (self.breakable_parts if (self.breakable_parts
and _break_find(part, self.monster_damage.parts.keys(), and _break_find(part, list(self.monster_damage.parts.keys()),
self.breakable_parts)): self.breakable_parts)):
part_damage.breakable = True part_damage.breakable = True
if hitbox > self.max_raw_part[1]: if hitbox > self.max_raw_part[1]:
@@ -366,15 +375,15 @@ class WeaponMonsterDamage(object):
if ehitbox > self.max_element_part[1]: if ehitbox > self.max_element_part[1]:
self.max_element_part = (part, ehitbox) self.max_element_part = (part, ehitbox)
for part in self.damage_map.keys(): for part in list(self.damage_map.keys()):
if None not in self.damage_map[part].states: if None not in self.damage_map[part].states:
#print "Failed to parse part:", part #print "Failed to parse part:", part
del self.damage_map[part] del self.damage_map[part]
for part, d in self.damage_map.iteritems(): for part, d in self.damage_map.items():
if d.is_breakable(): if d.is_breakable():
self.break_count += 1 self.break_count += 1
self.parts = self.damage_map.keys() self.parts = list(self.damage_map.keys())
self.averages["uniform"] = self.uniform() self.averages["uniform"] = self.uniform()
self.averages["raw"] = self.weighted_raw() self.averages["raw"] = self.weighted_raw()
self.averages["element"] = self.weighted_element() self.averages["element"] = self.weighted_element()
@@ -403,10 +412,10 @@ class WeaponMonsterDamage(object):
artillery_level=self.artillery_level) artillery_level=self.artillery_level)
self.cb_phial_damage[part][level] = damage_tuple self.cb_phial_damage[part][level] = damage_tuple
def uniform(self): def uniform(self, break_weight=0.25):
average = 0.0 average = 0.0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
average += damage.average() average += damage.average(break_weight)
return average / len(self.damage_map) return average / len(self.damage_map)
def weighted_raw(self): def weighted_raw(self):
@@ -417,7 +426,7 @@ class WeaponMonsterDamage(object):
""" """
average = 0.0 average = 0.0
total_hitbox = 0.0 total_hitbox = 0.0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
average += damage.average() * damage.hitbox average += damage.average() * damage.hitbox
total_hitbox += damage.hitbox total_hitbox += damage.hitbox
if total_hitbox == 0: if total_hitbox == 0:
@@ -430,7 +439,7 @@ class WeaponMonsterDamage(object):
""" """
average = 0.0 average = 0.0
total_ehitbox = 0.0 total_ehitbox = 0.0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
average += damage.average() * damage.ehitbox average += damage.average() * damage.ehitbox
total_ehitbox += damage.ehitbox total_ehitbox += damage.ehitbox
if total_ehitbox == 0: if total_ehitbox == 0:
@@ -444,7 +453,7 @@ class WeaponMonsterDamage(object):
else: else:
other_weight = (1 - weak_weight) / (len(self.parts) - 1) other_weight = (1 - weak_weight) / (len(self.parts) - 1)
average = 0 average = 0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
if part == self.max_raw_part[0]: if part == self.max_raw_part[0]:
weight = weak_weight weight = weak_weight
else: else:
@@ -459,7 +468,7 @@ class WeaponMonsterDamage(object):
else: else:
other_weight = (1 - weak_weight) / (len(self.parts) - 1) other_weight = (1 - weak_weight) / (len(self.parts) - 1)
average = 0 average = 0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
if part == self.max_element_part[0]: if part == self.max_element_part[0]:
weight = weak_weight weight = weak_weight
else: else:
@@ -475,7 +484,7 @@ class WeaponMonsterDamage(object):
return 0 return 0
average = 0.0 average = 0.0
count = self.break_count + 1 count = self.break_count + 1
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
if part == self.max_raw_part[0]: if part == self.max_raw_part[0]:
average += damage.average() average += damage.average()
if damage.is_breakable(): if damage.is_breakable():
@@ -494,7 +503,7 @@ class WeaponMonsterDamage(object):
return 0 return 0
average = 0.0 average = 0.0
count = self.break_count + 1 count = self.break_count + 1
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
if part == self.max_element_part[0]: if part == self.max_element_part[0]:
# If weakpart is also a break, assume continue attacking # If weakpart is also a break, assume continue attacking
# even after broken # even after broken
@@ -516,7 +525,7 @@ class WeaponMonsterDamage(object):
if not self.break_count: if not self.break_count:
return 0 return 0
average = 0.0 average = 0.0
for part, damage in self.damage_map.iteritems(): for part, damage in self.damage_map.items():
if damage.is_breakable(): if damage.is_breakable():
# attack until broken, then move to next break # attack until broken, then move to next break
average += damage.total average += damage.total
@@ -553,8 +562,21 @@ class WeaponMonsterDamage(object):
for row in self.monster_damage._rows: for row in self.monster_damage._rows:
part = row["body_part"] part = row["body_part"]
hitbox = int(row[raw_type]) hitbox = int(row[raw_type])
if self.etype:
try:
ehitbox = int(row[str(self.etype.lower())]) ehitbox = int(row[str(self.etype.lower())])
hitboxes.append((part, hitbox, ehitbox, float(hitbox) / ehitbox)) except IndexError:
ehitbox = 0
print("WARN: bad etype {}, row = {}".format(
self.etype, row))
else:
ehitbox = 0
if ehitbox > 0:
ratio = float(hitbox) / ehitbox
else:
ratio = 0
hitboxes.append((part, hitbox, ehitbox, ratio))
return hitboxes return hitboxes
def nohitbox_damage(self, motion=None): def nohitbox_damage(self, motion=None):
@@ -610,6 +632,20 @@ class PartDamage(object):
def ehitbox(self): def ehitbox(self):
return self.states[None].ehitbox 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 @property
def break_raw(self): def break_raw(self):
if "Break Part" in self.states: if "Break Part" in self.states:
@@ -660,7 +696,8 @@ class PartDamage(object):
# If the part has a hitbox with different damage in the break # If the part has a hitbox with different damage in the break
# rows from the db, or if it's explicitly marked as breakable # rows from the db, or if it's explicitly marked as breakable
# (done by checking hunt rewards for breaks). # (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): def average(self, break_weight=0.25, rage_weight=0.5):
if self.break_diff(): if self.break_diff():
@@ -680,7 +717,8 @@ class PartDamage(object):
+ self.total * (1 - rage_weight)) + self.total * (1 - rage_weight))
def set_damage(self, raw, element, hitbox, ehitbox, state=None): 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" state = "Break Part"
self.states[state] = PartDamageState(raw, element, self.states[state] = PartDamageState(raw, element,
hitbox, ehitbox, state) hitbox, ehitbox, state)

View File

@@ -7,6 +7,7 @@ import sqlite3
import json import json
from mhapi import model from mhapi import model
from mhapi.util import WEAPON_TYPES
def field_model(key): def field_model(key):
@@ -257,10 +258,15 @@ class MHDB(object):
return self._query_all("search_item", query, tuple(args), return self._query_all("search_item", query, tuple(args),
no_cache=True, model_cls=model.Item) no_cache=True, model_cls=model.Item)
def get_monsters(self): def get_monsters(self, monster_class=None):
return self._query_all("monsters", """ args = []
SELECT * FROM monsters where = []
""", model_cls=model.Monster) 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): def get_monster_names(self):
""" """
@@ -317,12 +323,17 @@ class MHDB(object):
WHERE quest_id=? WHERE quest_id=?
""", (quest_id,)) """, (quest_id,))
def get_monster_quests(self, monster_id, rank): def get_monster_quests(self, monster_id, rank=None):
return self._query_all("monster_quests", """
SELECT DISTINCT quests.* FROM quests, monster_to_quest query = """SELECT DISTINCT quests.* FROM quests, monster_to_quest
WHERE monster_to_quest.quest_id = quests._id WHERE monster_to_quest.quest_id = quests._id
AND monster_to_quest.monster_id=? AND rank=? AND monster_to_quest.monster_id=?"""
""", (monster_id, rank), model_cls=model.Quest) 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): def get_item_quests(self, item_id):
""" """
@@ -381,6 +392,14 @@ class MHDB(object):
WHERE monster_id=? WHERE monster_id=?
""", (monster_id,), collection_cls=model.MonsterDamage) """, (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): def get_weapons(self):
# Note: weapons only available via JP DLC have no localized # Note: weapons only available via JP DLC have no localized
# name, filter them out. # name, filter them out.
@@ -613,6 +632,13 @@ class MHDB(object):
item_data.set_components(ccomps, ucomps) 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): class MHDBX(object):
""" """
@@ -626,12 +652,14 @@ class MHDBX(object):
""" """
Loads JSON data, keeps in memory. Loads JSON data, keeps in memory.
""" """
self.game = game
module_path = os.path.dirname(__file__) module_path = os.path.dirname(__file__)
self._mhx_db_path = os.path.abspath(os.path.join(module_path, "..", self._mhx_db_path = os.path.abspath(os.path.join(module_path, "..",
"db", game)) "db", game))
self._4udb = MHDB() self._4udb = MHDB(game="gu")
self._weapon_list = [] self._weapon_list = []
self._weapons_by_name = {} self._weapons_by_name = {}
self._weapons_by_id = {}
self._monsters_by_name = {} self._monsters_by_name = {}
self._monster_damage = {} self._monster_damage = {}
@@ -644,10 +672,23 @@ class MHDBX(object):
with open(os.path.join(self._mhx_db_path, "weapon_list.json")) as f: with open(os.path.join(self._mhx_db_path, "weapon_list.json")) as f:
wlist = json.load(f) wlist = json.load(f)
for i, wdata in enumerate(wlist): for i, wdata in enumerate(wlist):
if "_id" not in wdata:
wdata["_id"] = i 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) weapon = model.Weapon(wdata)
self._weapon_list.append(weapon) self._weapon_list.append(weapon)
self._weapons_by_name[weapon.name_jp] = weapon self._weapons_by_name[weapon.name_jp] = weapon
self._weapons_by_id[weapon.id] = weapon
def _load_monsters(self): def _load_monsters(self):
names_path = os.path.join(self._mhx_db_path, names_path = os.path.join(self._mhx_db_path,
@@ -662,20 +703,39 @@ class MHDBX(object):
self._monsters_by_name[d["name"]] = model.Monster(d) self._monsters_by_name[d["name"]] = model.Monster(d)
with open(hitboxes_path) as f: with open(hitboxes_path) as f:
damage_map = json.load(f) damage_map = json.load(f)
for name, damage in damage_map.iteritems(): for name, damage in damage_map.items():
mid = self._monsters_by_name[name].id mid = self._monsters_by_name[name].id
damage_rows = [] damage_rows = []
for part, data in damage.iteritems(): for part, data in damage.items():
if part.startswith("_"): if part.startswith("_"):
continue continue
row = dict((k.lower(), v) for k, v in data.iteritems()) row = dict((k.lower(), v) for k, v in data.items())
row["body_part"] = part row["body_part"] = part
row["ko"] = 100 if part == "Head" else 0 row["ko"] = 100 if part == "Head" else 0
row["_id"] = 0 row["_id"] = 0
row["monster_id"] = mid row["monster_id"] = mid
damage_rows.append(row) damage_rows.append(row)
self._monster_damage[mid] = model.MonsterDamage(damage_rows) 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): def get_weapon_by_name(self, name):
return self._weapons_by_name.get(name) return self._weapons_by_name.get(name)
@@ -705,8 +765,9 @@ class MHDBX(object):
with no element. Otherwise @element is searched for in both with no element. Otherwise @element is searched for in both
awaken and native, and can be a status or an element. 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'
""" """
if final is not None:
final = int(final) final = int(final)
results = [] results = []
for w in self._weapon_list: for w in self._weapon_list:

View File

@@ -1,8 +1,11 @@
import os
import string import string
import json import json
import urllib from typing import NamedTuple
import urllib.request, urllib.parse, urllib.error
import re import re
import difflib import difflib
from collections import namedtuple
from mhapi.util import EnumBase from mhapi.util import EnumBase
@@ -57,7 +60,7 @@ class RowModel(ModelBase):
return key in self._data return key in self._data
def fields(self): def fields(self):
return self._data.keys() return list(self._data.keys())
def as_data(self): def as_data(self):
return self._data return self._data
@@ -69,7 +72,7 @@ class RowModel(ModelBase):
return list_data return list_data
def update_indexes(self, data): def update_indexes(self, data):
for key_field, value_fields in self._indexes.iteritems(): for key_field, value_fields in self._indexes.items():
if key_field not in data: if key_field not in data:
data[key_field] = {} data[key_field] = {}
self.update_index(key_field, value_fields, data[key_field]) self.update_index(key_field, value_fields, data[key_field])
@@ -86,7 +89,7 @@ class RowModel(ModelBase):
def __str__(self): def __str__(self):
if "name" in self._data and self.name is not None: if "name" in self._data and self.name is not None:
name = urllib.quote(self.name, safe=" ") name = urllib.parse.quote(self.name, safe=" ")
else: else:
name = str(self.id) name = str(self.id)
return "%s '%s'" % (self.__class__.__name__, name) return "%s '%s'" % (self.__class__.__name__, name)
@@ -139,7 +142,7 @@ class SharpnessLevel(EnumBase):
WHITE = 5 WHITE = 5
PURPLE = 6 PURPLE = 6
ALL = range(0, PURPLE + 1) ALL = list(range(0, PURPLE + 1))
_names = { _names = {
RED: "Red", RED: "Red",
@@ -162,7 +165,7 @@ class SharpnessLevel(EnumBase):
PURPLE: (1.44, 1.20), PURPLE: (1.44, 1.20),
} }
# for mhx, mhgen, mhxx, and likely mhw # for mhx, mhgen, mhxx
_modifier_mhx = { _modifier_mhx = {
RED: (0.50, 0.25), RED: (0.50, 0.25),
ORANGE: (0.75, 0.50), ORANGE: (0.75, 0.50),
@@ -172,14 +175,48 @@ class SharpnessLevel(EnumBase):
WHITE: (1.32, 1.125), 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 @classmethod
def raw_modifier(cls, sharpness): 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 @classmethod
def element_modifier(cls, sharpness): 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): class WeaponSharpness(ModelBase):
@@ -195,31 +232,53 @@ class WeaponSharpness(ModelBase):
self.value_list = [int(s) for s in db_string_or_list.split(".")] 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 # For MHX, Gen, no purple sharpness, but keep model the same for
# simplicity # simplicity
if len(self.value_list) < SharpnessLevel.PURPLE + 1: while len(self.value_list) < SharpnessLevel.PURPLE + 1:
self.value_list.append(0) self.value_list.append(0)
self._max = None self._max = None
@property @property
def max(self): def max(self):
if self._max is None: if self._max is None:
self._max = SharpnessLevel.RED for i in range(SharpnessLevel.PURPLE, -1, -1):
for i in xrange(SharpnessLevel.PURPLE+1): if self.value_list[i] > 0:
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 break
else: else:
self._max = i alt_values[i] = 0
return self._max minus_points -= val
return WeaponSharpness(alt_values)
def as_data(self): def as_data(self):
return self.value_list return self.value_list
def __str__(self):
return ",".join(str(v) for v in self.value_list)
class ItemCraftable(RowModel): class ItemCraftable(RowModel):
_list_fields = ["id", "name"] _list_fields = ["id", "name"]
def __init__(self, item_row): def __init__(self, item_row):
super(ItemCraftable, self).__init__(item_row) super(ItemCraftable, self).__init__(item_row)
if "create_components" not in item_row:
self.create_components = None self.create_components = None
if "upgrade_components" not in item_row:
self.upgrade_components = None self.upgrade_components = None
def set_components(self, create_components, upgrade_components): def set_components(self, create_components, upgrade_components):
@@ -230,10 +289,10 @@ class ItemCraftable(RowModel):
data = super(ItemCraftable, self).as_data() data = super(ItemCraftable, self).as_data()
if self.create_components is not None: if self.create_components is not None:
data["create_components"] = dict((item.name, item.quantity) 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: if self.upgrade_components is not None:
data["upgrade_components"] = dict((item.name, item.quantity) data["upgrade_components"] = dict((item.name, item.quantity)
for item in self.upgrade_components) for item in _item_list(self.upgrade_components))
return data return data
@@ -304,7 +363,7 @@ class Armor(ItemWithSkills):
assert self.skills is not None assert self.skills is not None
total = self.skills.get(skill_id_or_name, 0) total = self.skills.get(skill_id_or_name, 0)
slots_left = self.num_slots slots_left = self.num_slots
for slots in xrange(len(decoration_values), 0, -1): for slots in range(len(decoration_values), 0, -1):
if slots_left == 0: if slots_left == 0:
break break
decoration_value = decoration_values[slots-1] decoration_value = decoration_values[slots-1]
@@ -373,6 +432,8 @@ class Weapon(ItemCraftable):
def __init__(self, weapon_item_row): def __init__(self, weapon_item_row):
super(Weapon, self).__init__(weapon_item_row) super(Weapon, self).__init__(weapon_item_row)
self._parse_sharpness() self._parse_sharpness()
if "name_jp" not in self._data:
self._data["name_jp"] = self._data["name"]
def _parse_sharpness(self): def _parse_sharpness(self):
""" """
@@ -385,11 +446,12 @@ class Weapon(ItemCraftable):
if isinstance(self._row["sharpness"], list): if isinstance(self._row["sharpness"], list):
# MHX JSON data, already desired format, but doesn't have # MHX JSON data, already desired format, but doesn't have
# purple so we append 0 # purple so we append 0
self.sharpness = WeaponSharpness(self._row["sharpness"] + [0]) row_sharpness = self._row["sharpness"]
self.sharpness_plus = WeaponSharpness( row_sharpness_plus = self._row.get("sharpness_plus", row_sharpness)
self._row["sharpness_plus"] + [0]) row_sharpness_plus2 = self._row.get("sharpness_plus2", row_sharpness)
self.sharpness_plus2 = WeaponSharpness( self.sharpness = WeaponSharpness(row_sharpness)
self._row["sharpness_plus2"] + [0]) self.sharpness_plus = WeaponSharpness(row_sharpness_plus)
self.sharpness_plus2 = WeaponSharpness(row_sharpness_plus2)
else: else:
# 4U or gen data from db # 4U or gen data from db
parts = self._row["sharpness"].split(" ") parts = self._row["sharpness"].split(" ")
@@ -410,6 +472,10 @@ class Weapon(ItemCraftable):
# english weapons, and not for Japanese DLC weapons. # english weapons, and not for Japanese DLC weapons.
return ord(self.name[0]) < 128 return ord(self.name[0]) < 128
@property
def sharpness_name(self):
return SharpnessLevel.name(self.sharpness)
class Monster(RowModel): class Monster(RowModel):
_list_fields = ["id", "class", "name"] _list_fields = ["id", "class", "name"]
@@ -459,6 +525,12 @@ class MonsterPartStateDamage(RowModel):
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(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): class MonsterPartDamage(ModelBase):
""" """
@@ -493,6 +565,49 @@ class MonsterPartDamage(ModelBase):
damage=self.states 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): class MonsterDamage(ModelBase):
""" """
@@ -518,6 +633,10 @@ class MonsterDamage(ModelBase):
self.parts[part] = MonsterPartDamage(part) self.parts[part] = MonsterPartDamage(part)
self.parts[part].add_state(state, row) 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): def as_data(self):
return dict( return dict(
states=list(self.states), states=list(self.states),
@@ -529,11 +648,54 @@ class MonsterDamage(ModelBase):
Set breakable flag on parts based on the breakable list from Set breakable flag on parts based on the breakable list from
rewards (use MHDB.get_monster_breaks). rewards (use MHDB.get_monster_breaks).
""" """
for name, part_damage in self.parts.iteritems(): for name, part_damage in self.parts.items():
if _break_find(name, self.parts, breakable_list): if _break_find(name, self.parts, breakable_list):
#print "part %s is breakable [by rewards]" % name #print "part %s is breakable [by rewards]" % name
part_damage.breakable = True 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): def get_decoration_values(skill_id, decorations):
""" """
@@ -610,19 +772,19 @@ def get_costs(db, weapon):
upgrade_cost = int(weapon.upgrade_cost) upgrade_cost = int(weapon.upgrade_cost)
except ValueError: except ValueError:
upgrade_cost = 0 upgrade_cost = 0
print "WARN: bad upgrade cost for '%s' (%s): '%s'" \ print("WARN: bad upgrade cost for '%s' (%s): '%s'" \
% (weapon.name, weapon.id, weapon.upgrade_cost) % (weapon.name, weapon.id, weapon.upgrade_cost))
except UnicodeError: except UnicodeError:
upgrade_cost = 0 upgrade_cost = 0
cost_display = urllib.quote(weapon.upgrade_cost) cost_display = urllib.parse.quote(weapon.upgrade_cost)
print "WARN: bad upgrade cost for '%s' (%s): '%s'" \ print("WARN: bad upgrade cost for '%s' (%s): '%s'" \
% (weapon.name, weapon.id, cost_display) % (weapon.name, weapon.id, cost_display))
parent_weapon = db.get_weapon(weapon.parent_id) parent_weapon = db.get_weapon(weapon.parent_id)
costs = get_costs(db, parent_weapon) costs = get_costs(db, parent_weapon)
for cost in costs: for cost in costs:
cost["zenny"] += upgrade_cost cost["zenny"] += upgrade_cost
cost["path"] += [weapon] cost["path"] += [weapon]
for item in weapon.upgrade_components: for item in _item_list(weapon.upgrade_components):
if item.type == "Weapon": if item.type == "Weapon":
continue continue
if item.name not in cost["components"]: if item.name not in cost["components"]:
@@ -632,28 +794,65 @@ def get_costs(db, weapon):
try: try:
zenny = int(weapon.creation_cost) zenny = int(weapon.creation_cost)
except ValueError: except ValueError:
print "WARN: bad creation cost for '%s': '%s'" \ print("WARN: bad creation cost for '%s': '%s'" \
% (weapon.name, weapon.creation_cost) % (weapon.name, weapon.creation_cost))
zenny = weapon.upgrade_cost or 0 zenny = weapon.upgrade_cost or 0
create_cost = dict(zenny=zenny, create_cost = dict(zenny=zenny,
path=[weapon], path=[weapon],
components={}) components={})
for item in weapon.create_components: for item in _item_list(weapon.create_components):
create_cost["components"][item.name] = item.quantity create_cost["components"][item.name] = item.quantity
costs = [create_cost] + costs costs = [create_cost] + costs
if weapon.buy:
buy_cost = dict(zenny=int(weapon.buy),
path=[weapon],
components={})
costs = [buy_cost] + costs
return costs 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): class ItemStars(object):
""" """
Get the game progress (in hub stars) required to make an item. Caches Get the game progress (in hub stars) required to make an item. Caches
values. values.
""" """
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
self._item_stars = {} # item id -> stars dict self._item_stars = {} # item id -> stars dict
self._weapon_stars = {} # weapon id -> stars dict self._weapon_stars = {} # weapon id -> stars dict
self._monster_stars = {} # monster id -> stars dict
self._wyporium_trades = {} self._wyporium_trades = {}
if self.db.game == "4u": if self.db.game == "4u":
@@ -678,8 +877,12 @@ class ItemStars(object):
costs = get_costs(self.db, weapon) costs = get_costs(self.db, weapon)
# find least 'expensive' path # find least 'expensive' path
for c in costs: for c in costs:
# don't calculate stars from buy cost (buys aren't available
# until after item is craftable, sometimes much later)
if not c["components"]:
continue
current_stars = self._get_component_stars(c) current_stars = self._get_component_stars(c)
for k, v in current_stars.iteritems(): for k, v in current_stars.items():
if v is None: if v is None:
continue continue
if stars[k] is None or v < stars[k]: if stars[k] is None or v < stars[k]:
@@ -734,7 +937,7 @@ class ItemStars(object):
if "Scrap" in item.name: if "Scrap" in item.name:
continue continue
istars = self.get_item_stars(item.id) istars = self.get_item_stars(item.id)
for k, v in stars.items(): for k, v in list(stars.items()):
if istars[k] > v: if istars[k] > v:
stars[k] = istars[k] stars[k] = istars[k]
break break
@@ -765,7 +968,7 @@ class ItemStars(object):
gather_locations = set() gather_locations = set()
for gather in gathering: for gather in gathering:
gather_locations.add((gather["location_id"], gather["rank"])) gather_locations.add((gather["location_id"], gather["rank"]))
for location_id, rank in list(gather_locations): for location_id, rank in gather_locations:
gather_quests = self.db.get_location_quests(location_id, rank) gather_quests = self.db.get_location_quests(location_id, rank)
quests.extend(gather_quests) quests.extend(gather_quests)
@@ -773,7 +976,7 @@ class ItemStars(object):
monster_ranks = set() monster_ranks = set()
for monster in monsters: for monster in monsters:
monster_ranks.add((monster["monster_id"], monster["rank"])) monster_ranks.add((monster["monster_id"], monster["rank"]))
for monster_id, rank in list(monster_ranks): for monster_id, rank in monster_ranks:
monster_quests = self.db.get_monster_quests(monster_id, rank) monster_quests = self.db.get_monster_quests(monster_id, rank)
quests.extend(monster_quests) quests.extend(monster_quests)
@@ -785,17 +988,36 @@ class ItemStars(object):
if quest.stars == 0: if quest.stars == 0:
# ignore training quests # ignore training quests
if "Training" not in quest.name: if "Training" not in quest.name:
print "Error: non training quest has 0 stars", \ print("Error: non training quest has 0 stars", \
quest.id, quest.name quest.id, quest.name)
continue continue
if quest.hub in stars: if quest.hub in stars:
current = stars[quest.hub] current = stars[quest.hub]
if current is None or quest.stars < current: if current is None or quest.stars < current:
stars[quest.hub] = quest.stars stars[quest.hub] = quest.stars
else: else:
print "Error: unknown hub", quest.hub 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 # because they are more useful for filtering if limited to items
# exclusively available from permit or arena. Allows matching # exclusively available from permit or arena. Allows matching
# on based on meeting specified critera for # on based on meeting specified critera for
@@ -807,3 +1029,21 @@ class ItemStars(object):
self._item_stars[item_id] = stars self._item_stars[item_id] = stars
return 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

View File

@@ -3,7 +3,7 @@ Calculate expected values for monster hunter items and find the best quests
and hunts for getting an item with specified skills. and hunts for getting an item with specified skills.
""" """
from __future__ import print_function
from collections import OrderedDict from collections import OrderedDict
from mhapi import stats from mhapi import stats
@@ -105,7 +105,7 @@ class GatherLocation(object):
for gr in self._rewards: for gr in self._rewards:
gr.print(out, indent) gr.print(out, indent)
def __nonzero__(self): def __bool__(self):
return bool(len(self._rewards)) return bool(len(self._rewards))
def __len__(self): def __len__(self):
@@ -146,7 +146,7 @@ class QuestReward(object):
* (LuckSkill.AMAZING - LuckSkill.NONE + 1)) * (LuckSkill.AMAZING - LuckSkill.NONE + 1))
else: else:
counts = [stats.quest_reward_expected_c(self.slot, skill) counts = [stats.quest_reward_expected_c(self.slot, skill)
for skill in xrange(LuckSkill.NONE, for skill in range(LuckSkill.NONE,
LuckSkill.AMAZING+1)] LuckSkill.AMAZING+1)]
@@ -218,7 +218,7 @@ class QuestItemExpectedValue(object):
# chances there are to get other rewards. # chances there are to get other rewards.
fixed_seen = dict(A=set(), B=set(), Sub=set()) fixed_seen = dict(A=set(), B=set(), Sub=set())
dups = dict() dups = dict()
for i in xrange(len(rewards)): for i in range(len(rewards)):
reward = rewards[i] reward = rewards[i]
slot = reward["reward_slot"] slot = reward["reward_slot"]
if reward["percentage"] == 100: if reward["percentage"] == 100:
@@ -251,7 +251,7 @@ class QuestItemExpectedValue(object):
self.slot_rewards[reward.slot].append(reward) self.slot_rewards[reward.slot].append(reward)
evs = reward.expected_values() evs = reward.expected_values()
for i in xrange(len(evs)): for i in range(len(evs)):
self.total_expected_values[i] += evs[i] self.total_expected_values[i] += evs[i]
def print(self, out, indent=2): def print(self, out, indent=2):
@@ -320,10 +320,10 @@ class HuntReward(object):
shiny=self.shiny) shiny=self.shiny)
kill_ev = dict() kill_ev = dict()
cap_ev = dict() cap_ev = dict()
for skill in xrange(CarvingSkill.NONE, CarvingSkill.GOD+1): for skill in range(CarvingSkill.NONE, CarvingSkill.GOD+1):
kill_ev[CarvingSkill.name(skill)] = \ kill_ev[CarvingSkill.name(skill)] = \
self.expected_value(STRAT_CAP, carving_skill=skill) self.expected_value(STRAT_CAP, carving_skill=skill)
for skill in xrange(CapSkill.NONE, CapSkill.GOD+1): for skill in range(CapSkill.NONE, CapSkill.GOD+1):
cap_ev[CapSkill.name(skill)] = self.expected_value(STRAT_CAP, cap_ev[CapSkill.name(skill)] = self.expected_value(STRAT_CAP,
cap_skill=skill) cap_skill=skill)
@@ -357,7 +357,7 @@ class HuntReward(object):
self.kill = False self.kill = False
counts = [ counts = [
stats.capture_reward_expected_c(skill) stats.capture_reward_expected_c(skill)
for skill in xrange(CapSkill.NONE, for skill in range(CapSkill.NONE,
CapSkill.GOD+1) CapSkill.GOD+1)
] ]
elif self.condition == "Virus Reward": elif self.condition == "Virus Reward":
@@ -393,7 +393,7 @@ class HuntReward(object):
if self.skill == SKILL_CARVING: if self.skill == SKILL_CARVING:
counts = [ counts = [
self.stack_size + stats.carve_delta_expected_c(skill) self.stack_size + stats.carve_delta_expected_c(skill)
for skill in xrange(CarvingSkill.NONE, for skill in range(CarvingSkill.NONE,
CarvingSkill.GOD+1) CarvingSkill.GOD+1)
] ]
@@ -624,7 +624,7 @@ class HuntItemExpectedValue(object):
carving_skill=carving_skill) carving_skill=carving_skill)
return ev return ev
def __nonzero__(self): def __bool__(self):
return bool(len(self.matching_rewards)) return bool(len(self.matching_rewards))
def __len__(self): def __len__(self):
@@ -640,10 +640,10 @@ class HuntItemExpectedValue(object):
rewards=[r.as_data() for r in self.matching_rewards]) rewards=[r.as_data() for r in self.matching_rewards])
kill_ev = dict() kill_ev = dict()
cap_ev = dict() cap_ev = dict()
for skill in xrange(CarvingSkill.NONE, CarvingSkill.GOD+1): for skill in range(CarvingSkill.NONE, CarvingSkill.GOD+1):
kill_ev[CarvingSkill.name(skill)] = \ kill_ev[CarvingSkill.name(skill)] = \
self.expected_value(STRAT_CAP, carving_skill=skill) self.expected_value(STRAT_CAP, carving_skill=skill)
for skill in xrange(CapSkill.NONE, CapSkill.GOD+1): for skill in range(CapSkill.NONE, CapSkill.GOD+1):
cap_ev[CapSkill.name(skill)] = self.expected_value(STRAT_CAP, cap_ev[CapSkill.name(skill)] = self.expected_value(STRAT_CAP,
cap_skill=skill) cap_skill=skill)
@@ -737,8 +737,8 @@ class ItemRewards(object):
key = (mid, rank) key = (mid, rank)
self._hunt_items[key] = hunt_item self._hunt_items[key] = hunt_item
for rank, skill_sets in self.rank_skill_sets.iteritems(): for rank, skill_sets in self.rank_skill_sets.items():
for s in skill_sets.itervalues(): for s in skill_sets.values():
s.add_hunt_option(hunt_item) s.add_hunt_option(hunt_item)
def get_hunt_item(self, monster_id, monster_rank): def get_hunt_item(self, monster_id, monster_rank):
@@ -777,15 +777,15 @@ class ItemRewards(object):
gather_key = (quest_item.quest.location_id, quest_item.quest.rank) gather_key = (quest_item.quest.location_id, quest_item.quest.rank)
gather_location = self._gather_items.get(gather_key) gather_location = self._gather_items.get(gather_key)
for rank, skill_sets in self.rank_skill_sets.iteritems(): for rank, skill_sets in self.rank_skill_sets.items():
for s in skill_sets.itervalues(): for s in skill_sets.values():
s.add_quest_option(quest_item, hunt_items, gather_location) s.add_quest_option(quest_item, hunt_items, gather_location)
def print_gather_locations(self, out): def print_gather_locations(self, out):
if not self._gather_items: if not self._gather_items:
return return
for gl in self._gather_items.itervalues(): for gl in self._gather_items.values():
out.write("(GATHER) %s %s\n" out.write("(GATHER) %s %s\n"
% (gl.location_name, gl.rank)) % (gl.location_name, gl.rank))
gl.print(out, indent=2) gl.print(out, indent=2)
@@ -798,7 +798,7 @@ class ItemRewards(object):
if not self._hunt_items: if not self._hunt_items:
return return
for hunt_item in self._hunt_items.itervalues(): for hunt_item in self._hunt_items.values():
out.write("(HUNT) %s %s\n" out.write("(HUNT) %s %s\n"
% (hunt_item.monster_name, hunt_item.monster_rank)) % (hunt_item.monster_name, hunt_item.monster_rank))
hunt_item.print(out, indent=2) hunt_item.print(out, indent=2)
@@ -826,13 +826,13 @@ class ItemRewards(object):
def print_recommended_hunts(self, out): def print_recommended_hunts(self, out):
out.write("*** Poogie Recommends ***\n") out.write("*** Poogie Recommends ***\n")
for rank, skill_sets in self.rank_skill_sets.iteritems(): for rank, skill_sets in self.rank_skill_sets.items():
no_skill_best = skill_sets["No skills"].best no_skill_best = skill_sets["No skills"].best
if no_skill_best is None: if no_skill_best is None:
# not available at this rank # not available at this rank
continue continue
out.write("> " + rank + "\n") out.write("> " + rank + "\n")
for name, skill_set in skill_sets.iteritems(): for name, skill_set in skill_sets.items():
if skill_set.best is None: if skill_set.best is None:
# Don't print out a rank with no options # Don't print out a rank with no options
continue continue
@@ -850,8 +850,8 @@ class ItemRewards(object):
Get a list of the quests for acquiring a given item and the probability Get a list of the quests for acquiring a given item and the probability
of getting the item, depending on cap or kill and luck skills. of getting the item, depending on cap or kill and luck skills.
""" """
for quest_item in self._quest_items.itervalues(): for quest_item in self._quest_items.values():
out.write("(QUEST) " + unicode(quest_item.quest) + "\n") out.write("(QUEST) " + str(quest_item.quest) + "\n")
out.write(" %20s" % "= Quest\n") out.write(" %20s" % "= Quest\n")
quest_item.print(out, indent=2) quest_item.print(out, indent=2)
@@ -921,7 +921,7 @@ class ItemRewards(object):
item_name = self.item_row["name"] item_name = self.item_row["name"]
out.write("*** Wyporium trade for '%s'\n" % item_name) out.write("*** Wyporium trade for '%s'\n" % item_name)
out.write(" Unlocked by quest '%s'\n" out.write(" Unlocked by quest '%s'\n"
% unicode(self.trade_unlock_quest).split("\n")[0]) % str(self.trade_unlock_quest).split("\n")[0])
out.write("\n") out.write("\n")
self.print_recommended_hunts(out) self.print_recommended_hunts(out)

View File

@@ -42,7 +42,7 @@ def _reward_count_p(reward_count, min_rewards, max_rewards, extend_percent):
p = 1.0 p = 1.0
extend_p = extend_percent / 100.0 extend_p = extend_percent / 100.0
stop_p = 1.0 - extend_p stop_p = 1.0 - extend_p
for i in xrange(min_rewards+1, reward_count+1): for i in range(min_rewards+1, reward_count+1):
p *= extend_p p *= extend_p
if reward_count < max_rewards: if reward_count < max_rewards:
p *= stop_p p *= stop_p
@@ -56,7 +56,7 @@ def quest_reward_p(reward_percent, min_rewards, max_rewards, extend_percent=69):
extra attempt. extra attempt.
""" """
p = 0.0 p = 0.0
for reward_count in xrange(min_rewards, max_rewards + 1): for reward_count in range(min_rewards, max_rewards + 1):
p += (_reward_count_p(reward_count, min_rewards, max_rewards, p += (_reward_count_p(reward_count, min_rewards, max_rewards,
extend_percent) extend_percent)
* _quest_reward_p(reward_percent, reward_count)) * _quest_reward_p(reward_percent, reward_count))
@@ -71,7 +71,7 @@ def reward_expected_c(min_rewards, max_rewards, extend_percent):
""" """
total_p = 0.0 total_p = 0.0
expected_attempts = 0.0 expected_attempts = 0.0
for reward_count in xrange(min_rewards, max_rewards + 1): for reward_count in range(min_rewards, max_rewards + 1):
p = _reward_count_p(reward_count, min_rewards, max_rewards, p = _reward_count_p(reward_count, min_rewards, max_rewards,
extend_percent) extend_percent)
expected_attempts += p * reward_count expected_attempts += p * reward_count

View File

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

268
scrapers/fextralife-weapons.py Executable file
View File

@@ -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()

159
scrapers/mhrice_monsters.py Executable file
View File

@@ -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()

352
scrapers/mhrice_weapons.py Executable file
View File

@@ -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()

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
# vim: set fileencoding=utf8 : # vim: set fileencoding=utf8 :
import urllib import urllib.request, urllib.parse, urllib.error
import os import os
import json import json
import sys import sys
@@ -34,74 +34,74 @@ _RANGED_TYPES = ["Bow", "Light Bowgun", "Heavy Bowgun"]
_ELEMENT_MAP = { _ELEMENT_MAP = {
u"": "Fire", "": "Fire",
u"": "Water", "": "Water",
u"": "Thunder", "": "Thunder",
u"": "Ice", "": "Ice",
u"": "Dragon", "": "Dragon",
u"": "Poison", "": "Poison",
u"麻痺": "Paralysis", "麻痺": "Paralysis",
u"睡眠": "Sleep", "睡眠": "Sleep",
u"爆破": "Blast", "爆破": "Blast",
} }
_GL_SHOT_TYPES = { _GL_SHOT_TYPES = {
u"通常": "Normal", "通常": "Normal",
u"放射": "Long", "放射": "Long",
u"拡散": "Wide", "拡散": "Wide",
} }
_SA_PHIAL_TYPES = { _SA_PHIAL_TYPES = {
u"強撃ビン": "Power", "強撃ビン": "Power",
u"減気ビン": "Exhaust", "減気ビン": "Exhaust",
u"滅龍ビン": "Dragon", "滅龍ビン": "Dragon",
u"強属性ビン": "Element", "強属性ビン": "Element",
u"毒ビン": "Poison", "毒ビン": "Poison",
u"麻痺ビン": "Paralysis", "麻痺ビン": "Paralysis",
} }
_CB_PHIAL_TYPES = { _CB_PHIAL_TYPES = {
u"榴弾ビン": "Impact", "榴弾ビン": "Impact",
u"強属性ビン": "Element", "強属性ビン": "Element",
} }
_BUG_TYPES = { _BUG_TYPES = {
u"切断": "Cutting", "切断": "Cutting",
u"打撃": "Impact", "打撃": "Impact",
} }
_BOW_ARC_TYPES = { _BOW_ARC_TYPES = {
u"集中型": "Focus", "集中型": "Focus",
u"放散型": "Wide", "放散型": "Wide",
u"爆裂型": "Blast", "爆裂型": "Blast",
} }
_BOW_SHOT_TYPES = { _BOW_SHOT_TYPES = {
u"連射": "Rapid", "連射": "Rapid",
u"拡散": "Spread", "拡散": "Spread",
u"貫通": "Pierce", "貫通": "Pierce",
u"重射": "Heavy", "重射": "Heavy",
} }
_BOW_COATINGS = { _BOW_COATINGS = {
u"強1": "Power 1", "強1": "Power 1",
u"強2": "Power 2", "強2": "Power 2",
u"属1": "Element 1", "属1": "Element 1",
u"属2": "Element 2", "属2": "Element 2",
u"": "C. Range", "": "C. Range",
u"": "Paint", "": "Paint",
u"": "Poison", "": "Poison",
u"": "Paralysis", "": "Paralysis",
u"": "Sleep", "": "Sleep",
u"": "Exhaust", "": "Exhaust",
u"": "Blast", "": "Blast",
} }
@@ -195,8 +195,8 @@ def _add_phial_or_shot_data(data, td_element):
elif data["wtype"] == "Bow": elif data["wtype"] == "Bow":
data["arc_type"] = _BOW_ARC_TYPES[text] data["arc_type"] = _BOW_ARC_TYPES[text]
else: else:
msg = u"Unexpected element for wtype '%s'" % data["wtype"] msg = "Unexpected element for wtype '%s'" % data["wtype"]
print >>sys.stderr, msg, text print(msg, text, file=sys.stderr)
raise ValueError(msg) raise ValueError(msg)
@@ -223,7 +223,7 @@ def _get_detailed_sharpness(name, href, parser):
weapon_level = 1 weapon_level = 1
tmp_path = os.path.join(_pathfix.project_path, "tmp") tmp_path = os.path.join(_pathfix.project_path, "tmp")
fpath = os.path.join(tmp_path, "details-%s.html" % (base_name)) fpath = os.path.join(tmp_path, "details-%s.html" % (base_name))
urllib.urlretrieve(href, fpath) urllib.request.urlretrieve(href, fpath)
with open(fpath) as f: with open(fpath) as f:
tree = etree.parse(f, parser) tree = etree.parse(f, parser)
data1 = tree.xpath('//*/div[@class="data1"]') data1 = tree.xpath('//*/div[@class="data1"]')
@@ -252,7 +252,7 @@ def _get_detailed_sharpness(name, href, parser):
heads = tr.xpath('./th') heads = tr.xpath('./th')
if heads: if heads:
for j, th in enumerate(heads): for j, th in enumerate(heads):
if u"斬れ味" in th.text: if "斬れ味" in th.text:
sharpness_col = j sharpness_col = j
continue continue
@@ -265,8 +265,8 @@ def _get_detailed_sharpness(name, href, parser):
name = names[i] name = names[i]
try: try:
sharpness_levels = _parse_sharpness_td(sharpness_cell) sharpness_levels = _parse_sharpness_td(sharpness_cell)
except KeyError, ValueError: except KeyError as ValueError:
print >>sys.stderr, "bad sharpness:", href, name print("bad sharpness:", href, name, file=sys.stderr)
raise raise
SHARPNESS[name] = sharpness_levels SHARPNESS[name] = sharpness_levels
#print name, sharpness_levels #print name, sharpness_levels
@@ -322,11 +322,11 @@ def _parse_hh_attr_td(td_element):
affinity = int(span.text.strip()) affinity = int(span.text.strip())
text_lines = td_element.text.strip().split("\n") text_lines = td_element.text.strip().split("\n")
for line in text_lines: for line in text_lines:
if line.startswith(u"防御+"): if line.startswith("防御+"):
defense = int(line[3:]) defense = int(line[3:])
if td_element.tail: if td_element.tail:
slots = td_element.tail.count(u"") slots = td_element.tail.count("")
return attack, affinity, defense, elements, slots return attack, affinity, defense, elements, slots
@@ -347,13 +347,13 @@ def _parse_elements_td(td_element):
affinity = int(span.text.strip()) affinity = int(span.text.strip())
text_lines = td_element.text.strip().split("\n") text_lines = td_element.text.strip().split("\n")
for line in text_lines: for line in text_lines:
if line.startswith(u"防御+"): if line.startswith("防御+"):
defense = int(line[3:]) defense = int(line[3:])
return affinity, defense, elements return affinity, defense, elements
def _parse_element(text): def _parse_element(text):
for jp_element in sorted(_ELEMENT_MAP.keys(), key=lambda s: len(s), for jp_element in sorted(list(_ELEMENT_MAP.keys()), key=lambda s: len(s),
reverse=True): reverse=True):
if text.startswith(jp_element): if text.startswith(jp_element):
value = int(text[len(jp_element):]) value = int(text[len(jp_element):])
@@ -385,7 +385,7 @@ def _parse_name_td(td_element):
def _parse_slots_td(td_element): def _parse_slots_td(td_element):
text = td_element.text text = td_element.text
if text: if text:
return text.count(u"") return text.count("")
return 0 return 0
@@ -406,9 +406,9 @@ def _parse_sharpness_td(td_element):
if sub.text is None: if sub.text is None:
continue continue
current.append(sub.text.count(".")) current.append(sub.text.count("."))
for level in xrange(3): for level in range(3):
sharpness = sharpness_levels[level] sharpness = sharpness_levels[level]
for i in xrange(len(sharpness), 6): for i in range(len(sharpness), 6):
sharpness.append(0) sharpness.append(0)
return sharpness_levels return sharpness_levels
@@ -422,27 +422,27 @@ def _main():
raise raise
weapon_list = [] weapon_list = []
parser = etree.HTMLParser() parser = etree.HTMLParser()
for wtype, urls in _WEAPON_URLS.iteritems(): for wtype, urls in _WEAPON_URLS.items():
for i, url in enumerate(urls): for i, url in enumerate(urls):
fpath = os.path.join(tmp_path, "%s-%d.html" % (wtype, i)) fpath = os.path.join(tmp_path, "%s-%d.html" % (wtype, i))
urllib.urlretrieve(_BASE_URL + url, fpath) urllib.request.urlretrieve(_BASE_URL + url, fpath)
with open(fpath) as f: with open(fpath) as f:
tree = etree.parse(f, parser) tree = etree.parse(f, parser)
wlist = extract_weapon_list(wtype, tree, parser) wlist = extract_weapon_list(wtype, tree, parser)
weapon_list.extend(wlist) weapon_list.extend(wlist)
print json.dumps(weapon_list, indent=2) print(json.dumps(weapon_list, indent=2))
def _test_details(): def _test_details():
parser = etree.HTMLParser() parser = etree.HTMLParser()
# final level has same name # final level has same name
_get_detailed_sharpness(u"ベルダーハンマー", _get_detailed_sharpness("ベルダーハンマー",
"http://wiki.mhxg.org/ida/219225.html", parser) "http://wiki.mhxg.org/ida/219225.html", parser)
# final level has different name # final level has different name
_get_detailed_sharpness(u"テッケン", _get_detailed_sharpness("テッケン",
"http://wiki.mhxg.org/ida/230575.html", parser) "http://wiki.mhxg.org/ida/230575.html", parser)
# final level >= 10 (two chars) # final level >= 10 (two chars)
_get_detailed_sharpness(u"ウィルガシェルプレス", _get_detailed_sharpness("ウィルガシェルプレス",
"http://wiki.mhxg.org/ida/228545.html", parser) "http://wiki.mhxg.org/ida/228545.html", parser)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
# vim: set fileencoding=utf8 : # vim: set fileencoding=utf8 :
import urllib import urllib.request, urllib.parse, urllib.error
import os import os
import json import json
import sys import sys
@@ -17,7 +17,7 @@ _PAGES = {
"items": "MHX:_Item_List", "items": "MHX:_Item_List",
} }
_CIRCLE = u"\u26ab" _CIRCLE = "\u26ab"
def extract_names_and_icons(tree): def extract_names_and_icons(tree):
@@ -90,11 +90,11 @@ def _translate_icon_name(s):
def _main(): def _main():
tmp_path = os.path.join(_pathfix.project_path, "tmp") tmp_path = os.path.join(_pathfix.project_path, "tmp")
outdir = os.path.join(_pathfix.project_path, "db", "mhx") outdir = os.path.join(_pathfix.project_path, "db", "mhx")
for name, page in _PAGES.iteritems(): for name, page in _PAGES.items():
fpath = os.path.join(tmp_path, "wikia-%s.html" % name) fpath = os.path.join(tmp_path, "wikia-%s.html" % name)
opath = os.path.join(outdir, name.replace("-", "_") + ".json") opath = os.path.join(outdir, name.replace("-", "_") + ".json")
parser = etree.HTMLParser() parser = etree.HTMLParser()
urllib.urlretrieve(_BASE_URL + page, fpath) urllib.request.urlretrieve(_BASE_URL + page, fpath)
with open(fpath) as f: with open(fpath) as f:
tree = etree.parse(f, parser) tree = etree.parse(f, parser)
data = extract_names_and_icons(tree) data = extract_names_and_icons(tree)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
# vim: set fileencoding=utf8 : # vim: set fileencoding=utf8 :
import urllib import urllib.request, urllib.parse, urllib.error
import os import os
import json import json
import sys import sys
@@ -14,7 +14,7 @@ _BASE_URL = "http://monsterhunter.wikia.com/wiki/"
_PAGE = "MHX:_Palico_Skills" _PAGE = "MHX:_Palico_Skills"
_CIRCLE = u"\u26ab" _CIRCLE = "\u26ab"
def extract_arts_and_skills(tree): def extract_arts_and_skills(tree):
@@ -29,7 +29,7 @@ def extract_arts_and_skills(tree):
rows = list(table) rows = list(table)
for row in rows: for row in rows:
cols, is_header = _get_column_cells_texts(row) cols, is_header = _get_column_cells_texts(row)
print is_header, cols print(is_header, cols)
continue continue
if is_header: if is_header:
if len(cols) == 1: if len(cols) == 1:
@@ -100,13 +100,13 @@ def _main():
tmp_path = os.path.join(_pathfix.project_path, "tmp") tmp_path = os.path.join(_pathfix.project_path, "tmp")
fpath = os.path.join(tmp_path, "wikia-palico-skills.html") fpath = os.path.join(tmp_path, "wikia-palico-skills.html")
parser = etree.HTMLParser() parser = etree.HTMLParser()
urllib.urlretrieve(_BASE_URL + _PAGE, fpath) urllib.request.urlretrieve(_BASE_URL + _PAGE, fpath)
with open(fpath) as f: with open(fpath) as f:
tree = etree.parse(f, parser) tree = etree.parse(f, parser)
arts, skills = extract_arts_and_skills(tree) arts, skills = extract_arts_and_skills(tree)
#print json.dumps(weapon_list, indent=2) #print json.dumps(weapon_list, indent=2)
print json.dumps(arts, indent=2) print(json.dumps(arts, indent=2))
print json.dumps(skills, indent=2) print(json.dumps(skills, indent=2))
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

BIN
web/img/Cut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
web/img/Impact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
web/img/Shot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -135,7 +135,7 @@ function normalize_name(s) {
function setup_item_autocomplete(selector) { 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", $.getJSON(DATA_PATH + "items.json",
function(data) { function(data) {
$(selector).autocomplete({ source: 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) { function load_weapon_data(ready_fn) {
if (typeof DATA_PATH == "undefined") { if (typeof DATA_PATH == "undefined") {
DATA_PATH = get_base_path() + "/jsonapi/"; DATA_PATH = get_base_path() + "/jsonapi/";

View File

@@ -7,9 +7,9 @@
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" /> <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 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/ejs_production.js"></script>
<script src="js/common.js"></script> <script src="/js/common.js"></script>
<style> <style>
label { label {
@@ -34,7 +34,7 @@
</style> </style>
<script type="text/javascript"> <script type="text/javascript">
var DATA_PATH = get_base_path() + "/jsonapi/"; var DATA_PATH = "/jsonapi/mh4u/";
var TYPES = ["Head", "Body", "Arms", "Waist", "Legs"]; var TYPES = ["Head", "Body", "Arms", "Waist", "Legs"];
var GEAR = ["Weapon", "Head", "Body", "Arms", "Waist", "Legs", "Talisman"]; var GEAR = ["Weapon", "Head", "Body", "Arms", "Waist", "Legs", "Talisman"];
var ELEMENTS = ["fire", "water", "thunder", "ice", "dragon"]; var ELEMENTS = ["fire", "water", "thunder", "ice", "dragon"];
@@ -60,9 +60,9 @@
"Waist": [], "Legs": [], "Weapon": [] }; "Waist": [], "Legs": [], "Weapon": [] };
var slots_left = {}; var slots_left = {};
var template_skills = new EJS({ url: "templates/skills.ejs" }); var template_skills = new EJS({ url: "/templates/skills.ejs" });
var template_resist = new EJS({ url: "templates/resistance.ejs" }); var template_resist = new EJS({ url: "/templates/resistance.ejs" });
var template_decorations = new EJS({ url: "templates/decorations.ejs" }); var template_decorations = new EJS({ url: "/templates/decorations.ejs" });
$(document).ready(function(){ $(document).ready(function(){
$.getJSON(DATA_PATH + "armor/_index_name.json", $.getJSON(DATA_PATH + "armor/_index_name.json",

345
web/mhr/weaponlist.html Normal file
View File

@@ -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>

284
web/mhr/weaponplanner.html Normal file
View File

@@ -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>

View File

@@ -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>

View File

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