diff --git a/bin/mharmor.py b/bin/mharmor.py
index be61dee..8ecb8a0 100755
--- a/bin/mharmor.py
+++ b/bin/mharmor.py
@@ -7,9 +7,9 @@ import codecs
import _pathfix
from mhapi.db import MHDB
+from mhapi.model import get_decoration_values
+from mhapi.util import get_utf8_writer
-def get_utf8_writer(writer):
- return codecs.getwriter("utf8")(writer)
def parse_args(argv):
parser = argparse.ArgumentParser(description=
@@ -46,7 +46,7 @@ def find_armors(args):
ds = db.get_decorations_by_skills([sid])
for d in ds:
d.set_skills(db.get_item_skills(d.id))
- decoration_values = get_decoration_values(sid, ds)
+ decoration_values = get_decoration_values(sid, ds)[1]
decorations[sid] = (ds, decoration_values)
print "%s[%s]:" % (skill_name, sid), ", ".join(d.name for d in ds), \
decoration_values
@@ -88,20 +88,6 @@ def find_armors(args):
print " ", a.one_line_skills_u(args.skills)
-def get_decoration_values(skill_id, decorations):
- # TODO: write script to compute this and shove innto skill_tree table
- values = [0, 0, 0]
- for d in decorations:
- assert d.num_slots is not None
- # some skills like Handicraft have multiple decorations with
- # same number of slots - use the best one
- new = d.skills[skill_id]
- current = values[d.num_slots-1]
- if new > current:
- values[d.num_slots-1] = new
- return values
-
-
if __name__ == '__main__':
args = parse_args(None)
diff --git a/bin/mkjsonapi.py b/bin/mkjsonapi.py
index 8c22e8a..8868b24 100755
--- a/bin/mkjsonapi.py
+++ b/bin/mkjsonapi.py
@@ -35,13 +35,21 @@ def file_path(path, model_object, use_name=False):
def write_list_file(path, model_list):
list_path = os.path.join(path, "_list.json")
with open(list_path, "w") as f:
- json.dump([o.as_list_data() for o in model_list], f, indent=2)
+ json.dump([o.as_list_data() for o in model_list],
+ f, cls=model.ModelJSONEncoder, indent=2)
def write_index_file(path, indexes):
- index_path = os.path.join(path, "_index.json")
- with open(index_path, "w") as f:
- json.dump(indexes, f, indent=2)
+ for key, data in indexes.iteritems():
+ index_path = os.path.join(path, "_index_%s.json" % key)
+ with open(index_path, "w") as f:
+ json.dump(data, f, cls=model.ModelJSONEncoder, indent=2)
+
+
+def write_all_file(path, all_data):
+ all_path = os.path.join(path, "_all.json")
+ with open(all_path, "w") as f:
+ json.dump(all_data, f, cls=model.ModelJSONEncoder, indent=2)
def monster_json(db, path):
@@ -63,6 +71,73 @@ def monster_json(db, path):
write_index_file(path, indexes)
+def armor_json(db, path):
+ armors = db.get_armors()
+ mkdirs_p(path)
+ write_list_file(path, armors)
+ all_data = []
+
+ indexes = {}
+ for a in armors:
+ armor_path = file_path(path, a)
+ a.update_indexes(indexes)
+ skills = db.get_item_skills(a.id)
+ if not skills:
+ print "WARN: armor '%s' (%d) has no skills" % (a.name, a.id)
+ a.set_skills(skills)
+
+ all_data.append(a.as_data())
+
+ with open(armor_path, "w") as f:
+ a.json_dump(f)
+
+ write_index_file(path, indexes)
+ write_all_file(path, all_data)
+
+
+def decoration_json(db, path):
+ decorations = db.get_decorations()
+ mkdirs_p(path)
+ write_list_file(path, decorations)
+ all_data = []
+
+ indexes = {}
+ for a in decorations:
+ decoration_path = file_path(path, a)
+ a.update_indexes(indexes)
+ skills = db.get_item_skills(a.id)
+ if not skills:
+ print "WARN: decoration '%s' (%d) has no skills" % (a.name, a.id)
+ a.set_skills(skills)
+
+ all_data.append(a.as_data())
+
+ with open(decoration_path, "w") as f:
+ a.json_dump(f)
+
+ write_index_file(path, indexes)
+ write_all_file(path, all_data)
+
+
+def skilltree_json(db, path):
+ skill_trees = db.get_skill_trees()
+ mkdirs_p(path)
+ write_list_file(path, skill_trees)
+
+ all_data = {}
+ for st in skill_trees:
+ ds = db.get_decorations_by_skills([st.id])
+ for d in ds:
+ d.set_skills(db.get_item_skills(d.id))
+ st.set_decorations(ds)
+ skilltree_path = file_path(path, st)
+ all_data[st.name] = st
+ with open(skilltree_path, "w") as f:
+ st.json_dump(f)
+
+ write_all_file(path, all_data)
+
+
def weapon_json(db, path):
weapons = db.get_weapons()
mkdirs_p(path)
@@ -104,7 +179,10 @@ def main():
items_json(db, os.path.join(outpath, "item"))
weapon_json(db, os.path.join(outpath, "weapon"))
monster_json(db, os.path.join(outpath, "monster"))
- #quest_json
+ armor_json(db, os.path.join(outpath, "armor"))
+ skilltree_json(db, os.path.join(outpath, "skilltree"))
+ decoration_json(db, os.path.join(outpath, "decoration"))
+ #quest_json(db, os.path.join(outpath, "quest"))
if __name__ == '__main__':
diff --git a/mhapi/db.py b/mhapi/db.py
index 92c8dc7..a0858ef 100644
--- a/mhapi/db.py
+++ b/mhapi/db.py
@@ -299,27 +299,31 @@ class MHDB(object):
def get_weapon_by_name(self, name):
return self._query_one("weapon", """
- SELECT * FROM weapons
+ SELECT items._id, items.name, items.buy, weapons.*
+ FROM weapons
LEFT JOIN items ON weapons._id = items._id
WHERE items.name=?
""", (name,), model_cls=model.Weapon)
def get_armors(self):
return self._query_all("armors", """
- SELECT * FROM armor
+ SELECT items._id, items.name, items.buy, armor.*
+ FROM armor
LEFT JOIN items ON armor._id = items._id
""", model_cls=model.Armor)
def get_armor(self, armor_id):
return self._query_one("armor", """
- SELECT * FROM armor
+ SELECT items._id, items.name, items.buy, armor.*
+ FROM armor
LEFT JOIN items ON armor._id = items._id
WHERE armor._id=?
""", (armor_id,), model_cls=model.Armor)
def get_armor_by_name(self, name):
return self._query_one("armor", """
- SELECT * FROM armor
+ SELECT items._id, items.name, items.buy, armor.*
+ FROM armor
LEFT JOIN items ON armor._id = items._id
WHERE items.name=?
""", (name,), model_cls=model.Armor)
@@ -335,7 +339,7 @@ class MHDB(object):
def get_decorations(self):
return self._query_all("decorations", """
- SELECT *
+ SELECT items._id, items.name, items.buy, decorations.*
FROM decorations
INNER JOIN items
ON items._id = decorations._id
@@ -343,7 +347,7 @@ class MHDB(object):
def get_decoration(self, decoration_id):
return self._query_one("decoration", """
- SELECT *
+ SELECT items._id, items.name, items.buy, decorations.*
FROM decorations
INNER JOIN items
ON items._id = decorations._id
@@ -352,13 +356,18 @@ class MHDB(object):
def get_decoration_by_name(self, name):
return self._query_all("decoration", """
- SELECT *
+ SELECT items._id, items.name, items.buy, decorations.*
FROM decorations
INNER JOIN items
ON items._id = decorations._id
WHERE items.name = ?
""", (name,), model_cls=model.Decoration)
+ def get_skill_trees(self):
+ return self._query_all("skills", """
+ SELECT _id, name FROM skill_trees
+ """, model_cls=model.SkillTree)
+
def get_skill_tree_id(self, skill_tree_name):
result = self._query_one("skill", """
SELECT _id FROM skill_trees
@@ -372,7 +381,7 @@ class MHDB(object):
args = sorted(skill_tree_ids)
placeholders = ", ".join(["?"] * len(skill_tree_ids))
return self._query_all("decorations", """
- SELECT items.*, decorations.*
+ SELECT items._id, items.name, items.buy, decorations.*
FROM item_to_skill_tree
LEFT JOIN items
ON items._id = item_to_skill_tree.item_id
@@ -389,7 +398,7 @@ class MHDB(object):
placeholders = ", ".join(["?"] * len(skill_tree_ids))
args += [hunter_type]
return self._query_all("decorations", """
- SELECT items.*, armor.*
+ SELECT items._id, items.name, items.buy, armor.*
FROM item_to_skill_tree
LEFT JOIN items
ON items._id = item_to_skill_tree.item_id
diff --git a/mhapi/model.py b/mhapi/model.py
index c0af82f..771e370 100644
--- a/mhapi/model.py
+++ b/mhapi/model.py
@@ -76,7 +76,10 @@ class RowModel(ModelBase):
self.update_index(key_field, value_fields, data[key_field])
def update_index(self, key_field, value_fields, data):
- item = dict((k, self[k]) for k in value_fields)
+ if isinstance(value_fields, str):
+ item = self[value_fields]
+ else:
+ item = dict((k, self[k]) for k in value_fields)
key_value = self[key_field]
if key_value not in data:
data[key_value] = []
@@ -212,16 +215,29 @@ class ItemWithSkills(RowModel):
return self.skills.get(skill_id_or_name, 0)
def one_line_skills_u(self, skill_names=None):
+ """
+ Get comma separated list of skills on the item. If @skill_names
+ is passed, only include skills that are in that list.
+ """
if skill_names is None:
skill_names = sorted(self.skill_names)
return ", ".join("%s %d" % (name, self.skills[name])
for name in skill_names
if name in self.skills)
+ def as_data(self):
+ data = super(ItemWithSkills, self).as_data()
+ if self.skills is not None:
+ data["skills"] = dict((name, self.skills[name])
+ for name in self.skill_names)
+ #data["skills_by_id"] = dict((sid, self.skills[sid])
+ # for sid in self.skill_ids)
+ return data
+
class Armor(ItemWithSkills):
- _indexes = { "name": ["id"],
- "slot": ["id", "name"] }
+ _indexes = { "name": "id",
+ "slot": "name" }
_one_line_template = string.Template(
"$name ($slot) Def $defense-$max_defense Slot $num_slots"
@@ -236,9 +252,10 @@ class Armor(ItemWithSkills):
def skill(self, skill_id_or_name, decoration_values=()):
"""
decoration_values should be a list of points from the given
- number of slots, e.g. [1, 3] means that one slot gets 1 point
- and two slots get 3 points, [1, 0, 4] means that one slot gets 1 point,
- there is no two slot gem, and three slots gets 4 points.
+ number of slots, e.g. [1, 3] or [1, 3, 0] means that one slot
+ gets 1 point and two slots get 3 points, [1, 0, 4] means that
+ one slot gets 1 point, there is no two slot gem, and three slots
+ gets 4 points. If not passed, just returns native skill points.
"""
assert self.skills is not None
total = self.skills.get(skill_id_or_name, 0)
@@ -249,7 +266,7 @@ class Armor(ItemWithSkills):
decoration_value = decoration_values[slots-1]
if not decoration_value:
continue
- if slots <= slots_left:
+ while slots <= slots_left:
total += decoration_value
slots_left -= slots
return total
@@ -263,6 +280,27 @@ class ItemSkill(RowModel):
pass
+class SkillTree(RowModel):
+ def __init__(self, skill_tree_row):
+ super(SkillTree, self).__init__(skill_tree_row)
+ self.decoration_values = None
+ self.decoration_ids = None
+
+ def set_decorations(self, decorations):
+ if decorations is None:
+ self.decoration_values = None
+ else:
+ self.decoration_ids, self.decoration_values = \
+ get_decoration_values(self.id, decorations)
+
+ def as_data(self):
+ data = super(SkillTree, self).as_data()
+ if self.decoration_values is not None:
+ data["decoration_values"] = self.decoration_values
+ data["decoration_ids"] = self.decoration_ids
+ return data
+
+
class Weapon(RowModel):
_list_fields = ["id", "wtype", "name"]
_indexes = { "name": ["id"],
@@ -401,6 +439,22 @@ class MonsterDamage(ModelBase):
part_damage.breakable = True
+def get_decoration_values(skill_id, decorations):
+ # TODO: write script to compute this and shove into skill_tree table
+ values = [0, 0, 0]
+ ids = [None, None, None]
+ for d in decorations:
+ assert d.num_slots is not None
+ # some skills like Handicraft have multiple decorations with
+ # same number of slots - use the best one
+ new = d.skills[skill_id]
+ current = values[d.num_slots-1]
+ if new > current:
+ values[d.num_slots-1] = new
+ ids[d.num_slots-1] = d.id
+ return (ids, values)
+
+
def _break_find(part, parts, breaks):
# favor 'Tail Tip' over Tail for Basarios
if part == "Tail" and "Tail Tip" in parts:
diff --git a/mhapi/util.py b/mhapi/util.py
index d7362e0..ad4cd96 100644
--- a/mhapi/util.py
+++ b/mhapi/util.py
@@ -2,6 +2,8 @@
Shared utility classes and functions.
"""
+import codecs
+
class EnumBase(object):
_names = dict()
@@ -9,3 +11,7 @@ class EnumBase(object):
@classmethod
def name(cls, enum_value):
return cls._names[enum_value]
+
+
+def get_utf8_writer(writer):
+ return codecs.getwriter("utf8")(writer)
diff --git a/web/armor.html b/web/armor.html
new file mode 100644
index 0000000..d37476e
--- /dev/null
+++ b/web/armor.html
@@ -0,0 +1,402 @@
+
+
+ Poogie Outfitters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/js/ejs_production.js b/web/js/ejs_production.js
new file mode 100644
index 0000000..ccce922
--- /dev/null
+++ b/web/js/ejs_production.js
@@ -0,0 +1 @@
+(function(){var rsplit=function(string,regex){var result=regex.exec(string),retArr=new Array(),first_idx,last_idx,first_bit;while(result!=null){first_idx=result.index;last_idx=regex.lastIndex;if((first_idx)!=0){first_bit=string.substring(0,first_idx);retArr.push(string.substring(0,first_idx));string=string.slice(first_idx)}retArr.push(result[0]);string=string.slice(result[0].length);result=regex.exec(string)}if(!string==""){retArr.push(string)}return retArr},chop=function(string){return string.substr(0,string.length-1)},extend=function(d,s){for(var n in s){if(s.hasOwnProperty(n)){d[n]=s[n]}}};EJS=function(options){options=typeof options=="string"?{view:options}:options;this.set_options(options);if(options.precompiled){this.template={};this.template.process=options.precompiled;EJS.update(this.name,this);return }if(options.element){if(typeof options.element=="string"){var name=options.element;options.element=document.getElementById(options.element);if(options.element==null){throw name+"does not exist!"}}if(options.element.value){this.text=options.element.value}else{this.text=options.element.innerHTML}this.name=options.element.id;this.type="["}else{if(options.url){options.url=EJS.endExt(options.url,this.extMatch);this.name=this.name?this.name:options.url;var url=options.url;var template=EJS.get(this.name,this.cache);if(template){return template}if(template==EJS.INVALID_PATH){return null}try{this.text=EJS.request(url+(this.cache?"":"?"+Math.random()))}catch(e){}if(this.text==null){throw ({type:"EJS",message:"There is no template at "+url})}}}var template=new EJS.Compiler(this.text,this.type);template.compile(options,this.name);EJS.update(this.name,this);this.template=template};EJS.prototype={render:function(object,extra_helpers){object=object||{};this._extra_helpers=extra_helpers;var v=new EJS.Helpers(object,extra_helpers||{});return this.template.process.call(object,object,v)},update:function(element,options){if(typeof element=="string"){element=document.getElementById(element)}if(options==null){_template=this;return function(object){EJS.prototype.update.call(_template,element,object)}}if(typeof options=="string"){params={};params.url=options;_template=this;params.onComplete=function(request){var object=eval(request.responseText);EJS.prototype.update.call(_template,element,object)};EJS.ajax_request(params)}else{element.innerHTML=this.render(options)}},out:function(){return this.template.out},set_options:function(options){this.type=options.type||EJS.type;this.cache=options.cache!=null?options.cache:EJS.cache;this.text=options.text||null;this.name=options.name||null;this.ext=options.ext||EJS.ext;this.extMatch=new RegExp(this.ext.replace(/\./,"."))}};EJS.endExt=function(path,match){if(!path){return null}match.lastIndex=0;return path+(match.test(path)?"":this.ext)};EJS.Scanner=function(source,left,right){extend(this,{left_delimiter:left+"%",right_delimiter:"%"+right,double_left:left+"%%",double_right:"%%"+right,left_equal:left+"%=",left_comment:left+"%#"});this.SplitRegexp=left=="["?/(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/:new RegExp("("+this.double_left+")|(%%"+this.double_right+")|("+this.left_equal+")|("+this.left_comment+")|("+this.left_delimiter+")|("+this.right_delimiter+"\n)|("+this.right_delimiter+")|(\n)");this.source=source;this.stag=null;this.lines=0};EJS.Scanner.to_text=function(input){if(input==null||input===undefined){return""}if(input instanceof Date){return input.toDateString()}if(input.toString){return input.toString()}return""};EJS.Scanner.prototype={scan:function(block){scanline=this.scanline;regex=this.SplitRegexp;if(!this.source==""){var source_split=rsplit(this.source,/\n/);for(var i=0;i0){for(var i=0;i0){buff.push(put_cmd+'"'+clean(content)+'")')}content="";break;case scanner.double_left:content=content+scanner.left_delimiter;break;default:content=content+token;break}}else{switch(token){case scanner.right_delimiter:switch(scanner.stag){case scanner.left_delimiter:if(content[content.length-1]=="\n"){content=chop(content);buff.push(content);buff.cr()}else{buff.push(content)}break;case scanner.left_equal:buff.push(insert_cmd+"(EJS.Scanner.to_text("+content+")))");break}scanner.stag=null;content="";break;case scanner.double_right:content=content+scanner.right_delimiter;break;default:content=content+token;break}}});if(content.length>0){buff.push(put_cmd+'"'+clean(content)+'")')}buff.close();this.out=buff.script+";";var to_be_evaled="/*"+name+"*/this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {"+this.out+" return ___ViewO.join('');}}}catch(e){e.lineNumber=null;throw e;}};";try{eval(to_be_evaled)}catch(e){if(typeof JSLINT!="undefined"){JSLINT(this.out);for(var i=0;i").replace(/''/g,"'")}return""}};EJS.newRequest=function(){var factories=[function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new XMLHttpRequest()},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];for(var i=0;i")};EJS.Helpers.prototype.start_tag_for=function(A,B){return this.tag(A,B)};EJS.Helpers.prototype.submit_tag=function(A,B){B=B||{};B.type=B.type||"submit";B.value=A||"Submit";return this.single_tag_for("input",B)};EJS.Helpers.prototype.tag=function(C,E,D){if(!D){var D=">"}var B=" ";for(var A in E){if(E[A]!=null){var F=E[A].toString()}else{var F=""}if(A=="Class"){A="class"}if(F.indexOf("'")!=-1){B+=A+'="'+F+'" '}else{B+=A+"='"+F+"' "}}return"<"+C+B+D};EJS.Helpers.prototype.tag_end=function(A){return""+A+">"};EJS.Helpers.prototype.text_area_tag=function(A,C,B){B=B||{};B.id=B.id||A;B.name=B.name||A;C=C||"";if(B.size){B.cols=B.size.split("x")[0];B.rows=B.size.split("x")[1];delete B.size}B.cols=B.cols||50;B.rows=B.rows||4;return this.start_tag_for("textarea",B)+C+this.tag_end("textarea")};EJS.Helpers.prototype.text_tag=EJS.Helpers.prototype.text_area_tag;EJS.Helpers.prototype.text_field_tag=function(A,C,B){return this.input_field_tag(A,C,"text",B)};EJS.Helpers.prototype.url_for=function(A){return'window.location="'+A+'";'};EJS.Helpers.prototype.img_tag=function(B,C,A){A=A||{};A.src=B;A.alt=C;return this.single_tag_for("img",A)}
\ No newline at end of file
diff --git a/web/templates/decorations.ejs b/web/templates/decorations.ejs
new file mode 100644
index 0000000..5d7f50e
--- /dev/null
+++ b/web/templates/decorations.ejs
@@ -0,0 +1,18 @@
+
diff --git a/web/templates/resistance.ejs b/web/templates/resistance.ejs
new file mode 100644
index 0000000..2dd4822
--- /dev/null
+++ b/web/templates/resistance.ejs
@@ -0,0 +1,21 @@
+
+
+ | |
+ Total |
+ Head |
+ Body |
+ Arms |
+ Waist |
+ Legs |
+
+<% for(var i=0; i
+
+ | <%= ELEMENTS[i] %> |
+ <%= element_resist[ELEMENTS[i]] %> |
+ <% for(var j=0; j
+
+ <%= armors[TYPES[j]]
+ ? armors[TYPES[j]][ELEMENTS[i] + "_res"] : " " %> |
+ <% } %>
+<% } %>
+
diff --git a/web/templates/skills.ejs b/web/templates/skills.ejs
new file mode 100644
index 0000000..ed67c8b
--- /dev/null
+++ b/web/templates/skills.ejs
@@ -0,0 +1,25 @@
+
+
+ | |
+ Total |
+<% for(var i=0; i
+ <%= TYPES[i] %> |
+<% } %>
+
+<% for(var i=0; i
+
+ | <%= active_skills[i] %> |
+ <%= skills[active_skills[i]] %> |
+ <% for(var j=0; j
+
+ <%= (armors[TYPES[j]] && tskills_by_type[TYPES[j]][active_skills[i]])
+ ? tskills_by_type[TYPES[j]][active_skills[i]]
+ : " " %>
+ <%= 0 && (armors[TYPES[j]] && dskills_by_type[TYPES[j]][active_skills[i]])
+ ? " (" + dskills_by_type[TYPES[j]][active_skills[i]] + ")"
+ : " " %>
+ |
+ <% } %>
+
+<% } %>
+
diff --git a/web/templates/skills.ejs.bak b/web/templates/skills.ejs.bak
new file mode 100644
index 0000000..f49ec2c
--- /dev/null
+++ b/web/templates/skills.ejs.bak
@@ -0,0 +1,24 @@
+
+
+ | Skill |
+ Total |
+<% for(var i=0; i
+ <%= TYPES[i] %> |
+<% } %>
+
+<% for(var i=0; i
+
+ | <%= active_skills[i] %> |
+ <%= skills[active_skills[i]] %> |
+ <% for(var j=0; j
+
+ <%= (armors[TYPES[j]] && armors[TYPES[j]]["skills"][active_skills[i]])
+ ? armors[TYPES[j]]["skills"][active_skills[i]] : " " %>
+ <%= (armors[TYPES[j]] && dskills_by_type[TYPES[j]][active_skills[i]])
+ ? " (" + dskills_by_type[TYPES[j]][active_skills[i]] + ")"
+ : " " %>
+ |
+ <% } %>
+
+<% } %>
+