add weapon tree compare tool
This commit is contained in:
77
bin/mhweaponbuild.py
Executable file
77
bin/mhweaponbuild.py
Executable file
@@ -0,0 +1,77 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
|
||||||
|
import _pathfix
|
||||||
|
|
||||||
|
from mhapi.db import MHDB
|
||||||
|
from mhapi.model import ModelJSONEncoder
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv):
|
||||||
|
parser = argparse.ArgumentParser(description=
|
||||||
|
"Determine the different paths for making a weapons and the cost"
|
||||||
|
" and parts required for each path."
|
||||||
|
)
|
||||||
|
parser.add_argument("-j", "--json", action="store_true",
|
||||||
|
default=False,
|
||||||
|
help="output as json instead of plaintext")
|
||||||
|
parser.add_argument("weapon",
|
||||||
|
help="Full name of weapon")
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def get_costs(db, weapon):
|
||||||
|
"""
|
||||||
|
Get a list of alternative ways of making a weapon, as a list of dicts
|
||||||
|
containing item counts. The dicts also contain special keys _zenny
|
||||||
|
for the total zenny needed, and _path for a list of weapons that
|
||||||
|
make up the upgrade path.
|
||||||
|
"""
|
||||||
|
costs = []
|
||||||
|
if weapon.parent_id:
|
||||||
|
parent_weapon = db.get_weapon(weapon.parent_id, True)
|
||||||
|
costs = get_costs(db, parent_weapon)
|
||||||
|
for cost in costs:
|
||||||
|
for item in weapon.upgrade_components:
|
||||||
|
if item.type == "Weapon":
|
||||||
|
continue
|
||||||
|
if item.name not in cost["components"]:
|
||||||
|
cost["components"][item.name] = 0
|
||||||
|
cost["components"][item.name] += item.quantity
|
||||||
|
cost["zenny"] += weapon.upgrade_cost
|
||||||
|
cost["path"] += [weapon]
|
||||||
|
if weapon.creation_cost:
|
||||||
|
create_cost = dict(zenny=weapon.creation_cost,
|
||||||
|
path=[weapon],
|
||||||
|
components={})
|
||||||
|
for item in weapon.create_components:
|
||||||
|
create_cost["components"][item.name] = item.quantity
|
||||||
|
costs = [create_cost] + costs
|
||||||
|
return costs
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args(None)
|
||||||
|
|
||||||
|
db = MHDB()
|
||||||
|
|
||||||
|
weapon = db.get_weapon_by_name(args.weapon, True)
|
||||||
|
if not weapon:
|
||||||
|
raise ValueError("Weapon '%s' not found" % name)
|
||||||
|
costs = get_costs(db, weapon)
|
||||||
|
if args.json:
|
||||||
|
for cost in costs:
|
||||||
|
cost["path"] = [dict(name=w.name, id=w.id)
|
||||||
|
for w in cost["path"]]
|
||||||
|
json.dump(costs, sys.stdout, cls=ModelJSONEncoder, indent=2)
|
||||||
|
else:
|
||||||
|
for cost in costs:
|
||||||
|
components = cost["components"]
|
||||||
|
print "=", ", ".join([w.name for w in cost["path"]])
|
||||||
|
print " Zenny", cost["zenny"]
|
||||||
|
for item_name in sorted(components.iterkeys()):
|
||||||
|
print "%20s %2d" % (item_name, components[item_name])
|
||||||
|
print
|
||||||
29
mhapi/db.py
29
mhapi/db.py
@@ -290,20 +290,31 @@ class MHDB(object):
|
|||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
""", model_cls=model.Weapon)
|
""", model_cls=model.Weapon)
|
||||||
|
|
||||||
def get_weapon(self, weapon_id):
|
def _add_components(self, item_data):
|
||||||
return self._query_one("weapon", """
|
ccomps = self.get_item_components(item_data.id, "Create")
|
||||||
|
ucomps = self.get_item_components(item_data.id, "Improve")
|
||||||
|
item_data.set_components(ccomps, ucomps)
|
||||||
|
|
||||||
|
def get_weapon(self, weapon_id, get_components=False):
|
||||||
|
weapon = self._query_one("weapon", """
|
||||||
SELECT * FROM weapons
|
SELECT * FROM weapons
|
||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
WHERE weapons._id=?
|
WHERE weapons._id=?
|
||||||
""", (weapon_id,), model_cls=model.Weapon)
|
""", (weapon_id,), model_cls=model.Weapon)
|
||||||
|
if weapon and get_components:
|
||||||
|
self._add_components(weapon)
|
||||||
|
return weapon
|
||||||
|
|
||||||
def get_weapon_by_name(self, name):
|
def get_weapon_by_name(self, name, get_components=False):
|
||||||
return self._query_one("weapon", """
|
weapon = self._query_one("weapon", """
|
||||||
SELECT items._id, items.name, items.buy, weapons.*
|
SELECT items._id, items.name, items.buy, weapons.*
|
||||||
FROM weapons
|
FROM weapons
|
||||||
LEFT JOIN items ON weapons._id = items._id
|
LEFT JOIN items ON weapons._id = items._id
|
||||||
WHERE items.name=?
|
WHERE items.name=?
|
||||||
""", (name,), model_cls=model.Weapon)
|
""", (name,), model_cls=model.Weapon)
|
||||||
|
if weapon and get_components:
|
||||||
|
self._add_components(weapon)
|
||||||
|
return weapon
|
||||||
|
|
||||||
def get_armors(self):
|
def get_armors(self):
|
||||||
return self._query_all("armors", """
|
return self._query_all("armors", """
|
||||||
@@ -427,3 +438,13 @@ class MHDB(object):
|
|||||||
WHERE monster_id=?
|
WHERE monster_id=?
|
||||||
AND (condition LIKE 'Break %' OR condition = 'Tail Carve')
|
AND (condition LIKE 'Break %' OR condition = 'Tail Carve')
|
||||||
""", (monster_id,), model_cls=model)
|
""", (monster_id,), model_cls=model)
|
||||||
|
|
||||||
|
def get_item_components(self, item_id, method="Create"):
|
||||||
|
return self._query_all("item_components", """
|
||||||
|
SELECT items._id, items.name, items.type,
|
||||||
|
components.quantity, components.type AS method
|
||||||
|
FROM components
|
||||||
|
LEFT JOIN items
|
||||||
|
ON items._id = components.component_item_id
|
||||||
|
WHERE created_item_id=? AND components.type=?
|
||||||
|
""", (item_id, method), model_cls=model.ItemComponent)
|
||||||
|
|||||||
@@ -152,13 +152,14 @@ class SharpnessLevel(EnumBase):
|
|||||||
PURPLE: "Purple",
|
PURPLE: "Purple",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# source: http://kiranico.com/en/mh4u/wiki/weapons
|
||||||
_modifier = {
|
_modifier = {
|
||||||
RED: (0.50, 0.25),
|
RED: (0.50, 0.25),
|
||||||
ORANGE: (0.75, 0.50),
|
ORANGE: (0.75, 0.50),
|
||||||
YELLOW: (1.00, 0.75),
|
YELLOW: (1.00, 0.75),
|
||||||
GREEN: (1.05, 1.00),
|
GREEN: (1.125, 1.00),
|
||||||
BLUE: (1.20, 1.06),
|
BLUE: (1.25, 1.0625),
|
||||||
WHITE: (1.32, 1.12),
|
WHITE: (1.32, 1.125),
|
||||||
PURPLE: (1.44, 1.20),
|
PURPLE: (1.44, 1.20),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,13 +304,19 @@ class SkillTree(RowModel):
|
|||||||
|
|
||||||
class Weapon(RowModel):
|
class Weapon(RowModel):
|
||||||
_list_fields = ["id", "wtype", "name"]
|
_list_fields = ["id", "wtype", "name"]
|
||||||
_indexes = { "name": ["id"],
|
_indexes = { "name": "id",
|
||||||
"wtype": ["id", "name"] }
|
"wtype": ["id", "name"] }
|
||||||
|
|
||||||
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()
|
||||||
|
self.create_components = []
|
||||||
|
self.upgrade_components = []
|
||||||
|
|
||||||
|
def set_components(self, create_components, upgrade_components):
|
||||||
|
self.create_components = create_components
|
||||||
|
self.upgrade_components = upgrade_components
|
||||||
|
|
||||||
def _parse_sharpness(self):
|
def _parse_sharpness(self):
|
||||||
"""
|
"""
|
||||||
@@ -327,6 +334,17 @@ class Weapon(RowModel):
|
|||||||
self._data["sharpness"] = WeaponSharpness(normal)
|
self._data["sharpness"] = WeaponSharpness(normal)
|
||||||
self._data["sharpness_plus"] = WeaponSharpness(plus)
|
self._data["sharpness_plus"] = WeaponSharpness(plus)
|
||||||
|
|
||||||
|
def as_data(self):
|
||||||
|
data = super(Weapon, self).as_data()
|
||||||
|
if self.create_components is not None:
|
||||||
|
data["create_components"] = dict((item.name, item.quantity)
|
||||||
|
for item in self.create_components)
|
||||||
|
if self.upgrade_components is not None:
|
||||||
|
data["upgrade_components"] = dict((item.name, item.quantity)
|
||||||
|
for item in self.upgrade_components)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Monster(RowModel):
|
class Monster(RowModel):
|
||||||
_list_fields = ["id", "class", "name"]
|
_list_fields = ["id", "class", "name"]
|
||||||
@@ -338,6 +356,12 @@ class Item(RowModel):
|
|||||||
"type": ["id", "name"] }
|
"type": ["id", "name"] }
|
||||||
|
|
||||||
|
|
||||||
|
class ItemComponent(RowModel):
|
||||||
|
_list_fields = ["id", "name"]
|
||||||
|
_indexes = { "method": ["id", "name"] }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Location(RowModel):
|
class Location(RowModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
23
web/templates/weaponpath.ejs
Normal file
23
web/templates/weaponpath.ejs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<fieldset>
|
||||||
|
<legend title="<%= path_string %>"
|
||||||
|
><%= path[0]["name"] %> (<%= path.length %>)</legend>
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2"><%= zenny %>z</td>
|
||||||
|
</tr>
|
||||||
|
<% for(var i=0; i<all_components.length; i++) { %>
|
||||||
|
<tr>
|
||||||
|
<% if (all_components[i] in components) { %>
|
||||||
|
<td class="<%= delta[all_components[i]] < 0 ? 'plus' :
|
||||||
|
delta[all_components[i]] > 0 ? 'minus' :
|
||||||
|
'neutral' %>"
|
||||||
|
><%= all_components[i] %></td>
|
||||||
|
<td class="num"><%= components[all_components[i]] %></td>
|
||||||
|
<% } else { %>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<% } %>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</table>
|
||||||
|
</fieldset>
|
||||||
124
web/weapontree.html
Normal file
124
web/weapontree.html
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Poogie's Weapon Tree</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>
|
||||||
|
|
||||||
|
<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var DATA_PATH = get_base_path() + "/jsonapi/";
|
||||||
|
|
||||||
|
var WEAPON_NAME_IDX = {};
|
||||||
|
|
||||||
|
var template_path = new EJS({ url: "templates/weaponpath.ejs" });
|
||||||
|
|
||||||
|
$(document).ready(function(){
|
||||||
|
$.getJSON(DATA_PATH + "weapon/_index_name.json",
|
||||||
|
function(data) {
|
||||||
|
WEAPON_NAME_IDX = data;
|
||||||
|
setup_autocomplete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function get_base_path() {
|
||||||
|
var path = document.location.pathname;
|
||||||
|
return path.substring(0, path.lastIndexOf('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setup_autocomplete() {
|
||||||
|
$("#weapon").autocomplete(
|
||||||
|
{ source: Object.keys(WEAPON_NAME_IDX),
|
||||||
|
change: function (event, ui) {
|
||||||
|
show_trees(ui.item["value"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$("#weapon").keypress(function(e) {
|
||||||
|
if (e.which == 13) {
|
||||||
|
show_trees($("#weapon").val());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_trees(weapon_name) {
|
||||||
|
weapon_id = WEAPON_NAME_IDX[weapon_name][0];
|
||||||
|
$("#results").html("");
|
||||||
|
$.getJSON(DATA_PATH + "weapon/" + weapon_id + "_tree.json",
|
||||||
|
function(data) {
|
||||||
|
all_components = Object.keys(
|
||||||
|
data[data.length-1]["components"]);
|
||||||
|
all_components.sort();
|
||||||
|
for (i=0; i<data.length; i++) {
|
||||||
|
delta = {};
|
||||||
|
path = data[i];
|
||||||
|
components = path["components"]
|
||||||
|
path["path_string"] = "";
|
||||||
|
for (j=0; j<path["path"].length; j++) {
|
||||||
|
if (j != 0) {
|
||||||
|
path["path_string"] += " -> ";
|
||||||
|
}
|
||||||
|
path["path_string"] += path["path"][j]["name"];
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
var html = template_path.render(path);
|
||||||
|
$("#results").append(html);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<label for="weapon">Weapon:</label>
|
||||||
|
<input id="weapon" type="text" size="20" />
|
||||||
|
<button id="search">Ask Poogie</button>
|
||||||
|
</div>
|
||||||
|
<div id="results" class="flexbox"></div>
|
||||||
|
</body>
|
||||||
|
</body>
|
||||||
Reference in New Issue
Block a user