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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  
  
  
  
  
  0 
+
+
+ Skills +
+
+
+ Elemental Resistance +
+
+
+ + 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""};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 @@ + +<% for(var i=0; i + + + + +<% } %> +<% if (free_slots > 0) { %> + + + + +<% } %> +
<%= Array(decorations[i]["num_slots"] + 1).join("\u26ab") %><%= decorations[i]["display_name"] %> +
<%= Array(free_slots + 1).join("\u26aa") %><%= Array(3- free_slots + 1).join(" ") %> +
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 @@ + + + + + + + + + + +<% for(var i=0; i + + + + <% for(var j=0; j + + <% } %> +<% } %> +
 TotalHeadBodyArmsWaistLegs
<%= ELEMENTS[i] %><%= element_resist[ELEMENTS[i]] %> + <%= 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 @@ + + + + +<% for(var i=0; i + +<% } %> + +<% for(var i=0; i + + + + <% for(var j=0; j + + <% } %> + +<% } %> +
 Total<%= TYPES[i] %>
<%= active_skills[i] %><%= skills[active_skills[i]] %> + <%= (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 @@ + + + + +<% for(var i=0; i + +<% } %> + +<% for(var i=0; i + + + + <% for(var j=0; j + + <% } %> + +<% } %> +
SkillTotal<%= TYPES[i] %>
<%= active_skills[i] %><%= skills[active_skills[i]] %> + <%= (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]] + ")" + : " " %> +