Compare commits
10 Commits
85003665c9
...
webui_grai
| Author | SHA1 | Date | |
|---|---|---|---|
| 31e57da117 | |||
| 2baa43db20 | |||
| 3e2c481f6f | |||
| 1cb9ff63e7 | |||
| b8b79a0ea5 | |||
| 20fbdfdea9 | |||
| a8938264b0 | |||
| 8cca6e3464 | |||
| 9e7d69f667 | |||
| bd96f1e0ac |
28
contrib/skills.py
Normal file
28
contrib/skills.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import json
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
path = sys.argv[1] if len(sys.argv) >= 2 else "."
|
||||||
|
|
||||||
|
items = {}
|
||||||
|
|
||||||
|
item_patches = {
|
||||||
|
"tbk": {"class": "tome"},
|
||||||
|
"ibk": {"class": "tome"},
|
||||||
|
}
|
||||||
|
|
||||||
|
with open(os.path.join(path, "skills.json"), encoding="utf-8-sig") as f:
|
||||||
|
rows = json.load(f)
|
||||||
|
|
||||||
|
lookup_table = {}
|
||||||
|
for entry in rows:
|
||||||
|
key = entry["Key"]
|
||||||
|
text = entry["enUS"]
|
||||||
|
if len(text.strip()) == 0:
|
||||||
|
continue
|
||||||
|
lookup_table[key] = text
|
||||||
|
|
||||||
|
with open("skills.json", "w", newline="\n") as f:
|
||||||
|
json.dump(lookup_table, f, indent=4)
|
||||||
|
f.write("\n")
|
||||||
@@ -1,21 +1,36 @@
|
|||||||
import csv
|
import csv
|
||||||
import json
|
import json
|
||||||
import os
|
import argparse
|
||||||
import sys
|
from pathlib import Path
|
||||||
|
|
||||||
path = sys.argv[1] if len(sys.argv) >= 2 else "."
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Process unique and set items from game data"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"DATA_DIR", help="Path to d2 data dir containing local/ and global/"
|
||||||
|
)
|
||||||
|
parser.add_argument("OUTPUT_DIR", help="Path to destination directory")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
excelpath = Path(args.DATA_DIR) / "global/excel"
|
||||||
|
outputpath = Path(args.OUTPUT_DIR)
|
||||||
|
namespath = Path(args.DATA_DIR) / "local/lng/strings/item-names.json"
|
||||||
|
|
||||||
|
with namespath.open(encoding="utf-8-sig") as f:
|
||||||
|
names = json.load(f)
|
||||||
|
names = {name["Key"]: name["enUS"] for name in names}
|
||||||
|
|
||||||
category = "Base"
|
category = "Base"
|
||||||
setitems = {}
|
setitems = {}
|
||||||
with open(os.path.join(path, "setitems.txt")) as f:
|
with (excelpath / "setitems.txt").open() as f:
|
||||||
dr = csv.DictReader(f, delimiter="\t")
|
dr = csv.DictReader(f, delimiter="\t")
|
||||||
for row in dr:
|
for row in dr:
|
||||||
if row["index"] == "Expansion":
|
if row["index"] == "Expansion":
|
||||||
category = row["index"]
|
category = row["index"]
|
||||||
continue
|
continue
|
||||||
setitems[row["*ID"]] = {
|
setitems[row["*ID"]] = {
|
||||||
"name": row["index"],
|
"name": names[row["index"]],
|
||||||
"set": row["set"],
|
"set": names[row["set"]],
|
||||||
"itembase": row["item"],
|
"itembase": row["item"],
|
||||||
"req_lvl": int(row["lvl req"]),
|
"req_lvl": int(row["lvl req"]),
|
||||||
"ilvl": int(row["lvl"]),
|
"ilvl": int(row["lvl"]),
|
||||||
@@ -25,7 +40,7 @@ with open(os.path.join(path, "setitems.txt")) as f:
|
|||||||
|
|
||||||
category = "Base"
|
category = "Base"
|
||||||
uniqueitems = {}
|
uniqueitems = {}
|
||||||
with open(os.path.join(path, "uniqueitems.txt")) as f:
|
with (excelpath / "uniqueitems.txt").open() as f:
|
||||||
dr = csv.DictReader(f, delimiter="\t")
|
dr = csv.DictReader(f, delimiter="\t")
|
||||||
for row in dr:
|
for row in dr:
|
||||||
if row["index"] in [
|
if row["index"] in [
|
||||||
@@ -42,7 +57,7 @@ with open(os.path.join(path, "uniqueitems.txt")) as f:
|
|||||||
if len(row["lvl req"]) == 0:
|
if len(row["lvl req"]) == 0:
|
||||||
continue # deleted uniques
|
continue # deleted uniques
|
||||||
uniqueitems[row["*ID"]] = {
|
uniqueitems[row["*ID"]] = {
|
||||||
"name": row["index"],
|
"name": names[row["index"]],
|
||||||
"itembase": row["code"],
|
"itembase": row["code"],
|
||||||
"req_lvl": int(row["lvl req"]),
|
"req_lvl": int(row["lvl req"]),
|
||||||
"ilvl": int(row["lvl"]),
|
"ilvl": int(row["lvl"]),
|
||||||
@@ -50,10 +65,10 @@ with open(os.path.join(path, "uniqueitems.txt")) as f:
|
|||||||
"category": category,
|
"category": category,
|
||||||
}
|
}
|
||||||
|
|
||||||
with open("uniques.json", "w", newline="\n") as f:
|
with (outputpath / "uniques.json").open("w", newline="\n") as f:
|
||||||
json.dump(uniqueitems, f, indent=4)
|
json.dump(uniqueitems, f, indent=4)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|
||||||
with open("sets.json", "w", newline="\n") as f:
|
with (outputpath / "sets.json").open("w", newline="\n") as f:
|
||||||
json.dump(setitems, f, indent=4)
|
json.dump(setitems, f, indent=4)
|
||||||
f.write("\n")
|
f.write("\n")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from flask import Flask, redirect, abort, render_template, request
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
import psutil
|
import psutil
|
||||||
from d2warehouse.item import Item, Quality, lookup_basetype
|
from d2warehouse.item import Item, Quality, lookup_basetype
|
||||||
@@ -14,6 +15,7 @@ import re
|
|||||||
|
|
||||||
from d2warehouse.stash import StashFullError
|
from d2warehouse.stash import StashFullError
|
||||||
|
|
||||||
|
|
||||||
STASH_FILES = {
|
STASH_FILES = {
|
||||||
"softcore": "SharedStashSoftCoreV2.d2i",
|
"softcore": "SharedStashSoftCoreV2.d2i",
|
||||||
"hardcore": "SharedStashHardCoreV2.d2i",
|
"hardcore": "SharedStashHardCoreV2.d2i",
|
||||||
@@ -92,22 +94,26 @@ def storage_count(item: Item, stash: str) -> int | str:
|
|||||||
db = get_stash_db(stash)
|
db = get_stash_db(stash)
|
||||||
if item.is_simple:
|
if item.is_simple:
|
||||||
return db.execute(
|
return db.execute(
|
||||||
"SELECT COUNT(id) FROM item WHERE code = ? AND deleted IS NULL",
|
"SELECT COUNT(id) FROM item "
|
||||||
|
"WHERE code = ? AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
(item.code,),
|
(item.code,),
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
elif item.quality == Quality.UNIQUE:
|
elif item.quality == Quality.UNIQUE:
|
||||||
return db.execute(
|
return db.execute(
|
||||||
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id WHERE code = ? AND unique_id = ? AND deleted IS NULL",
|
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE code = ? AND unique_id = ? AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
(item.code, item.unique_id),
|
(item.code, item.unique_id),
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
elif item.quality == Quality.SET:
|
elif item.quality == Quality.SET:
|
||||||
return db.execute(
|
return db.execute(
|
||||||
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id WHERE code = ? AND set_id = ? AND deleted IS NULL",
|
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE code = ? AND set_id = ? AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
(item.code, item.set_id),
|
(item.code, item.set_id),
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
elif item.is_runeword:
|
elif item.is_runeword:
|
||||||
return db.execute(
|
return db.execute(
|
||||||
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id WHERE code = ? AND runeword_id = ? AND deleted IS NULL",
|
"SELECT COUNT(id) FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE code = ? AND runeword_id = ? AND deleted IS NULL and socketed_into IS NULL",
|
||||||
(item.code, item.runeword_id),
|
(item.code, item.runeword_id),
|
||||||
).fetchone()[0]
|
).fetchone()[0]
|
||||||
else:
|
else:
|
||||||
@@ -217,12 +223,17 @@ def list_storage(stash_name: str):
|
|||||||
|
|
||||||
db = get_stash_db(stash_name)
|
db = get_stash_db(stash_name)
|
||||||
items = {}
|
items = {}
|
||||||
rows = db.execute("SELECT id FROM item WHERE deleted IS NULL").fetchall()
|
rows = db.execute(
|
||||||
|
"SELECT id FROM item WHERE deleted IS NULL and socketed_into IS NULL"
|
||||||
|
).fetchall()
|
||||||
for row in rows:
|
for row in rows:
|
||||||
items[row["id"]] = Item.load_from_db(row["id"], db=db)
|
items[row["id"]] = Item.load_from_db(row["id"], db=db)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"list_storage.html", stash_name=stash_name, storage_items=items
|
"list_storage.html",
|
||||||
|
stash_name=stash_name,
|
||||||
|
storage_items=items,
|
||||||
|
storage_count=lambda x: storage_count(x, stash_name),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -235,16 +246,21 @@ def list_storage_category(stash_name: str, category: str):
|
|||||||
|
|
||||||
if category == "uniques":
|
if category == "uniques":
|
||||||
q = db.execute(
|
q = db.execute(
|
||||||
"SELECT id FROM item INNER JOIN item_extra ON id = item_id WHERE deleted IS NULL AND quality = ?",
|
"SELECT id FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE deleted IS NULL AND socketed_into IS NULL AND quality = ?",
|
||||||
(int(Quality.UNIQUE),),
|
(int(Quality.UNIQUE),),
|
||||||
)
|
)
|
||||||
elif category == "sets":
|
elif category == "sets":
|
||||||
q = db.execute(
|
q = db.execute(
|
||||||
"SELECT id FROM item INNER JOIN item_extra ON id = item_id WHERE deleted IS NULL AND quality = ?",
|
"SELECT id FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE deleted IS NULL AND socketed_into IS NULL AND quality = ?",
|
||||||
(int(Quality.SET),),
|
(int(Quality.SET),),
|
||||||
)
|
)
|
||||||
elif category == "misc":
|
elif category == "misc":
|
||||||
q = db.execute("SELECT id FROM item WHERE deleted IS NULL AND is_simple = TRUE")
|
q = db.execute(
|
||||||
|
"SELECT id FROM item "
|
||||||
|
"WHERE deleted IS NULL AND socketed_into IS NULL AND is_simple = TRUE"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
return "Unexpected category", 400
|
return "Unexpected category", 400
|
||||||
|
|
||||||
@@ -254,7 +270,11 @@ def list_storage_category(stash_name: str, category: str):
|
|||||||
items[row["id"]] = Item.load_from_db(row["id"], db=db)
|
items[row["id"]] = Item.load_from_db(row["id"], db=db)
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"list_storage.html", stash_name=stash_name, storage_items=items
|
"list_storage.html",
|
||||||
|
stash_name=stash_name,
|
||||||
|
storage_items=items,
|
||||||
|
category=category,
|
||||||
|
storage_count=lambda x: storage_count(x, stash_name),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -319,14 +339,16 @@ def storage_currency_counts(item_codes: list[str], stash_name: str) -> dict:
|
|||||||
for code in item_codes:
|
for code in item_codes:
|
||||||
currencies[code] = {
|
currencies[code] = {
|
||||||
"count": db.execute(
|
"count": db.execute(
|
||||||
"SELECT COUNT(id) FROM item WHERE code = ?", (code,)
|
"SELECT COUNT(id) FROM item "
|
||||||
|
"WHERE code = ? AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
|
(code,),
|
||||||
).fetchone()[0],
|
).fetchone()[0],
|
||||||
"name": lookup_basetype(code)["name"],
|
"name": lookup_basetype(code)["name"],
|
||||||
}
|
}
|
||||||
return currencies
|
return currencies
|
||||||
|
|
||||||
|
|
||||||
@app.route("/storage/<stash_name>/currency")
|
@app.route("/currency/<stash_name>")
|
||||||
def storage_currency(stash_name: str):
|
def storage_currency(stash_name: str):
|
||||||
if stash_name not in DB_FILES:
|
if stash_name not in DB_FILES:
|
||||||
abort(404)
|
abort(404)
|
||||||
@@ -337,3 +359,86 @@ def storage_currency(stash_name: str):
|
|||||||
return render_template(
|
return render_template(
|
||||||
"currency.html", runes=runes, gems=gems, keys=keys, essences=essences
|
"currency.html", runes=runes, gems=gems, keys=keys, essences=essences
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_uniques_data():
|
||||||
|
"""Return a sorted dictionary of unique item ids indexed by names."""
|
||||||
|
uniques_path = Path(__file__).resolve().parent.parent / "data/uniques.json"
|
||||||
|
with uniques_path.open() as f:
|
||||||
|
data = json.load(f)
|
||||||
|
name_to_id = {v["name"]: int(k) for k, v in data.items()}
|
||||||
|
del name_to_id["Amulet of the Viper"]
|
||||||
|
del name_to_id["Staff of Kings"]
|
||||||
|
del name_to_id["Horadric Staff"]
|
||||||
|
del name_to_id["Khalim's Flail"]
|
||||||
|
del name_to_id["Khalim's Will"]
|
||||||
|
del name_to_id["Hell Forge Hammer"]
|
||||||
|
return {name: name_to_id[name] for name in sorted(name_to_id.keys())}
|
||||||
|
|
||||||
|
|
||||||
|
def load_sets_data():
|
||||||
|
"""Return a sorted dictionary of unique item ids indexed by names."""
|
||||||
|
uniques_path = Path(__file__).resolve().parent.parent / "data/sets.json"
|
||||||
|
with uniques_path.open() as f:
|
||||||
|
data = json.load(f)
|
||||||
|
name_to_id = {v["name"]: int(k) for k, v in data.items()}
|
||||||
|
return {name: name_to_id[name] for name in sorted(name_to_id.keys())}
|
||||||
|
|
||||||
|
|
||||||
|
all_uniques = load_uniques_data()
|
||||||
|
all_sets = load_sets_data()
|
||||||
|
|
||||||
|
|
||||||
|
def get_found_uniques(db):
|
||||||
|
rows = db.execute(
|
||||||
|
"SELECT DISTINCT unique_id "
|
||||||
|
"FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE quality = 7 AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
|
).fetchall()
|
||||||
|
return {row["unique_id"]: True for row in rows}
|
||||||
|
|
||||||
|
|
||||||
|
def get_found_sets(db):
|
||||||
|
rows = db.execute(
|
||||||
|
"SELECT DISTINCT set_id "
|
||||||
|
"FROM item INNER JOIN item_extra ON id = item_id "
|
||||||
|
"WHERE quality = 5 AND deleted IS NULL AND socketed_into IS NULL",
|
||||||
|
).fetchall()
|
||||||
|
return {row["set_id"]: True for row in rows}
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/grail/<stash_name>")
|
||||||
|
def storage_grail(stash_name: str):
|
||||||
|
if stash_name not in DB_FILES:
|
||||||
|
abort(404)
|
||||||
|
db = get_stash_db(stash_name)
|
||||||
|
|
||||||
|
# Unique progress
|
||||||
|
found = get_found_uniques(db)
|
||||||
|
items = {name: found.get(id, False) for name, id in all_uniques.items()}
|
||||||
|
count = len(found)
|
||||||
|
total = len(all_uniques)
|
||||||
|
uniques = {
|
||||||
|
"list": items,
|
||||||
|
"count": count,
|
||||||
|
"total": total,
|
||||||
|
"progress": count / total * 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set progress
|
||||||
|
found = get_found_sets(db)
|
||||||
|
items = {name: found.get(id, False) for name, id in all_sets.items()}
|
||||||
|
count = len(found)
|
||||||
|
total = len(all_sets)
|
||||||
|
sets = {
|
||||||
|
"list": items,
|
||||||
|
"count": count,
|
||||||
|
"total": total,
|
||||||
|
"progress": count / total * 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"grail.html",
|
||||||
|
uniques=uniques,
|
||||||
|
sets=sets,
|
||||||
|
)
|
||||||
|
|||||||
@@ -11,3 +11,10 @@ function toggleSelectAll(tabIndex) {
|
|||||||
cb.checked = !allSelected;
|
cb.checked = !allSelected;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleCollected() {
|
||||||
|
const collected = document.querySelectorAll('.collected');
|
||||||
|
collected.forEach(item => {
|
||||||
|
item.classList.toggle('hidden');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,25 @@ body {
|
|||||||
color: rgb(240, 240, 240);
|
color: rgb(240, 240, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
display: block;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
color: rgb(240, 240, 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
.stash-tab {
|
.stash-tab {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(5, 1fr);
|
grid-template-columns: repeat(5, 1fr);
|
||||||
@@ -47,6 +66,27 @@ body {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.storage-item-entry {
|
||||||
|
position: relative;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-hover {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 30px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: #222;
|
||||||
|
border: 1px solid #555;
|
||||||
|
padding: 8px;
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.storage-item-entry:hover .item-hover {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.item .name {
|
.item .name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
@@ -83,3 +123,28 @@ input[type="checkbox"]:checked + label {
|
|||||||
background-color: #444;
|
background-color: #444;
|
||||||
color: rgb(240, 240, 240);
|
color: rgb(240, 240, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.grail {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.grail li {
|
||||||
|
flex: 0 0 15rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
width: 15rem;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collected {
|
||||||
|
background-color: #343;
|
||||||
|
}
|
||||||
|
.uncollected {
|
||||||
|
background-color: #433;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,10 +2,19 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Shared Stash</title>
|
<title>Currency</title>
|
||||||
<link rel="stylesheet" href="/static/style.css" />
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
<head>
|
<head>
|
||||||
<body>
|
<body>
|
||||||
|
{% include "menu.html" %}
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}">All</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/uniques">Uniques</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/sets">Sets</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/currency">Currency</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
<div class="currencies">
|
<div class="currencies">
|
||||||
<div>
|
<div>
|
||||||
<table>
|
<table>
|
||||||
|
|||||||
28
d2warehouse/app/templates/grail.html
Normal file
28
d2warehouse/app/templates/grail.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Grail</title>
|
||||||
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
|
<script src="/static/helpers.js"></script>
|
||||||
|
<head>
|
||||||
|
<body>
|
||||||
|
{% include "menu.html" %}
|
||||||
|
<button type="button" onclick="toggleCollected()">Toggle collected</button>
|
||||||
|
<h1>Unique Items</h1>
|
||||||
|
Progress: {{uniques.count}}/{{uniques.total}} ({{uniques.progress | round(1)}}%)
|
||||||
|
<ul class="grail">
|
||||||
|
{% for name,collected in uniques.list.items() %}
|
||||||
|
<li class="{{ 'collected' if collected else 'uncollected' }}">{{name}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
<h1>Set Items</h1>
|
||||||
|
Progress: {{sets.count}}/{{sets.total}} ({{sets.progress | round(1)}}%)
|
||||||
|
<ul class="grail">
|
||||||
|
{% for name,collected in sets.list.items() %}
|
||||||
|
<li class="{{ 'collected' if collected else 'uncollected' }}">{{name}}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
<input type="checkbox" id="item_{{tabloop.index0}}_{{itemloop.index0}}" name="item_{{tabloop.index0}}_{{itemloop.index0}}" value="remove" />
|
<ul>
|
||||||
<label class="item" for="item_{{tabloop.index0}}_{{itemloop.index0}}">
|
<li class="name color-{{item.color}}">{{item.name}}</li>
|
||||||
<ul>
|
{% if item.quality and item.quality >= 5 %}
|
||||||
<li class="name color-{{item.color}}">{{item.name}}</li>
|
<li class="name color-{{item.color}}">{{item.basename}}</li>
|
||||||
{% if item.quality and item.quality >= 5 %}
|
{% endif %}
|
||||||
<li class="name color-{{item.color}}">{{item.basename}}</li>
|
<li>(in storage: {{storage_count(item)}})</li>
|
||||||
{% endif %}
|
{% if item.stats %}
|
||||||
<li>(in storage: {{storage_count(item)}})</li>
|
{% for stat in item.stats %}
|
||||||
{% if item.stats %}
|
<li>{{stat}}</li>
|
||||||
{% for stat in item.stats %}
|
{% endfor %}
|
||||||
<li>{{stat}}</li>
|
{% endif %}
|
||||||
{% endfor %}
|
<li><input class="raw-item" type="text" name="raw item" value="{{item.raw().hex()}}" onfocus="this.select()" readonly></li>
|
||||||
{% endif %}
|
</ul>
|
||||||
<li><input class="raw-item" type="text" name="raw item" value="{{item.raw().hex()}}" onfocus="this.select()" readonly></li>
|
|
||||||
</ul>
|
|
||||||
</label>
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<script src="/static/helpers.js"></script>
|
<script src="/static/helpers.js"></script>
|
||||||
<head>
|
<head>
|
||||||
<body>
|
<body>
|
||||||
|
{% include "menu.html" %}
|
||||||
<form action="/stash/{{stash_name}}/store" method="POST">
|
<form action="/stash/{{stash_name}}/store" method="POST">
|
||||||
{% for tab in stash.tabs %}
|
{% for tab in stash.tabs %}
|
||||||
{% set tabloop = loop %}
|
{% set tabloop = loop %}
|
||||||
@@ -14,7 +15,10 @@
|
|||||||
<div class="stash-tab" data-tab="{{tabloop.index}}">
|
<div class="stash-tab" data-tab="{{tabloop.index}}">
|
||||||
{% for item in tab.items %}
|
{% for item in tab.items %}
|
||||||
{% set itemloop = loop %}
|
{% set itemloop = loop %}
|
||||||
|
<input type="checkbox" id="item_{{tabloop.index0}}_{{itemloop.index0}}" name="item_{{tabloop.index0}}_{{itemloop.index0}}" value="remove" />
|
||||||
|
<label class="item" for="item_{{tabloop.index0}}_{{itemloop.index0}}">
|
||||||
{% include "item.html" %}
|
{% include "item.html" %}
|
||||||
|
</label>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@@ -2,18 +2,30 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>Storage</title>
|
<title>Storage - {{category or 'all'}}</title>
|
||||||
<link rel="stylesheet" href="/static/style.css" />
|
<link rel="stylesheet" href="/static/style.css" />
|
||||||
<head>
|
<head>
|
||||||
<body>
|
<body>
|
||||||
|
{% include "menu.html" %}
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}">All</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/uniques">Uniques</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/sets">Sets</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}/misc">Misc</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
<form action="/storage/{{stash_name}}/take" method="POST">
|
<form action="/storage/{{stash_name}}/take" method="POST">
|
||||||
<div>
|
<div>
|
||||||
<!-- TODO: Include item.html -->
|
<!-- TODO: Include item.html -->
|
||||||
|
There are {{ storage_items | length }} items.
|
||||||
{% for db_id, item in storage_items.items() %}
|
{% for db_id, item in storage_items.items() %}
|
||||||
<div>
|
<div class="storage-item-entry">
|
||||||
<input type="checkbox" name="item_{{db_id}}" value="take" />
|
<input type="checkbox" name="item_{{db_id}}" id="item_{{db_id}}" value="take" />
|
||||||
{{item.name}}
|
<label for="item_{{db_id}}">{{item.name}} ({{db_id}})</label>
|
||||||
({{db_id}})
|
<div class="item-hover">
|
||||||
|
{% include "item.html" %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
8
d2warehouse/app/templates/menu.html
Normal file
8
d2warehouse/app/templates/menu.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/stash/{{stash_name or 'softcore'}}">Stash</a></li>
|
||||||
|
<li><a href="/storage/{{stash_name or 'softcore'}}">Storage</a></li>
|
||||||
|
<li><a href="/grail/{{stash_name or 'softcore'}}">Grail</a></li>
|
||||||
|
<li><a href="/currency/{{stash_name or 'softcore'}}">Currency</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
@@ -397,7 +397,7 @@
|
|||||||
},
|
},
|
||||||
"44": {
|
"44": {
|
||||||
"name": "Berserker's Headgear",
|
"name": "Berserker's Headgear",
|
||||||
"set": "Berserker's Garb",
|
"set": "Berserker's Arsenal",
|
||||||
"itembase": "hlm",
|
"itembase": "hlm",
|
||||||
"req_lvl": 3,
|
"req_lvl": 3,
|
||||||
"ilvl": 5,
|
"ilvl": 5,
|
||||||
@@ -406,7 +406,7 @@
|
|||||||
},
|
},
|
||||||
"45": {
|
"45": {
|
||||||
"name": "Berserker's Hauberk",
|
"name": "Berserker's Hauberk",
|
||||||
"set": "Berserker's Garb",
|
"set": "Berserker's Arsenal",
|
||||||
"itembase": "spl",
|
"itembase": "spl",
|
||||||
"req_lvl": 3,
|
"req_lvl": 3,
|
||||||
"ilvl": 5,
|
"ilvl": 5,
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
},
|
},
|
||||||
"46": {
|
"46": {
|
||||||
"name": "Berserker's Hatchet",
|
"name": "Berserker's Hatchet",
|
||||||
"set": "Berserker's Garb",
|
"set": "Berserker's Arsenal",
|
||||||
"itembase": "2ax",
|
"itembase": "2ax",
|
||||||
"req_lvl": 3,
|
"req_lvl": 3,
|
||||||
"ilvl": 5,
|
"ilvl": 5,
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
},
|
},
|
||||||
"50": {
|
"50": {
|
||||||
"name": "Angelic Sickle",
|
"name": "Angelic Sickle",
|
||||||
"set": "Angelical Raiment",
|
"set": "Angelic Raiment",
|
||||||
"itembase": "sbr",
|
"itembase": "sbr",
|
||||||
"req_lvl": 12,
|
"req_lvl": 12,
|
||||||
"ilvl": 17,
|
"ilvl": 17,
|
||||||
@@ -460,7 +460,7 @@
|
|||||||
},
|
},
|
||||||
"51": {
|
"51": {
|
||||||
"name": "Angelic Mantle",
|
"name": "Angelic Mantle",
|
||||||
"set": "Angelical Raiment",
|
"set": "Angelic Raiment",
|
||||||
"itembase": "rng",
|
"itembase": "rng",
|
||||||
"req_lvl": 12,
|
"req_lvl": 12,
|
||||||
"ilvl": 17,
|
"ilvl": 17,
|
||||||
@@ -469,7 +469,7 @@
|
|||||||
},
|
},
|
||||||
"52": {
|
"52": {
|
||||||
"name": "Angelic Halo",
|
"name": "Angelic Halo",
|
||||||
"set": "Angelical Raiment",
|
"set": "Angelic Raiment",
|
||||||
"itembase": "rin",
|
"itembase": "rin",
|
||||||
"req_lvl": 12,
|
"req_lvl": 12,
|
||||||
"ilvl": 17,
|
"ilvl": 17,
|
||||||
@@ -478,7 +478,7 @@
|
|||||||
},
|
},
|
||||||
"53": {
|
"53": {
|
||||||
"name": "Angelic Wings",
|
"name": "Angelic Wings",
|
||||||
"set": "Angelical Raiment",
|
"set": "Angelic Raiment",
|
||||||
"itembase": "amu",
|
"itembase": "amu",
|
||||||
"req_lvl": 12,
|
"req_lvl": 12,
|
||||||
"ilvl": 17,
|
"ilvl": 17,
|
||||||
@@ -612,7 +612,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"68": {
|
"68": {
|
||||||
"name": "Aldur's Gauntlet",
|
"name": "Aldur's Rhythm",
|
||||||
"set": "Aldur's Watchtower",
|
"set": "Aldur's Watchtower",
|
||||||
"itembase": "9mt",
|
"itembase": "9mt",
|
||||||
"req_lvl": 42,
|
"req_lvl": 42,
|
||||||
@@ -684,7 +684,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"76": {
|
"76": {
|
||||||
"name": "Tal Rasha's Fire-Spun Cloth",
|
"name": "Tal Rasha's Fine-Spun Cloth",
|
||||||
"set": "Tal Rasha's Wrappings",
|
"set": "Tal Rasha's Wrappings",
|
||||||
"itembase": "zmb",
|
"itembase": "zmb",
|
||||||
"req_lvl": 53,
|
"req_lvl": 53,
|
||||||
@@ -711,7 +711,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"79": {
|
"79": {
|
||||||
"name": "Tal Rasha's Howling Wind",
|
"name": "Tal Rasha's Guardianship",
|
||||||
"set": "Tal Rasha's Wrappings",
|
"set": "Tal Rasha's Wrappings",
|
||||||
"itembase": "uth",
|
"itembase": "uth",
|
||||||
"req_lvl": 71,
|
"req_lvl": 71,
|
||||||
@@ -747,7 +747,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"83": {
|
"83": {
|
||||||
"name": "Griswolds's Redemption",
|
"name": "Griswold's Redemption",
|
||||||
"set": "Griswold's Legacy",
|
"set": "Griswold's Legacy",
|
||||||
"itembase": "7ws",
|
"itembase": "7ws",
|
||||||
"req_lvl": 53,
|
"req_lvl": 53,
|
||||||
@@ -882,7 +882,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"98": {
|
"98": {
|
||||||
"name": "Spiritual Custodian",
|
"name": "Dark Adherent",
|
||||||
"set": "The Disciple",
|
"set": "The Disciple",
|
||||||
"itembase": "uui",
|
"itembase": "uui",
|
||||||
"req_lvl": 43,
|
"req_lvl": 43,
|
||||||
@@ -909,7 +909,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"101": {
|
"101": {
|
||||||
"name": "Heaven's Taebaek",
|
"name": "Taebaek's Glory",
|
||||||
"set": "Heaven's Brethren",
|
"set": "Heaven's Brethren",
|
||||||
"itembase": "uts",
|
"itembase": "uts",
|
||||||
"req_lvl": 81,
|
"req_lvl": 81,
|
||||||
@@ -918,7 +918,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"102": {
|
"102": {
|
||||||
"name": "Haemosu's Adament",
|
"name": "Haemosu's Adamant",
|
||||||
"set": "Heaven's Brethren",
|
"set": "Heaven's Brethren",
|
||||||
"itembase": "xrs",
|
"itembase": "xrs",
|
||||||
"req_lvl": 44,
|
"req_lvl": 44,
|
||||||
@@ -963,7 +963,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"107": {
|
"107": {
|
||||||
"name": "Wihtstan's Guard",
|
"name": "Whitstan's Guard",
|
||||||
"set": "Orphan's Call",
|
"set": "Orphan's Call",
|
||||||
"itembase": "xml",
|
"itembase": "xml",
|
||||||
"req_lvl": 29,
|
"req_lvl": 29,
|
||||||
@@ -990,7 +990,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"110": {
|
"110": {
|
||||||
"name": "Hwanin's Seal",
|
"name": "Hwanin's Blessing",
|
||||||
"set": "Hwanin's Majesty",
|
"set": "Hwanin's Majesty",
|
||||||
"itembase": "mbl",
|
"itembase": "mbl",
|
||||||
"req_lvl": 35,
|
"req_lvl": 35,
|
||||||
@@ -1071,7 +1071,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"119": {
|
"119": {
|
||||||
"name": "Cow King's Hoofs",
|
"name": "Cow King's Hooves",
|
||||||
"set": "Cow King's Leathers",
|
"set": "Cow King's Leathers",
|
||||||
"itembase": "vbt",
|
"itembase": "vbt",
|
||||||
"req_lvl": 13,
|
"req_lvl": 13,
|
||||||
@@ -1081,7 +1081,7 @@
|
|||||||
},
|
},
|
||||||
"120": {
|
"120": {
|
||||||
"name": "Naj's Puzzler",
|
"name": "Naj's Puzzler",
|
||||||
"set": "Naj's Ancient Set",
|
"set": "Naj's Ancient Vestige",
|
||||||
"itembase": "6cs",
|
"itembase": "6cs",
|
||||||
"req_lvl": 78,
|
"req_lvl": 78,
|
||||||
"ilvl": 43,
|
"ilvl": 43,
|
||||||
@@ -1090,7 +1090,7 @@
|
|||||||
},
|
},
|
||||||
"121": {
|
"121": {
|
||||||
"name": "Naj's Light Plate",
|
"name": "Naj's Light Plate",
|
||||||
"set": "Naj's Ancient Set",
|
"set": "Naj's Ancient Vestige",
|
||||||
"itembase": "ult",
|
"itembase": "ult",
|
||||||
"req_lvl": 71,
|
"req_lvl": 71,
|
||||||
"ilvl": 43,
|
"ilvl": 43,
|
||||||
@@ -1099,7 +1099,7 @@
|
|||||||
},
|
},
|
||||||
"122": {
|
"122": {
|
||||||
"name": "Naj's Circlet",
|
"name": "Naj's Circlet",
|
||||||
"set": "Naj's Ancient Set",
|
"set": "Naj's Ancient Vestige",
|
||||||
"itembase": "ci0",
|
"itembase": "ci0",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 43,
|
"ilvl": 43,
|
||||||
@@ -1107,8 +1107,8 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"123": {
|
"123": {
|
||||||
"name": "McAuley's Paragon",
|
"name": "Sander's Paragon",
|
||||||
"set": "McAuley's Folly",
|
"set": "Sander's Folly",
|
||||||
"itembase": "cap",
|
"itembase": "cap",
|
||||||
"req_lvl": 25,
|
"req_lvl": 25,
|
||||||
"ilvl": 20,
|
"ilvl": 20,
|
||||||
@@ -1116,8 +1116,8 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"124": {
|
"124": {
|
||||||
"name": "McAuley's Riprap",
|
"name": "Sander's Riprap",
|
||||||
"set": "McAuley's Folly",
|
"set": "Sander's Folly",
|
||||||
"itembase": "vbt",
|
"itembase": "vbt",
|
||||||
"req_lvl": 20,
|
"req_lvl": 20,
|
||||||
"ilvl": 20,
|
"ilvl": 20,
|
||||||
@@ -1125,8 +1125,8 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"125": {
|
"125": {
|
||||||
"name": "McAuley's Taboo",
|
"name": "Sander's Taboo",
|
||||||
"set": "McAuley's Folly",
|
"set": "Sander's Folly",
|
||||||
"itembase": "vgl",
|
"itembase": "vgl",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 20,
|
"ilvl": 20,
|
||||||
@@ -1134,8 +1134,8 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"126": {
|
"126": {
|
||||||
"name": "McAuley's Superstition",
|
"name": "Sander's Superstition",
|
||||||
"set": "McAuley's Folly",
|
"set": "Sander's Folly",
|
||||||
"itembase": "bwn",
|
"itembase": "bwn",
|
||||||
"req_lvl": 25,
|
"req_lvl": 25,
|
||||||
"ilvl": 20,
|
"ilvl": 20,
|
||||||
|
|||||||
1251
d2warehouse/data/skills.json
Normal file
1251
d2warehouse/data/skills.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"name": "Mindrend",
|
"name": "Skull Splitter",
|
||||||
"itembase": "mpi",
|
"itembase": "mpi",
|
||||||
"req_lvl": 21,
|
"req_lvl": 21,
|
||||||
"ilvl": 28,
|
"ilvl": 28,
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"5": {
|
"5": {
|
||||||
"name": "Fechmars Axe",
|
"name": "Axe of Fechmar",
|
||||||
"itembase": "lax",
|
"itembase": "lax",
|
||||||
"req_lvl": 8,
|
"req_lvl": 8,
|
||||||
"ilvl": 11,
|
"ilvl": 11,
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"7": {
|
"7": {
|
||||||
"name": "The Chieftan",
|
"name": "The Chieftain",
|
||||||
"itembase": "btx",
|
"itembase": "btx",
|
||||||
"req_lvl": 19,
|
"req_lvl": 19,
|
||||||
"ilvl": 26,
|
"ilvl": 26,
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"9": {
|
"9": {
|
||||||
"name": "The Humongous",
|
"name": "Humongous",
|
||||||
"itembase": "gix",
|
"itembase": "gix",
|
||||||
"req_lvl": 29,
|
"req_lvl": 29,
|
||||||
"ilvl": 39,
|
"ilvl": 39,
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"10": {
|
"10": {
|
||||||
"name": "Iros Torch",
|
"name": "Torch of Iro",
|
||||||
"itembase": "wnd",
|
"itembase": "wnd",
|
||||||
"req_lvl": 5,
|
"req_lvl": 5,
|
||||||
"ilvl": 7,
|
"ilvl": 7,
|
||||||
@@ -88,7 +88,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"11": {
|
"11": {
|
||||||
"name": "Maelstromwrath",
|
"name": "Maelstrom",
|
||||||
"itembase": "ywn",
|
"itembase": "ywn",
|
||||||
"req_lvl": 14,
|
"req_lvl": 14,
|
||||||
"ilvl": 19,
|
"ilvl": 19,
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"13": {
|
"13": {
|
||||||
"name": "Umes Lament",
|
"name": "Ume's Lament",
|
||||||
"itembase": "gwn",
|
"itembase": "gwn",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 38,
|
"ilvl": 38,
|
||||||
@@ -168,7 +168,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"21": {
|
"21": {
|
||||||
"name": "The Generals Tan Do Li Ga",
|
"name": "The General's Tan Do Li Ga",
|
||||||
"itembase": "fla",
|
"itembase": "fla",
|
||||||
"req_lvl": 21,
|
"req_lvl": 21,
|
||||||
"ilvl": 28,
|
"ilvl": 28,
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"23": {
|
"23": {
|
||||||
"name": "Bonesob",
|
"name": "Bonesnap",
|
||||||
"itembase": "mau",
|
"itembase": "mau",
|
||||||
"req_lvl": 24,
|
"req_lvl": 24,
|
||||||
"ilvl": 32,
|
"ilvl": 32,
|
||||||
@@ -200,7 +200,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"25": {
|
"25": {
|
||||||
"name": "Rixots Keen",
|
"name": "Rixot's Keen",
|
||||||
"itembase": "ssd",
|
"itembase": "ssd",
|
||||||
"req_lvl": 2,
|
"req_lvl": 2,
|
||||||
"ilvl": 3,
|
"ilvl": 3,
|
||||||
@@ -216,7 +216,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"27": {
|
"27": {
|
||||||
"name": "Krintizs Skewer",
|
"name": "Skewer of Krintiz",
|
||||||
"itembase": "sbr",
|
"itembase": "sbr",
|
||||||
"req_lvl": 10,
|
"req_lvl": 10,
|
||||||
"ilvl": 14,
|
"ilvl": 14,
|
||||||
@@ -240,7 +240,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"30": {
|
"30": {
|
||||||
"name": "Griswolds Edge",
|
"name": "Griswold's Edge",
|
||||||
"itembase": "bsd",
|
"itembase": "bsd",
|
||||||
"req_lvl": 17,
|
"req_lvl": 17,
|
||||||
"ilvl": 23,
|
"ilvl": 23,
|
||||||
@@ -256,7 +256,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"32": {
|
"32": {
|
||||||
"name": "Culwens Point",
|
"name": "Culwen's Point",
|
||||||
"itembase": "wsd",
|
"itembase": "wsd",
|
||||||
"req_lvl": 29,
|
"req_lvl": 29,
|
||||||
"ilvl": 39,
|
"ilvl": 39,
|
||||||
@@ -280,7 +280,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"35": {
|
"35": {
|
||||||
"name": "Kinemils Awl",
|
"name": "Kinemil's Awl",
|
||||||
"itembase": "gis",
|
"itembase": "gis",
|
||||||
"req_lvl": 23,
|
"req_lvl": 23,
|
||||||
"ilvl": 31,
|
"ilvl": 31,
|
||||||
@@ -336,7 +336,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"42": {
|
"42": {
|
||||||
"name": "Irices Shard",
|
"name": "Spectral Shard",
|
||||||
"itembase": "bld",
|
"itembase": "bld",
|
||||||
"req_lvl": 25,
|
"req_lvl": 25,
|
||||||
"ilvl": 34,
|
"ilvl": 34,
|
||||||
@@ -384,7 +384,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"48": {
|
"48": {
|
||||||
"name": "Dimoaks Hew",
|
"name": "Dimoak's Hew",
|
||||||
"itembase": "bar",
|
"itembase": "bar",
|
||||||
"req_lvl": 8,
|
"req_lvl": 8,
|
||||||
"ilvl": 11,
|
"ilvl": 11,
|
||||||
@@ -448,7 +448,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"56": {
|
"56": {
|
||||||
"name": "Lazarus Spire",
|
"name": "Spire of Lazarus",
|
||||||
"itembase": "cst",
|
"itembase": "cst",
|
||||||
"req_lvl": 18,
|
"req_lvl": 18,
|
||||||
"ilvl": 24,
|
"ilvl": 24,
|
||||||
@@ -488,7 +488,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"61": {
|
"61": {
|
||||||
"name": "Rimeraven",
|
"name": "Raven Claw",
|
||||||
"itembase": "lbw",
|
"itembase": "lbw",
|
||||||
"req_lvl": 15,
|
"req_lvl": 15,
|
||||||
"ilvl": 20,
|
"ilvl": 20,
|
||||||
@@ -496,7 +496,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"62": {
|
"62": {
|
||||||
"name": "Piercerib",
|
"name": "Rogue's Bow",
|
||||||
"itembase": "cbw",
|
"itembase": "cbw",
|
||||||
"req_lvl": 20,
|
"req_lvl": 20,
|
||||||
"ilvl": 27,
|
"ilvl": 27,
|
||||||
@@ -504,7 +504,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"63": {
|
"63": {
|
||||||
"name": "Pullspite",
|
"name": "Stormstrike",
|
||||||
"itembase": "sbb",
|
"itembase": "sbb",
|
||||||
"req_lvl": 25,
|
"req_lvl": 25,
|
||||||
"ilvl": 34,
|
"ilvl": 34,
|
||||||
@@ -560,7 +560,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"70": {
|
"70": {
|
||||||
"name": "Doomspittle",
|
"name": "Doomslinger",
|
||||||
"itembase": "rxb",
|
"itembase": "rxb",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 38,
|
"ilvl": 38,
|
||||||
@@ -568,7 +568,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"71": {
|
"71": {
|
||||||
"name": "War Bonnet",
|
"name": "Biggin's Bonnet",
|
||||||
"itembase": "cap",
|
"itembase": "cap",
|
||||||
"req_lvl": 3,
|
"req_lvl": 3,
|
||||||
"ilvl": 4,
|
"ilvl": 4,
|
||||||
@@ -640,7 +640,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"80": {
|
"80": {
|
||||||
"name": "Blinkbats Form",
|
"name": "Blinkbat's Form",
|
||||||
"itembase": "lea",
|
"itembase": "lea",
|
||||||
"req_lvl": 12,
|
"req_lvl": 12,
|
||||||
"ilvl": 16,
|
"ilvl": 16,
|
||||||
@@ -688,7 +688,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"86": {
|
"86": {
|
||||||
"name": "Venomsward",
|
"name": "Venom Ward",
|
||||||
"itembase": "brs",
|
"itembase": "brs",
|
||||||
"req_lvl": 20,
|
"req_lvl": 20,
|
||||||
"ilvl": 27,
|
"ilvl": 27,
|
||||||
@@ -736,7 +736,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"92": {
|
"92": {
|
||||||
"name": "Victors Silk",
|
"name": "Silks of the Victor",
|
||||||
"itembase": "aar",
|
"itembase": "aar",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 38,
|
"ilvl": 38,
|
||||||
@@ -896,7 +896,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"112": {
|
"112": {
|
||||||
"name": "Lenyms Cord",
|
"name": "Lenymo",
|
||||||
"itembase": "lbl",
|
"itembase": "lbl",
|
||||||
"req_lvl": 7,
|
"req_lvl": 7,
|
||||||
"ilvl": 10,
|
"ilvl": 10,
|
||||||
@@ -1016,7 +1016,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"127": {
|
"127": {
|
||||||
"name": "KhalimFlail",
|
"name": "Khalim's Flail",
|
||||||
"itembase": "qf1",
|
"itembase": "qf1",
|
||||||
"req_lvl": 0,
|
"req_lvl": 0,
|
||||||
"ilvl": 0,
|
"ilvl": 0,
|
||||||
@@ -1024,7 +1024,7 @@
|
|||||||
"category": "Base"
|
"category": "Base"
|
||||||
},
|
},
|
||||||
"128": {
|
"128": {
|
||||||
"name": "SuperKhalimFlail",
|
"name": "Khalim's Will",
|
||||||
"itembase": "qf2",
|
"itembase": "qf2",
|
||||||
"req_lvl": 0,
|
"req_lvl": 0,
|
||||||
"ilvl": 0,
|
"ilvl": 0,
|
||||||
@@ -1056,7 +1056,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"132": {
|
"132": {
|
||||||
"name": "Pompe's Wrath",
|
"name": "Pompeii's Wrath",
|
||||||
"itembase": "9mp",
|
"itembase": "9mp",
|
||||||
"req_lvl": 45,
|
"req_lvl": 45,
|
||||||
"ilvl": 53,
|
"ilvl": 53,
|
||||||
@@ -1104,7 +1104,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"138": {
|
"138": {
|
||||||
"name": "The Minataur",
|
"name": "The Minotaur",
|
||||||
"itembase": "9gi",
|
"itembase": "9gi",
|
||||||
"req_lvl": 45,
|
"req_lvl": 45,
|
||||||
"ilvl": 53,
|
"ilvl": 53,
|
||||||
@@ -1288,7 +1288,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"161": {
|
"161": {
|
||||||
"name": "The Atlantian",
|
"name": "The Atlantean",
|
||||||
"itembase": "9wd",
|
"itembase": "9wd",
|
||||||
"req_lvl": 42,
|
"req_lvl": 42,
|
||||||
"ilvl": 50,
|
"ilvl": 50,
|
||||||
@@ -1496,7 +1496,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"187": {
|
"187": {
|
||||||
"name": "Skullcollector",
|
"name": "Skull Collector",
|
||||||
"itembase": "8ws",
|
"itembase": "8ws",
|
||||||
"req_lvl": 41,
|
"req_lvl": 41,
|
||||||
"ilvl": 49,
|
"ilvl": 49,
|
||||||
@@ -1536,7 +1536,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"192": {
|
"192": {
|
||||||
"name": "Whichwild String",
|
"name": "Witchwild String",
|
||||||
"itembase": "8s8",
|
"itembase": "8s8",
|
||||||
"req_lvl": 39,
|
"req_lvl": 39,
|
||||||
"ilvl": 47,
|
"ilvl": 47,
|
||||||
@@ -1560,7 +1560,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"195": {
|
"195": {
|
||||||
"name": "Godstrike Arch",
|
"name": "Goldstrike Arch",
|
||||||
"itembase": "8lw",
|
"itembase": "8lw",
|
||||||
"req_lvl": 46,
|
"req_lvl": 46,
|
||||||
"ilvl": 54,
|
"ilvl": 54,
|
||||||
@@ -1576,7 +1576,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"197": {
|
"197": {
|
||||||
"name": "Pus Spiter",
|
"name": "Pus Spitter",
|
||||||
"itembase": "8mx",
|
"itembase": "8mx",
|
||||||
"req_lvl": 36,
|
"req_lvl": 36,
|
||||||
"ilvl": 44,
|
"ilvl": 44,
|
||||||
@@ -1600,7 +1600,7 @@
|
|||||||
"category": "Expansion"
|
"category": "Expansion"
|
||||||
},
|
},
|
||||||
"201": {
|
"201": {
|
||||||
"name": "Peasent Crown",
|
"name": "Peasant Crown",
|
||||||
"itembase": "xap",
|
"itembase": "xap",
|
||||||
"req_lvl": 28,
|
"req_lvl": 28,
|
||||||
"ilvl": 36,
|
"ilvl": 36,
|
||||||
@@ -1632,7 +1632,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"205": {
|
"205": {
|
||||||
"name": "Valkiry Wing",
|
"name": "Valkyrie Wing",
|
||||||
"itembase": "xhm",
|
"itembase": "xhm",
|
||||||
"req_lvl": 44,
|
"req_lvl": 44,
|
||||||
"ilvl": 52,
|
"ilvl": 52,
|
||||||
@@ -1656,7 +1656,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"208": {
|
"208": {
|
||||||
"name": "Vampiregaze",
|
"name": "Vampire Gaze",
|
||||||
"itembase": "xh9",
|
"itembase": "xh9",
|
||||||
"req_lvl": 41,
|
"req_lvl": 41,
|
||||||
"ilvl": 49,
|
"ilvl": 49,
|
||||||
@@ -1680,7 +1680,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"211": {
|
"211": {
|
||||||
"name": "Skin of the Flayerd One",
|
"name": "Skin of the Flayed One",
|
||||||
"itembase": "xla",
|
"itembase": "xla",
|
||||||
"req_lvl": 31,
|
"req_lvl": 31,
|
||||||
"ilvl": 39,
|
"ilvl": 39,
|
||||||
@@ -1688,7 +1688,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"212": {
|
"212": {
|
||||||
"name": "Ironpelt",
|
"name": "Iron Pelt",
|
||||||
"itembase": "xtu",
|
"itembase": "xtu",
|
||||||
"req_lvl": 33,
|
"req_lvl": 33,
|
||||||
"ilvl": 41,
|
"ilvl": 41,
|
||||||
@@ -1696,7 +1696,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"213": {
|
"213": {
|
||||||
"name": "Spiritforge",
|
"name": "Spirit Forge",
|
||||||
"itembase": "xng",
|
"itembase": "xng",
|
||||||
"req_lvl": 35,
|
"req_lvl": 35,
|
||||||
"ilvl": 43,
|
"ilvl": 43,
|
||||||
@@ -1776,7 +1776,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"223": {
|
"223": {
|
||||||
"name": "Que-Hegan's Wisdon",
|
"name": "Que-Hegan's Wisdom",
|
||||||
"itembase": "xtp",
|
"itembase": "xtp",
|
||||||
"req_lvl": 51,
|
"req_lvl": 51,
|
||||||
"ilvl": 59,
|
"ilvl": 59,
|
||||||
@@ -1792,7 +1792,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"225": {
|
"225": {
|
||||||
"name": "Mosers Blessed Circle",
|
"name": "Moser's Blessed Circle",
|
||||||
"itembase": "xml",
|
"itembase": "xml",
|
||||||
"req_lvl": 31,
|
"req_lvl": 31,
|
||||||
"ilvl": 39,
|
"ilvl": 39,
|
||||||
@@ -1816,7 +1816,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"228": {
|
"228": {
|
||||||
"name": "Kerke's Sanctuary",
|
"name": "Gerke's Sanctuary",
|
||||||
"itembase": "xow",
|
"itembase": "xow",
|
||||||
"req_lvl": 44,
|
"req_lvl": 44,
|
||||||
"ilvl": 52,
|
"ilvl": 52,
|
||||||
@@ -1824,7 +1824,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"229": {
|
"229": {
|
||||||
"name": "Radimant's Sphere",
|
"name": "Radament's Sphere",
|
||||||
"itembase": "xts",
|
"itembase": "xts",
|
||||||
"req_lvl": 50,
|
"req_lvl": 50,
|
||||||
"ilvl": 58,
|
"ilvl": 58,
|
||||||
@@ -1872,7 +1872,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"235": {
|
"235": {
|
||||||
"name": "Lavagout",
|
"name": "Lava Gout",
|
||||||
"itembase": "xtg",
|
"itembase": "xtg",
|
||||||
"req_lvl": 42,
|
"req_lvl": 42,
|
||||||
"ilvl": 50,
|
"ilvl": 50,
|
||||||
@@ -1912,7 +1912,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"240": {
|
"240": {
|
||||||
"name": "Wartraveler",
|
"name": "War Traveler",
|
||||||
"itembase": "xtb",
|
"itembase": "xtb",
|
||||||
"req_lvl": 42,
|
"req_lvl": 42,
|
||||||
"ilvl": 50,
|
"ilvl": 50,
|
||||||
@@ -1920,7 +1920,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"241": {
|
"241": {
|
||||||
"name": "Gorerider",
|
"name": "Gore Rider",
|
||||||
"itembase": "xhb",
|
"itembase": "xhb",
|
||||||
"req_lvl": 47,
|
"req_lvl": 47,
|
||||||
"ilvl": 55,
|
"ilvl": 55,
|
||||||
@@ -1944,7 +1944,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"244": {
|
"244": {
|
||||||
"name": "Gloomstrap",
|
"name": "Gloom's Trap",
|
||||||
"itembase": "zmb",
|
"itembase": "zmb",
|
||||||
"req_lvl": 36,
|
"req_lvl": 36,
|
||||||
"ilvl": 45,
|
"ilvl": 45,
|
||||||
@@ -1960,7 +1960,7 @@
|
|||||||
"category": "Armor"
|
"category": "Armor"
|
||||||
},
|
},
|
||||||
"246": {
|
"246": {
|
||||||
"name": "Thudergod's Vigor",
|
"name": "Thundergod's Vigor",
|
||||||
"itembase": "zhb",
|
"itembase": "zhb",
|
||||||
"req_lvl": 47,
|
"req_lvl": 47,
|
||||||
"ilvl": 55,
|
"ilvl": 55,
|
||||||
@@ -2120,7 +2120,7 @@
|
|||||||
"category": "Elite Uniques"
|
"category": "Elite Uniques"
|
||||||
},
|
},
|
||||||
"268": {
|
"268": {
|
||||||
"name": "Bul Katho's Wedding Band",
|
"name": "Bul-Kathos' Wedding Band",
|
||||||
"itembase": "rin",
|
"itembase": "rin",
|
||||||
"req_lvl": 58,
|
"req_lvl": 58,
|
||||||
"ilvl": 66,
|
"ilvl": 66,
|
||||||
@@ -2256,7 +2256,7 @@
|
|||||||
"category": "Class Specific"
|
"category": "Class Specific"
|
||||||
},
|
},
|
||||||
"286": {
|
"286": {
|
||||||
"name": "Cutthroat1",
|
"name": "Bartuc's Cut-Throat",
|
||||||
"itembase": "9tw",
|
"itembase": "9tw",
|
||||||
"req_lvl": 42,
|
"req_lvl": 42,
|
||||||
"ilvl": 50,
|
"ilvl": 50,
|
||||||
@@ -2288,7 +2288,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"290": {
|
"290": {
|
||||||
"name": "Djinnslayer",
|
"name": "Djinn Slayer",
|
||||||
"itembase": "7sm",
|
"itembase": "7sm",
|
||||||
"req_lvl": 65,
|
"req_lvl": 65,
|
||||||
"ilvl": 73,
|
"ilvl": 73,
|
||||||
@@ -2312,7 +2312,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"293": {
|
"293": {
|
||||||
"name": "Gutsiphon",
|
"name": "Gut Siphon",
|
||||||
"itembase": "6rx",
|
"itembase": "6rx",
|
||||||
"req_lvl": 71,
|
"req_lvl": 71,
|
||||||
"ilvl": 79,
|
"ilvl": 79,
|
||||||
@@ -2320,7 +2320,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"294": {
|
"294": {
|
||||||
"name": "Razoredge",
|
"name": "Razor's Edge",
|
||||||
"itembase": "7ha",
|
"itembase": "7ha",
|
||||||
"req_lvl": 67,
|
"req_lvl": 67,
|
||||||
"ilvl": 75,
|
"ilvl": 75,
|
||||||
@@ -2328,7 +2328,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"296": {
|
"296": {
|
||||||
"name": "Demonlimb",
|
"name": "Demon Limb",
|
||||||
"itembase": "7sp",
|
"itembase": "7sp",
|
||||||
"req_lvl": 63,
|
"req_lvl": 63,
|
||||||
"ilvl": 71,
|
"ilvl": 71,
|
||||||
@@ -2336,7 +2336,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"297": {
|
"297": {
|
||||||
"name": "Steelshade",
|
"name": "Steel Shade",
|
||||||
"itembase": "ulm",
|
"itembase": "ulm",
|
||||||
"req_lvl": 62,
|
"req_lvl": 62,
|
||||||
"ilvl": 70,
|
"ilvl": 70,
|
||||||
@@ -2352,7 +2352,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"299": {
|
"299": {
|
||||||
"name": "Deaths's Web",
|
"name": "Death's Web",
|
||||||
"itembase": "7gw",
|
"itembase": "7gw",
|
||||||
"req_lvl": 66,
|
"req_lvl": 66,
|
||||||
"ilvl": 74,
|
"ilvl": 74,
|
||||||
@@ -2408,7 +2408,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"308": {
|
"308": {
|
||||||
"name": "Jadetalon",
|
"name": "Jade Talon",
|
||||||
"itembase": "7wb",
|
"itembase": "7wb",
|
||||||
"req_lvl": 66,
|
"req_lvl": 66,
|
||||||
"ilvl": 74,
|
"ilvl": 74,
|
||||||
@@ -2416,7 +2416,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"309": {
|
"309": {
|
||||||
"name": "Shadowdancer",
|
"name": "Shadow Dancer",
|
||||||
"itembase": "uhb",
|
"itembase": "uhb",
|
||||||
"req_lvl": 71,
|
"req_lvl": 71,
|
||||||
"ilvl": 79,
|
"ilvl": 79,
|
||||||
@@ -2424,7 +2424,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"310": {
|
"310": {
|
||||||
"name": "Cerebus",
|
"name": "Cerebus' Bite",
|
||||||
"itembase": "drb",
|
"itembase": "drb",
|
||||||
"req_lvl": 63,
|
"req_lvl": 63,
|
||||||
"ilvl": 71,
|
"ilvl": 71,
|
||||||
@@ -2440,7 +2440,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"312": {
|
"312": {
|
||||||
"name": "Souldrain",
|
"name": "Soul Drainer",
|
||||||
"itembase": "umg",
|
"itembase": "umg",
|
||||||
"req_lvl": 74,
|
"req_lvl": 74,
|
||||||
"ilvl": 82,
|
"ilvl": 82,
|
||||||
@@ -2448,7 +2448,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"313": {
|
"313": {
|
||||||
"name": "Runemaster",
|
"name": "Rune Master",
|
||||||
"itembase": "72a",
|
"itembase": "72a",
|
||||||
"req_lvl": 72,
|
"req_lvl": 72,
|
||||||
"ilvl": 80,
|
"ilvl": 80,
|
||||||
@@ -2456,7 +2456,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"314": {
|
"314": {
|
||||||
"name": "Deathcleaver",
|
"name": "Death Cleaver",
|
||||||
"itembase": "7wa",
|
"itembase": "7wa",
|
||||||
"req_lvl": 70,
|
"req_lvl": 70,
|
||||||
"ilvl": 78,
|
"ilvl": 78,
|
||||||
@@ -2488,7 +2488,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"319": {
|
"319": {
|
||||||
"name": "Wisp",
|
"name": "Wisp Projector",
|
||||||
"itembase": "rin",
|
"itembase": "rin",
|
||||||
"req_lvl": 76,
|
"req_lvl": 76,
|
||||||
"ilvl": 84,
|
"ilvl": 84,
|
||||||
@@ -2552,7 +2552,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"327": {
|
"327": {
|
||||||
"name": "Spiritkeeper",
|
"name": "Spirit Keeper",
|
||||||
"itembase": "drd",
|
"itembase": "drd",
|
||||||
"req_lvl": 67,
|
"req_lvl": 67,
|
||||||
"ilvl": 75,
|
"ilvl": 75,
|
||||||
@@ -2576,7 +2576,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"330": {
|
"330": {
|
||||||
"name": "Darkforge Spawn",
|
"name": "Darkforce Spawn",
|
||||||
"itembase": "nef",
|
"itembase": "nef",
|
||||||
"req_lvl": 64,
|
"req_lvl": 64,
|
||||||
"ilvl": 72,
|
"ilvl": 72,
|
||||||
@@ -2592,7 +2592,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"332": {
|
"332": {
|
||||||
"name": "Bloodraven's Charge",
|
"name": "Blood Raven's Charge",
|
||||||
"itembase": "amb",
|
"itembase": "amb",
|
||||||
"req_lvl": 71,
|
"req_lvl": 71,
|
||||||
"ilvl": 79,
|
"ilvl": 79,
|
||||||
@@ -2608,7 +2608,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"334": {
|
"334": {
|
||||||
"name": "Shadowkiller",
|
"name": "Shadow Killer",
|
||||||
"itembase": "7cs",
|
"itembase": "7cs",
|
||||||
"req_lvl": 78,
|
"req_lvl": 78,
|
||||||
"ilvl": 85,
|
"ilvl": 85,
|
||||||
@@ -2664,7 +2664,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"342": {
|
"342": {
|
||||||
"name": "Steelpillar",
|
"name": "Steel Pillar",
|
||||||
"itembase": "7p7",
|
"itembase": "7p7",
|
||||||
"req_lvl": 69,
|
"req_lvl": 69,
|
||||||
"ilvl": 77,
|
"ilvl": 77,
|
||||||
@@ -2704,7 +2704,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"348": {
|
"348": {
|
||||||
"name": "Steel Carapice",
|
"name": "Steel Carapace",
|
||||||
"itembase": "uul",
|
"itembase": "uul",
|
||||||
"req_lvl": 66,
|
"req_lvl": 66,
|
||||||
"ilvl": 74,
|
"ilvl": 74,
|
||||||
@@ -2744,7 +2744,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"354": {
|
"354": {
|
||||||
"name": "Fathom",
|
"name": "Death's Fathom",
|
||||||
"itembase": "obf",
|
"itembase": "obf",
|
||||||
"req_lvl": 73,
|
"req_lvl": 73,
|
||||||
"ilvl": 81,
|
"ilvl": 81,
|
||||||
@@ -2840,7 +2840,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"367": {
|
"367": {
|
||||||
"name": "Eschuta's temper",
|
"name": "Eschuta's Temper",
|
||||||
"itembase": "obc",
|
"itembase": "obc",
|
||||||
"req_lvl": 72,
|
"req_lvl": 72,
|
||||||
"ilvl": 80,
|
"ilvl": 80,
|
||||||
@@ -2904,7 +2904,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"376": {
|
"376": {
|
||||||
"name": "Verdugo's Hearty Cord",
|
"name": "Verdungo's Hearty Cord",
|
||||||
"itembase": "umc",
|
"itembase": "umc",
|
||||||
"req_lvl": 63,
|
"req_lvl": 63,
|
||||||
"ilvl": 71,
|
"ilvl": 71,
|
||||||
@@ -2920,7 +2920,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"379": {
|
"379": {
|
||||||
"name": "Giantskull",
|
"name": "Giant Skull",
|
||||||
"itembase": "uh9",
|
"itembase": "uh9",
|
||||||
"req_lvl": 65,
|
"req_lvl": 65,
|
||||||
"ilvl": 73,
|
"ilvl": 73,
|
||||||
@@ -2928,7 +2928,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"380": {
|
"380": {
|
||||||
"name": "Ironward",
|
"name": "Astreon's Iron Ward",
|
||||||
"itembase": "7ws",
|
"itembase": "7ws",
|
||||||
"req_lvl": 60,
|
"req_lvl": 60,
|
||||||
"ilvl": 68,
|
"ilvl": 68,
|
||||||
@@ -2968,7 +2968,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"385": {
|
"385": {
|
||||||
"name": "Earthshifter",
|
"name": "Earth Shifter",
|
||||||
"itembase": "7gm",
|
"itembase": "7gm",
|
||||||
"req_lvl": 69,
|
"req_lvl": 69,
|
||||||
"ilvl": 77,
|
"ilvl": 77,
|
||||||
@@ -2976,7 +2976,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"386": {
|
"386": {
|
||||||
"name": "Wraithflight",
|
"name": "Wraith Flight",
|
||||||
"itembase": "7gl",
|
"itembase": "7gl",
|
||||||
"req_lvl": 76,
|
"req_lvl": 76,
|
||||||
"ilvl": 84,
|
"ilvl": 84,
|
||||||
@@ -3000,7 +3000,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"389": {
|
"389": {
|
||||||
"name": "The Reedeemer",
|
"name": "The Redeemer",
|
||||||
"itembase": "7sc",
|
"itembase": "7sc",
|
||||||
"req_lvl": 72,
|
"req_lvl": 72,
|
||||||
"ilvl": 80,
|
"ilvl": 80,
|
||||||
@@ -3008,7 +3008,7 @@
|
|||||||
"category": "Patch 1.10+"
|
"category": "Patch 1.10+"
|
||||||
},
|
},
|
||||||
"390": {
|
"390": {
|
||||||
"name": "Headhunter's Glory",
|
"name": "Head Hunter's Glory",
|
||||||
"itembase": "ush",
|
"itembase": "ush",
|
||||||
"req_lvl": 75,
|
"req_lvl": 75,
|
||||||
"ilvl": 83,
|
"ilvl": 83,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ _unique_map = None
|
|||||||
_set_item_map = None
|
_set_item_map = None
|
||||||
_runeword_map = None
|
_runeword_map = None
|
||||||
_affix_map = None
|
_affix_map = None
|
||||||
|
_skills_map = None
|
||||||
|
|
||||||
|
|
||||||
class Quality(IntEnum):
|
class Quality(IntEnum):
|
||||||
@@ -77,8 +78,18 @@ class Stat:
|
|||||||
for val in self.values:
|
for val in self.values:
|
||||||
subst_text = subst_text.replace("#", str(val), 1)
|
subst_text = subst_text.replace("#", str(val), 1)
|
||||||
if param:
|
if param:
|
||||||
subst_text = re.sub(r"\[[^\]]*\]", str(param), subst_text, 1)
|
subst_text = self.try_add_skill_text(subst_text)
|
||||||
return subst_text
|
return subst_text
|
||||||
|
|
||||||
|
def try_add_skill_text(self, subst_text: str) -> str | None:
|
||||||
|
if self.id == 107: # +X to [Skill] ([Class] only)
|
||||||
|
return re.sub(r"\[[^\]]*\]", lookup_skill_name(self.parameter), subst_text, 1)
|
||||||
|
elif self.id == 188: # +X to [Skill]
|
||||||
|
if '[' in subst_text:
|
||||||
|
return subst_text[:subst_text.find('[')] + lookup_random_skill_tab(self.parameter)
|
||||||
|
return re.sub(r"\[[^\]]*\]", lookup_random_skill_tab(self.parameter), subst_text, 1)
|
||||||
|
else:
|
||||||
|
return re.sub(r"\[[^\]]*\]", str(self.parameter), subst_text, 1)
|
||||||
|
|
||||||
|
|
||||||
def txtbits(bits: bitarray) -> str:
|
def txtbits(bits: bitarray) -> str:
|
||||||
@@ -546,3 +557,57 @@ def lookup_affix(id: int, prefix: bool) -> dict:
|
|||||||
with open(os.path.join(_data_path, "affixes.json")) as f:
|
with open(os.path.join(_data_path, "affixes.json")) as f:
|
||||||
_affix_map = json.load(f)
|
_affix_map = json.load(f)
|
||||||
return _affix_map["prefixes" if prefix else "suffixes"][str(id)]
|
return _affix_map["prefixes" if prefix else "suffixes"][str(id)]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_skills_map():
|
||||||
|
global _skills_map
|
||||||
|
if _skills_map is None:
|
||||||
|
with open(os.path.join(_data_path, "skills.json")) as f:
|
||||||
|
_skills_map = json.load(f)
|
||||||
|
return _skills_map
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_skill_name(id: int) -> str:
|
||||||
|
# FIXME: This hackish way of calculating the key is because we directly index into local/lng/strings/skills.json
|
||||||
|
# but the actual ID points to global/excel/skills.txt
|
||||||
|
skills_map = _get_skills_map()
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
return skills_map[f"Skillname{id + 1}"]
|
||||||
|
except KeyError:
|
||||||
|
return skills_map[f"skillname{id}"]
|
||||||
|
except KeyError:
|
||||||
|
return f"<Invalid key: skillname{id} or Skillname{id + 1}>"
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_random_skill_tab(id: int) -> str:
|
||||||
|
# (ClassId * 8) is the id of the first tab for that class
|
||||||
|
skills_map = _get_skills_map()
|
||||||
|
class_name = lookup_class(int(id / 8))
|
||||||
|
two_letter_class_code = lookup_class(int(id / 8))[:2]
|
||||||
|
tab_index = 3 - id % 8 # for some reason local/lng/strings/skills.json index backwards (0 -> 3, 1 -> 2, ...)
|
||||||
|
try:
|
||||||
|
return skills_map[f"SkillCategory{two_letter_class_code}{tab_index}"] + f" ({class_name} only)"
|
||||||
|
except KeyError:
|
||||||
|
return f"<Invalid random skill tab: {id}>"
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_class(id: int) -> str:
|
||||||
|
match id:
|
||||||
|
case 0:
|
||||||
|
return "Amazon"
|
||||||
|
case 1:
|
||||||
|
return "Sorceress"
|
||||||
|
case 2:
|
||||||
|
return "Necromancer"
|
||||||
|
case 3:
|
||||||
|
return "Paladin"
|
||||||
|
case 4:
|
||||||
|
return "Barbarian"
|
||||||
|
case 5:
|
||||||
|
return "Druid"
|
||||||
|
case 6:
|
||||||
|
return "Assassin"
|
||||||
|
case _:
|
||||||
|
# TODO: In 3.11 replace this with assert_never
|
||||||
|
assert False, f"{id} Should be unreachable"
|
||||||
|
|||||||
Reference in New Issue
Block a user