Compare commits
1 Commits
90f2601c1d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5a5755b6f |
@@ -1,4 +1,4 @@
|
|||||||
# OnlyScavs v0.1.1
|
# OnlyScavs v0.2
|
||||||
|
|
||||||
A personal Escape from Tarkov database and toolkit. The goal is to maintain a **local SQLite database that I fully control** — tarkov.dev is used only as a one-time (or on-demand) data source to seed it. Once imported, the local DB is the source of truth and can be edited, annotated, and extended freely without relying on any external API being up or accurate.
|
A personal Escape from Tarkov database and toolkit. The goal is to maintain a **local SQLite database that I fully control** — tarkov.dev is used only as a one-time (or on-demand) data source to seed it. Once imported, the local DB is the source of truth and can be edited, annotated, and extended freely without relying on any external API being up or accurate.
|
||||||
|
|
||||||
|
|||||||
61
app.py
61
app.py
@@ -1,7 +1,7 @@
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, jsonify
|
from flask import Flask, render_template, request, redirect, url_for, jsonify
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__, static_folder="assets", static_url_path="/assets")
|
||||||
DB_PATH = "tarkov.db"
|
DB_PATH = "tarkov.db"
|
||||||
|
|
||||||
|
|
||||||
@@ -444,16 +444,54 @@ def _sort_col(sort):
|
|||||||
"class_asc": "armor_class ASC NULLS LAST, weight_kg ASC NULLS LAST",
|
"class_asc": "armor_class ASC NULLS LAST, weight_kg ASC NULLS LAST",
|
||||||
"capacity_desc": "capacity DESC NULLS LAST, weight_kg ASC NULLS LAST",
|
"capacity_desc": "capacity DESC NULLS LAST, weight_kg ASC NULLS LAST",
|
||||||
"capacity_asc": "capacity ASC NULLS LAST, weight_kg ASC NULLS LAST",
|
"capacity_asc": "capacity ASC NULLS LAST, weight_kg ASC NULLS LAST",
|
||||||
|
# carry_efficiency sorts are handled in Python after query; fall back to weight
|
||||||
|
"efficiency_desc": "weight_kg ASC NULLS LAST",
|
||||||
|
"efficiency_asc": "weight_kg ASC NULLS LAST",
|
||||||
}.get(sort, "weight_kg ASC NULLS LAST")
|
}.get(sort, "weight_kg ASC NULLS LAST")
|
||||||
|
|
||||||
|
|
||||||
|
def _carry_efficiency(weight_kg, slot_count):
|
||||||
|
"""Return (slots_per_kg, kg_per_slot) or (None, None) if inputs are invalid."""
|
||||||
|
if not weight_kg or not slot_count:
|
||||||
|
return None, None
|
||||||
|
try:
|
||||||
|
w = float(weight_kg)
|
||||||
|
s = int(slot_count)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return None, None
|
||||||
|
if w <= 0 or s <= 0:
|
||||||
|
return None, None
|
||||||
|
return round(s / w, 2), round(w / s, 3)
|
||||||
|
|
||||||
|
|
||||||
|
def _enrich_with_efficiency(rows):
|
||||||
|
"""Attach slots_per_kg and kg_per_slot to each sqlite3.Row (returns plain dicts)."""
|
||||||
|
enriched = []
|
||||||
|
for row in rows:
|
||||||
|
d = dict(row)
|
||||||
|
d["slots_per_kg"], d["kg_per_slot"] = _carry_efficiency(
|
||||||
|
d.get("weight_kg"), d.get("capacity")
|
||||||
|
)
|
||||||
|
enriched.append(d)
|
||||||
|
return enriched
|
||||||
|
|
||||||
|
|
||||||
|
def _sort_enriched(rows, sort):
|
||||||
|
"""Sort a list of enriched dicts by carry efficiency when requested."""
|
||||||
|
if sort == "efficiency_desc":
|
||||||
|
return sorted(rows, key=lambda r: (r["slots_per_kg"] is None, -(r["slots_per_kg"] or 0)))
|
||||||
|
if sort == "efficiency_asc":
|
||||||
|
return sorted(rows, key=lambda r: (r["slots_per_kg"] is None, r["slots_per_kg"] or 0))
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
@app.route("/loadout")
|
@app.route("/loadout")
|
||||||
def loadout():
|
def loadout():
|
||||||
conn = get_db()
|
conn = get_db()
|
||||||
tab = request.args.get("tab", "guns")
|
tab = request.args.get("tab", "guns")
|
||||||
sort = request.args.get("sort", "weight_asc")
|
sort = request.args.get("sort", "weight_asc")
|
||||||
|
|
||||||
guns = armor = helmets = headwear = backpacks = rigs = plates = []
|
guns = armor = helmets = headwear = backpacks = rigs = armored_rigs = plates = []
|
||||||
builder_guns = builder_armor = builder_helmets = builder_rigs = builder_backpacks = []
|
builder_guns = builder_armor = builder_helmets = builder_rigs = builder_backpacks = []
|
||||||
requires = request.args.getlist("requires") # list of slot_nameids that must exist
|
requires = request.args.getlist("requires") # list of slot_nameids that must exist
|
||||||
min_class = request.args.get("min_class", 0, type=int)
|
min_class = request.args.get("min_class", 0, type=int)
|
||||||
@@ -523,21 +561,34 @@ def loadout():
|
|||||||
""", (min_class, min_class)).fetchall()
|
""", (min_class, min_class)).fetchall()
|
||||||
|
|
||||||
elif tab == "backpacks":
|
elif tab == "backpacks":
|
||||||
backpacks = conn.execute(f"""
|
rows = conn.execute(f"""
|
||||||
SELECT * FROM gear_items
|
SELECT * FROM gear_items
|
||||||
WHERE category = 'backpack'
|
WHERE category = 'backpack'
|
||||||
AND (? = 0 OR capacity >= ?)
|
AND (? = 0 OR capacity >= ?)
|
||||||
ORDER BY {sort_frag}
|
ORDER BY {sort_frag}
|
||||||
""", (min_capacity, min_capacity)).fetchall()
|
""", (min_capacity, min_capacity)).fetchall()
|
||||||
|
backpacks = _sort_enriched(_enrich_with_efficiency(rows), sort)
|
||||||
|
|
||||||
elif tab == "rigs":
|
elif tab == "rigs":
|
||||||
rigs = conn.execute(f"""
|
rows = conn.execute(f"""
|
||||||
SELECT * FROM gear_items
|
SELECT * FROM gear_items
|
||||||
WHERE category = 'rig'
|
WHERE category = 'rig'
|
||||||
|
AND armor_class IS NULL
|
||||||
|
AND (? = 0 OR capacity >= ?)
|
||||||
|
ORDER BY {sort_frag}
|
||||||
|
""", (min_capacity, min_capacity)).fetchall()
|
||||||
|
rigs = _sort_enriched(_enrich_with_efficiency(rows), sort)
|
||||||
|
|
||||||
|
elif tab == "armored_rigs":
|
||||||
|
rows = conn.execute(f"""
|
||||||
|
SELECT * FROM gear_items
|
||||||
|
WHERE category = 'rig'
|
||||||
|
AND armor_class IS NOT NULL
|
||||||
AND (? = 0 OR capacity >= ?)
|
AND (? = 0 OR capacity >= ?)
|
||||||
AND (? = 0 OR armor_class >= ?)
|
AND (? = 0 OR armor_class >= ?)
|
||||||
ORDER BY {sort_frag}
|
ORDER BY {sort_frag}
|
||||||
""", (min_capacity, min_capacity, min_class, min_class)).fetchall()
|
""", (min_capacity, min_capacity, min_class, min_class)).fetchall()
|
||||||
|
armored_rigs = _sort_enriched(_enrich_with_efficiency(rows), sort)
|
||||||
|
|
||||||
elif tab == "plates":
|
elif tab == "plates":
|
||||||
plates = conn.execute(f"""
|
plates = conn.execute(f"""
|
||||||
@@ -563,7 +614,7 @@ def loadout():
|
|||||||
"loadout.html",
|
"loadout.html",
|
||||||
tab=tab, sort=sort,
|
tab=tab, sort=sort,
|
||||||
guns=guns, armor=armor, helmets=helmets, headwear=headwear,
|
guns=guns, armor=armor, helmets=helmets, headwear=headwear,
|
||||||
backpacks=backpacks, rigs=rigs, plates=plates,
|
backpacks=backpacks, rigs=rigs, armored_rigs=armored_rigs, plates=plates,
|
||||||
slot_filters=LOADOUT_SLOT_FILTERS,
|
slot_filters=LOADOUT_SLOT_FILTERS,
|
||||||
requires=requires,
|
requires=requires,
|
||||||
min_class=min_class, min_capacity=min_capacity,
|
min_class=min_class, min_capacity=min_capacity,
|
||||||
|
|||||||
BIN
assets/onlyscavs.png
Normal file
BIN
assets/onlyscavs.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.2 MiB |
@@ -19,30 +19,38 @@
|
|||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
}
|
}
|
||||||
.page { max-width: 1100px; margin: 0 auto; }
|
body::before {
|
||||||
|
content: '';
|
||||||
nav {
|
position: fixed;
|
||||||
display: flex;
|
inset: 0;
|
||||||
gap: 12px;
|
background: rgba(14,14,14,0.88);
|
||||||
flex-wrap: wrap;
|
pointer-events: none;
|
||||||
margin-bottom: 24px;
|
z-index: 0;
|
||||||
font-size: 0.88rem;
|
|
||||||
}
|
}
|
||||||
nav a {
|
.page { max-width: 1100px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
color: var(--muted);
|
.site-nav {
|
||||||
text-decoration: none;
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
padding: 4px 10px;
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
border: 1px solid var(--border);
|
padding: 0 24px; height: 52px;
|
||||||
border-radius: 4px;
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
}
|
}
|
||||||
nav a:hover { color: var(--accent); border-color: var(--accent); }
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
nav a.active { color: var(--accent); border-color: var(--accent); background: #1a2533; }
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
|
|
||||||
h1 { font-size: 1.4rem; margin: 0 0 4px; }
|
h1 { font-size: 1.4rem; margin: 0 0 4px; }
|
||||||
.subtitle { color: var(--muted); font-size: 0.88rem; margin: 0 0 20px; }
|
.subtitle { color: var(--muted); font-size: 0.88rem; margin: 0 0 20px; }
|
||||||
@@ -232,14 +240,16 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
<div class="nav-links">
|
||||||
<a href="/keys">Keys</a>
|
<a href="/keys">Keys</a>
|
||||||
<a href="/collector">Collector</a>
|
<a href="/collector">Collector</a>
|
||||||
<a href="/quests">Quests</a>
|
<a href="/quests">Quests</a>
|
||||||
<a href="/loadout">Loadout</a>
|
<a href="/loadout">Loadout</a>
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
<a href="/barters" class="active">Barters</a>
|
<a href="/barters" class="active">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<h1>Barter Calculator</h1>
|
<h1>Barter Calculator</h1>
|
||||||
|
|||||||
@@ -77,15 +77,38 @@
|
|||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
}
|
}
|
||||||
.page { max-width: 960px; margin: 0 auto; }
|
body::before {
|
||||||
nav { margin-bottom: 16px; font-size: 0.9rem; }
|
content: '';
|
||||||
nav a { color: var(--accent); }
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.page { max-width: 960px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
|
.site-nav {
|
||||||
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 0 24px; height: 52px;
|
||||||
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
h1 { margin: 0 0 4px; }
|
h1 { margin: 0 0 4px; }
|
||||||
.toolbar {
|
.toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -289,18 +312,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
|
<div class="nav-links">
|
||||||
<a href="/keys">Keys</a>
|
<a href="/keys">Keys</a>
|
||||||
|
|
<a href="/collector" class="active">Collector</a>
|
||||||
<a href="/quests">Quest Trees</a>
|
<a href="/quests">Quests</a>
|
||||||
|
|
<a href="/loadout">Loadout</a>
|
||||||
<a href="/loadout">Loadout Planner</a>
|
|
||||||
|
|
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
|
|
|
||||||
<a href="/barters">Barters</a>
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Collector Checklist</h1>
|
<h1>Collector Checklist</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
|
|||||||
@@ -15,11 +15,40 @@
|
|||||||
--key-border: #5a7a3a;
|
--key-border: #5a7a3a;
|
||||||
--key-bg: #141e10;
|
--key-bg: #141e10;
|
||||||
}
|
}
|
||||||
body { font-family: sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 16px; }
|
body {
|
||||||
.page { max-width: 900px; margin: 0 auto; }
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
nav { margin-bottom: 20px; }
|
background: var(--bg);
|
||||||
nav a { color: var(--accent); font-size: 0.9rem; }
|
color: var(--text);
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.page { max-width: 900px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
a { color: var(--accent); }
|
a { color: var(--accent); }
|
||||||
|
.site-nav {
|
||||||
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 0 24px; height: 52px;
|
||||||
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
h1 { margin: 0 0 2px; font-size: 1.4rem; }
|
h1 { margin: 0 0 2px; font-size: 1.4rem; }
|
||||||
.subtitle { color: var(--muted); font-size: 0.9rem; margin: 0 0 20px; }
|
.subtitle { color: var(--muted); font-size: 0.9rem; margin: 0 0 20px; }
|
||||||
|
|
||||||
@@ -153,8 +182,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/loadout?tab=guns">← Back to Guns</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="/keys">Keys</a>
|
||||||
|
<a href="/collector">Collector</a>
|
||||||
|
<a href="/quests">Quests</a>
|
||||||
|
<a href="/loadout" class="active">Loadout</a>
|
||||||
|
<a href="/meds">Injectors</a>
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="gun-card">
|
<div class="gun-card">
|
||||||
|
|||||||
@@ -15,32 +15,69 @@
|
|||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
}
|
}
|
||||||
.page {
|
.page {
|
||||||
max-width: 980px;
|
max-width: 980px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
padding: 24px 16px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
nav {
|
.site-nav {
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
z-index: 100;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
justify-content: space-between;
|
||||||
margin-bottom: 16px;
|
padding: 0 24px;
|
||||||
font-size: 0.9rem;
|
height: 52px;
|
||||||
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
}
|
}
|
||||||
nav a {
|
.nav-brand {
|
||||||
color: var(--muted);
|
font-size: 0.85rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--accent);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 4px 0;
|
flex-shrink: 0;
|
||||||
border-bottom: 2px solid transparent;
|
|
||||||
transition: color 0.15s, border-color 0.15s;
|
|
||||||
}
|
}
|
||||||
nav a:hover { color: var(--text); }
|
.nav-links {
|
||||||
nav a.active { color: var(--accent); border-bottom-color: var(--accent); }
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.nav-links a {
|
||||||
|
color: #666;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
margin: 0 0 16px;
|
margin: 0 0 16px;
|
||||||
@@ -228,14 +265,16 @@
|
|||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
<div class="nav-links">
|
||||||
<a href="/keys" class="active">Keys</a>
|
<a href="/keys" class="active">Keys</a>
|
||||||
<a href="/collector">Collector</a>
|
<a href="/collector">Collector</a>
|
||||||
<a href="/quests">Quests</a>
|
<a href="/quests">Quests</a>
|
||||||
<a href="/loadout">Loadout</a>
|
<a href="/loadout">Loadout</a>
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
<a href="/barters">Barters</a>
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Key Ratings</h1>
|
<h1>Key Ratings</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -1,125 +1,295 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
<title>OnlyScavs</title>
|
<title>OnlyScavs</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #121212;
|
--bg: #0e0e0e;
|
||||||
--panel: #1a1a1a;
|
--panel: #161616;
|
||||||
--text: #eee;
|
--panel2: #1c1c1c;
|
||||||
--muted: #888;
|
--text: #e8e8e8;
|
||||||
--border: #2a2a2a;
|
--muted: #777;
|
||||||
|
--muted2: #555;
|
||||||
|
--border: #262626;
|
||||||
--accent: #9ccfff;
|
--accent: #9ccfff;
|
||||||
--accent2: #ffd580;
|
--accent2: #ffd580;
|
||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── NAV ─────────────────────────────────────────────────── */
|
||||||
|
.site-nav {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: space-between;
|
||||||
padding: 32px 16px;
|
padding: 0 32px;
|
||||||
|
height: 52px;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
}
|
}
|
||||||
.hero {
|
.nav-brand {
|
||||||
text-align: center;
|
font-size: 0.95rem;
|
||||||
margin-bottom: 48px;
|
font-weight: 700;
|
||||||
}
|
letter-spacing: 0.08em;
|
||||||
.hero h1 {
|
text-transform: uppercase;
|
||||||
font-size: 2.4rem;
|
|
||||||
letter-spacing: 0.04em;
|
|
||||||
color: var(--accent);
|
color: var(--accent);
|
||||||
margin: 0 0 8px;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
.hero p {
|
.nav-links {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.nav-links a {
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
font-size: 1rem;
|
text-decoration: none;
|
||||||
margin: 0;
|
font-size: 0.82rem;
|
||||||
|
padding: 6px 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: color 0.15s, background 0.15s;
|
||||||
|
}
|
||||||
|
.nav-links a:hover {
|
||||||
|
color: var(--text);
|
||||||
|
background: rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── HERO ─────────────────────────────────────────────────── */
|
||||||
|
.hero {
|
||||||
|
position: relative;
|
||||||
|
height: 580px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
.hero-img {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center 65%;
|
||||||
|
}
|
||||||
|
.hero-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(14,14,14,0.05) 0%,
|
||||||
|
rgba(14,14,14,0.1) 35%,
|
||||||
|
rgba(14,14,14,0.7) 68%,
|
||||||
|
rgba(14,14,14,1) 92%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.hero-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
padding: 0 48px 48px;
|
||||||
|
max-width: 860px;
|
||||||
|
}
|
||||||
|
.hero-content h1 {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.01em;
|
||||||
|
line-height: 1;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.hero-content h1 span {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
.hero-content p {
|
||||||
|
font-size: 1.05rem;
|
||||||
|
color: rgba(255,255,255,0.55);
|
||||||
|
max-width: 420px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── TOOLS GRID ───────────────────────────────────────────── */
|
||||||
|
.tools-section {
|
||||||
|
max-width: 920px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 48px 32px 80px;
|
||||||
|
}
|
||||||
|
.section-label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.12em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--muted2);
|
||||||
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
.cards {
|
.cards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
width: 100%;
|
|
||||||
max-width: 860px;
|
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
padding: 24px 20px;
|
padding: 22px 20px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
transition: border-color 0.15s, background 0.15s, transform 0.1s;
|
transition: border-color 0.15s, background 0.15s, transform 0.1s;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--accent);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.15s;
|
||||||
}
|
}
|
||||||
.card:hover {
|
.card:hover {
|
||||||
border-color: #444;
|
border-color: #333;
|
||||||
background: #1e1e1e;
|
background: var(--panel2);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
.card:hover::before {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
.card-icon {
|
.card-icon {
|
||||||
font-size: 1.8rem;
|
font-size: 1.4rem;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
.card-title {
|
.card-title {
|
||||||
font-size: 1.05rem;
|
font-size: 0.95rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--accent);
|
color: var(--text);
|
||||||
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
.card-desc {
|
.card-desc {
|
||||||
font-size: 0.85rem;
|
font-size: 0.8rem;
|
||||||
color: var(--muted);
|
color: var(--muted);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
.card-arrow {
|
||||||
|
margin-top: auto;
|
||||||
|
padding-top: 14px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted2);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
transition: color 0.15s;
|
||||||
|
}
|
||||||
|
.card:hover .card-arrow {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── FOOTER ───────────────────────────────────────────────── */
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
padding: 20px 32px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--muted2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── RESPONSIVE ───────────────────────────────────────────── */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.site-nav { padding: 0 16px; }
|
||||||
|
.nav-links { display: none; }
|
||||||
|
.hero { height: 420px; }
|
||||||
|
.hero-content { padding: 0 20px 40px; }
|
||||||
|
.hero-content h1 { font-size: 2rem; }
|
||||||
|
.tools-section { padding: 32px 16px 60px; }
|
||||||
|
.cards { grid-template-columns: 1fr 1fr; }
|
||||||
|
}
|
||||||
|
@media (max-width: 400px) {
|
||||||
|
.cards { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="hero">
|
<nav class="site-nav">
|
||||||
<h1>OnlyScavs</h1>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
<p>Escape from Tarkov reference tools</p>
|
<div class="nav-links">
|
||||||
|
<a href="/keys">Keys</a>
|
||||||
|
<a href="/collector">Collector</a>
|
||||||
|
<a href="/quests">Quests</a>
|
||||||
|
<a href="/loadout">Loadout</a>
|
||||||
|
<a href="/meds">Injectors</a>
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
</div>
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<img class="hero-img" src="/assets/onlyscavs.png" alt="OnlyScavs">
|
||||||
|
<div class="hero-overlay"></div>
|
||||||
|
<div class="hero-content">
|
||||||
|
<h1>Only<span>Scavs</span></h1>
|
||||||
|
<p>Escape from Tarkov reference tools. Keys, quests, loadouts — all local, all yours.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="tools-section">
|
||||||
|
<div class="section-label">Tools</div>
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
<a class="card" href="/keys">
|
<a class="card" href="/keys">
|
||||||
<div class="card-icon">🗝</div>
|
<div class="card-icon">🗝</div>
|
||||||
<div class="card-title">Key Ratings</div>
|
<div class="card-title">Key Ratings</div>
|
||||||
<div class="card-desc">Rate and filter keys by map, priority, and quest use.</div>
|
<div class="card-desc">Rate and filter keys by map, priority, and quest use.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="card" href="/collector">
|
<a class="card" href="/collector">
|
||||||
<div class="card-icon">★</div>
|
<div class="card-icon">★</div>
|
||||||
<div class="card-title">Collector</div>
|
<div class="card-title">Collector</div>
|
||||||
<div class="card-desc">Track your progress toward the Kappa container.</div>
|
<div class="card-desc">Track your Kappa container progress across all 255 quests.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="card" href="/quests">
|
<a class="card" href="/quests">
|
||||||
<div class="card-icon">📋</div>
|
<div class="card-icon">📋</div>
|
||||||
<div class="card-title">Quest Trees</div>
|
<div class="card-title">Quest Trees</div>
|
||||||
<div class="card-desc">Visualize quest chains and trader dependencies.</div>
|
<div class="card-desc">Visualize quest chains and trader dependencies.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="card" href="/loadout">
|
<a class="card" href="/loadout">
|
||||||
<div class="card-icon">🎽</div>
|
<div class="card-icon">🎽</div>
|
||||||
<div class="card-title">Loadout Planner</div>
|
<div class="card-title">Loadout Planner</div>
|
||||||
<div class="card-desc">Browse and compare guns, armor, rigs, and more.</div>
|
<div class="card-desc">Browse and compare guns, armor, rigs, and more.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="card" href="/meds">
|
<a class="card" href="/meds">
|
||||||
<div class="card-icon">💉</div>
|
<div class="card-icon">💉</div>
|
||||||
<div class="card-title">Injectors</div>
|
<div class="card-title">Injectors</div>
|
||||||
<div class="card-desc">Compare stim effects, skills, and side effects.</div>
|
<div class="card-desc">Compare stim effects, skills, and side effects.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
<a class="card" href="/barters">
|
<a class="card" href="/barters">
|
||||||
<div class="card-icon">🔄</div>
|
<div class="card-icon">🔄</div>
|
||||||
<div class="card-title">Barter Calculator</div>
|
<div class="card-title">Barter Calculator</div>
|
||||||
<div class="card-desc">Calculate the true rouble cost of any barter by entering flea market prices.</div>
|
<div class="card-desc">Calculate the true rouble cost of any barter deal.</div>
|
||||||
|
<div class="card-arrow">Open →</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
OnlyScavs — personal Tarkov toolkit
|
||||||
|
</footer>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -14,17 +14,40 @@
|
|||||||
--amber: #ffd580;
|
--amber: #ffd580;
|
||||||
}
|
}
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
}
|
}
|
||||||
.page { max-width: 1100px; margin: 0 auto; }
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.page { max-width: 1100px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
h1 { margin-bottom: 4px; }
|
h1 { margin-bottom: 4px; }
|
||||||
nav { margin-bottom: 20px; }
|
|
||||||
nav a { color: var(--accent); font-size: 0.9rem; }
|
|
||||||
a { color: var(--accent); }
|
a { color: var(--accent); }
|
||||||
|
.site-nav {
|
||||||
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 0 24px; height: 52px;
|
||||||
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
|
|
||||||
/* Tab bar */
|
/* Tab bar */
|
||||||
.tab-bar {
|
.tab-bar {
|
||||||
@@ -221,13 +244,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a> |
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
<a href="/keys">Keys</a> |
|
<div class="nav-links">
|
||||||
<a href="/collector">Collector</a> |
|
<a href="/keys">Keys</a>
|
||||||
<a href="/quests">Quests</a> |
|
<a href="/collector">Collector</a>
|
||||||
<a href="/meds">Injectors</a> |
|
<a href="/quests">Quests</a>
|
||||||
|
<a href="/loadout" class="active">Loadout</a>
|
||||||
|
<a href="/meds">Injectors</a>
|
||||||
<a href="/barters">Barters</a>
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Loadout Planner</h1>
|
<h1>Loadout Planner</h1>
|
||||||
<p style="color:var(--muted);margin:0 0 16px;font-size:0.9rem;">
|
<p style="color:var(--muted);margin:0 0 16px;font-size:0.9rem;">
|
||||||
@@ -235,7 +261,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="tab-bar">
|
<div class="tab-bar">
|
||||||
{% for t_id, t_label in [('guns','Guns'),('armor','Armor'),('helmets','Helmets'),('headwear','Headwear'),('backpacks','Backpacks'),('rigs','Rigs'),('plates','Plates'),('builder','Build Builder')] %}
|
{% for t_id, t_label in [('guns','Guns'),('armor','Armor'),('helmets','Helmets'),('headwear','Headwear'),('backpacks','Backpacks'),('rigs','Soft Rigs'),('armored_rigs','Armored Rigs'),('plates','Plates'),('builder','Build Builder')] %}
|
||||||
<a href="/loadout?tab={{ t_id }}" class="{% if tab == t_id %}active{% endif %}">{{ t_label }}</a>
|
<a href="/loadout?tab={{ t_id }}" class="{% if tab == t_id %}active{% endif %}">{{ t_label }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
@@ -603,6 +629,7 @@
|
|||||||
<option value="weight_desc" {% if sort=='weight_desc' %}selected{% endif %}>Weight ↓</option>
|
<option value="weight_desc" {% if sort=='weight_desc' %}selected{% endif %}>Weight ↓</option>
|
||||||
<option value="capacity_desc" {% if sort=='capacity_desc' %}selected{% endif %}>Capacity ↓</option>
|
<option value="capacity_desc" {% if sort=='capacity_desc' %}selected{% endif %}>Capacity ↓</option>
|
||||||
<option value="capacity_asc" {% if sort=='capacity_asc' %}selected{% endif %}>Capacity ↑</option>
|
<option value="capacity_asc" {% if sort=='capacity_asc' %}selected{% endif %}>Capacity ↑</option>
|
||||||
|
<option value="efficiency_desc" {% if sort=='efficiency_desc' %}selected{% endif %}>Carry Efficiency ↓</option>
|
||||||
<option value="name_asc" {% if sort=='name_asc' %}selected{% endif %}>Name A→Z</option>
|
<option value="name_asc" {% if sort=='name_asc' %}selected{% endif %}>Name A→Z</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit">Filter</button>
|
<button type="submit">Filter</button>
|
||||||
@@ -610,7 +637,7 @@
|
|||||||
<table class="gear-table">
|
<table class="gear-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th><th>Name</th><th>Capacity (slots)</th><th>Weight</th>
|
<th></th><th>Name</th><th>Capacity (slots)</th><th>Weight</th><th>Carry Efficiency</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -624,20 +651,71 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="muted">{{ item.capacity or '—' }}</td>
|
<td class="muted">{{ item.capacity or '—' }}</td>
|
||||||
<td class="w">{% if item.weight_kg is not none %}{{ "%.3f"|format(item.weight_kg) }} kg{% else %}—{% endif %}</td>
|
<td class="w">{% if item.weight_kg is not none %}{{ "%.3f"|format(item.weight_kg) }} kg{% else %}—{% endif %}</td>
|
||||||
|
<td class="muted">
|
||||||
|
{% if item.slots_per_kg is not none %}
|
||||||
|
<span title="{{ "%.3f"|format(item.kg_per_slot) }} kg/slot">{{ "%.2f"|format(item.slots_per_kg) }} slots/kg</span>
|
||||||
|
{% else %}—{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><td colspan="4" class="empty">No backpacks found.</td></tr>
|
<tr><td colspan="5" class="empty">No backpacks found.</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{# =============================== RIGS TAB =============================== #}
|
{# =============================== SOFT RIGS TAB =============================== #}
|
||||||
{% if tab == "rigs" %}
|
{% if tab == "rigs" %}
|
||||||
<form method="get" class="filter-bar">
|
<form method="get" class="filter-bar">
|
||||||
<input type="hidden" name="tab" value="rigs">
|
<input type="hidden" name="tab" value="rigs">
|
||||||
<label>Min slots</label>
|
<label>Min slots</label>
|
||||||
<input type="number" name="min_capacity" value="{{ min_capacity or '' }}" min="0" placeholder="0" style="width:60px">
|
<input type="number" name="min_capacity" value="{{ min_capacity or '' }}" min="0" placeholder="0" style="width:60px">
|
||||||
|
<label>Sort</label>
|
||||||
|
<select name="sort">
|
||||||
|
<option value="weight_asc" {% if sort=='weight_asc' %}selected{% endif %}>Weight ↑</option>
|
||||||
|
<option value="weight_desc" {% if sort=='weight_desc' %}selected{% endif %}>Weight ↓</option>
|
||||||
|
<option value="capacity_desc" {% if sort=='capacity_desc' %}selected{% endif %}>Capacity ↓</option>
|
||||||
|
<option value="efficiency_desc" {% if sort=='efficiency_desc' %}selected{% endif %}>Carry Efficiency ↓</option>
|
||||||
|
<option value="name_asc" {% if sort=='name_asc' %}selected{% endif %}>Name A→Z</option>
|
||||||
|
</select>
|
||||||
|
<button type="submit">Filter</button>
|
||||||
|
</form>
|
||||||
|
<table class="gear-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th></th><th>Name</th><th>Capacity (slots)</th><th>Weight</th><th>Carry Efficiency</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in rigs %}
|
||||||
|
<tr>
|
||||||
|
<td>{% if item.grid_image_url %}<img src="{{ item.grid_image_url }}" loading="lazy" alt="">{% endif %}</td>
|
||||||
|
<td class="name-cell">
|
||||||
|
<strong>{{ item.short_name or item.name }}</strong>
|
||||||
|
{% if item.short_name and item.short_name != item.name %}<small>{{ item.name }}</small>{% endif %}
|
||||||
|
{% if item.wiki_url %}<a href="{{ item.wiki_url }}" target="_blank" style="font-size:0.78rem;margin-left:4px">wiki</a>{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="muted">{{ item.capacity or '—' }}</td>
|
||||||
|
<td class="w">{% if item.weight_kg is not none %}{{ "%.3f"|format(item.weight_kg) }} kg{% else %}—{% endif %}</td>
|
||||||
|
<td class="muted">
|
||||||
|
{% if item.slots_per_kg is not none %}
|
||||||
|
<span title="{{ "%.3f"|format(item.kg_per_slot) }} kg/slot">{{ "%.2f"|format(item.slots_per_kg) }} slots/kg</span>
|
||||||
|
{% else %}—{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="5" class="empty">No soft rigs found.</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# =============================== ARMORED RIGS TAB =============================== #}
|
||||||
|
{% if tab == "armored_rigs" %}
|
||||||
|
<form method="get" class="filter-bar">
|
||||||
|
<input type="hidden" name="tab" value="armored_rigs">
|
||||||
|
<label>Min slots</label>
|
||||||
|
<input type="number" name="min_capacity" value="{{ min_capacity or '' }}" min="0" placeholder="0" style="width:60px">
|
||||||
<label>Min class</label>
|
<label>Min class</label>
|
||||||
<select name="min_class">
|
<select name="min_class">
|
||||||
<option value="0" {% if min_class==0 %}selected{% endif %}>Any</option>
|
<option value="0" {% if min_class==0 %}selected{% endif %}>Any</option>
|
||||||
@@ -651,6 +729,7 @@
|
|||||||
<option value="weight_desc" {% if sort=='weight_desc' %}selected{% endif %}>Weight ↓</option>
|
<option value="weight_desc" {% if sort=='weight_desc' %}selected{% endif %}>Weight ↓</option>
|
||||||
<option value="capacity_desc" {% if sort=='capacity_desc' %}selected{% endif %}>Capacity ↓</option>
|
<option value="capacity_desc" {% if sort=='capacity_desc' %}selected{% endif %}>Capacity ↓</option>
|
||||||
<option value="class_desc" {% if sort=='class_desc' %}selected{% endif %}>Class ↓</option>
|
<option value="class_desc" {% if sort=='class_desc' %}selected{% endif %}>Class ↓</option>
|
||||||
|
<option value="efficiency_desc" {% if sort=='efficiency_desc' %}selected{% endif %}>Carry Efficiency ↓</option>
|
||||||
<option value="name_asc" {% if sort=='name_asc' %}selected{% endif %}>Name A→Z</option>
|
<option value="name_asc" {% if sort=='name_asc' %}selected{% endif %}>Name A→Z</option>
|
||||||
</select>
|
</select>
|
||||||
<button type="submit">Filter</button>
|
<button type="submit">Filter</button>
|
||||||
@@ -658,11 +737,11 @@
|
|||||||
<table class="gear-table">
|
<table class="gear-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th><th>Name</th><th>Class</th><th>Capacity (slots)</th><th>Zones</th><th>Weight</th>
|
<th></th><th>Name</th><th>Class</th><th>Capacity (slots)</th><th>Zones</th><th>Weight</th><th>Carry Efficiency</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in rigs %}
|
{% for item in armored_rigs %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% if item.grid_image_url %}<img src="{{ item.grid_image_url }}" loading="lazy" alt="">{% endif %}</td>
|
<td>{% if item.grid_image_url %}<img src="{{ item.grid_image_url }}" loading="lazy" alt="">{% endif %}</td>
|
||||||
<td class="name-cell">
|
<td class="name-cell">
|
||||||
@@ -671,9 +750,7 @@
|
|||||||
{% if item.wiki_url %}<a href="{{ item.wiki_url }}" target="_blank" style="font-size:0.78rem;margin-left:4px">wiki</a>{% endif %}
|
{% if item.wiki_url %}<a href="{{ item.wiki_url }}" target="_blank" style="font-size:0.78rem;margin-left:4px">wiki</a>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if item.armor_class %}
|
|
||||||
<span class="cls cls-{{ item.armor_class }}">{{ item.armor_class }}</span>
|
<span class="cls cls-{{ item.armor_class }}">{{ item.armor_class }}</span>
|
||||||
{% else %}<span class="muted">—</span>{% endif %}
|
|
||||||
</td>
|
</td>
|
||||||
<td class="muted">{{ item.capacity or '—' }}</td>
|
<td class="muted">{{ item.capacity or '—' }}</td>
|
||||||
<td class="muted" style="font-size:0.8rem">{{ item.zones or '—' }}</td>
|
<td class="muted" style="font-size:0.8rem">{{ item.zones or '—' }}</td>
|
||||||
@@ -681,9 +758,14 @@
|
|||||||
{% if item.weight_kg is not none %}{{ "%.3f"|format(item.weight_kg) }} kg{% else %}—{% endif %}
|
{% if item.weight_kg is not none %}{{ "%.3f"|format(item.weight_kg) }} kg{% else %}—{% endif %}
|
||||||
{% if item.id in carrier_ids_with_open_slots %}<br><small class="muted">no plates</small>{% endif %}
|
{% if item.id in carrier_ids_with_open_slots %}<br><small class="muted">no plates</small>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
<td class="muted">
|
||||||
|
{% if item.slots_per_kg is not none %}
|
||||||
|
<span title="{{ "%.3f"|format(item.kg_per_slot) }} kg/slot">{{ "%.2f"|format(item.slots_per_kg) }} slots/kg</span>
|
||||||
|
{% else %}—{% endif %}
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
<tr><td colspan="6" class="empty">No rigs found.</td></tr>
|
<tr><td colspan="7" class="empty">No armored rigs found.</td></tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -23,31 +23,38 @@
|
|||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 16px;
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
}
|
}
|
||||||
.page { max-width: 1200px; margin: 0 auto; }
|
body::before {
|
||||||
|
content: '';
|
||||||
/* ── Nav ── */
|
position: fixed;
|
||||||
nav {
|
inset: 0;
|
||||||
display: flex;
|
background: rgba(14,14,14,0.88);
|
||||||
gap: 12px;
|
pointer-events: none;
|
||||||
flex-wrap: wrap;
|
z-index: 0;
|
||||||
margin-bottom: 24px;
|
|
||||||
font-size: 0.88rem;
|
|
||||||
}
|
}
|
||||||
nav a {
|
.page { max-width: 1200px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
color: var(--muted);
|
.site-nav {
|
||||||
text-decoration: none;
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
padding: 4px 10px;
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
border: 1px solid var(--border);
|
padding: 0 24px; height: 52px;
|
||||||
border-radius: 4px;
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
}
|
}
|
||||||
nav a:hover { color: var(--accent); border-color: var(--accent); }
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
nav a.active { color: var(--accent); border-color: var(--accent); background: #1a2533; }
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
|
|
||||||
h1 { font-size: 1.4rem; margin: 0 0 4px; }
|
h1 { font-size: 1.4rem; margin: 0 0 4px; }
|
||||||
.subtitle { color: var(--muted); font-size: 0.88rem; margin: 0 0 28px; }
|
.subtitle { color: var(--muted); font-size: 0.88rem; margin: 0 0 28px; }
|
||||||
@@ -246,14 +253,16 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
|
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
<div class="nav-links">
|
||||||
<a href="/keys">Keys</a>
|
<a href="/keys">Keys</a>
|
||||||
<a href="/quests">Quest Tree</a>
|
|
||||||
<a href="/collector">Collector</a>
|
<a href="/collector">Collector</a>
|
||||||
|
<a href="/quests">Quests</a>
|
||||||
<a href="/loadout">Loadout</a>
|
<a href="/loadout">Loadout</a>
|
||||||
<a href="/meds" class="active">Injectors</a>
|
<a href="/meds" class="active">Injectors</a>
|
||||||
<a href="/barters">Barters</a>
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<h1>Injector Quick Reference</h1>
|
<h1>Injector Quick Reference</h1>
|
||||||
|
|||||||
@@ -116,10 +116,39 @@
|
|||||||
--line: #333;
|
--line: #333;
|
||||||
}
|
}
|
||||||
* { box-sizing: border-box; }
|
* { box-sizing: border-box; }
|
||||||
body { font-family: sans-serif; background: var(--bg); color: var(--text); margin: 0; padding: 16px; }
|
body {
|
||||||
.page { max-width: 1100px; margin: 0 auto; }
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||||
nav { margin-bottom: 16px; font-size: 0.9rem; }
|
background: var(--bg);
|
||||||
nav a { color: var(--accent); }
|
color: var(--text);
|
||||||
|
margin: 0;
|
||||||
|
padding-top: 52px;
|
||||||
|
background-image: url('/assets/onlyscavs.png');
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 65%;
|
||||||
|
}
|
||||||
|
body::before {
|
||||||
|
content: '';
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(14,14,14,0.88);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.page { max-width: 1100px; margin: 0 auto; padding: 24px 16px; position: relative; z-index: 1; }
|
||||||
|
.site-nav {
|
||||||
|
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 0 24px; height: 52px;
|
||||||
|
background: rgba(14,14,14,0.92);
|
||||||
|
backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
|
}
|
||||||
|
.nav-brand { font-size: 0.85rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); text-decoration: none; flex-shrink: 0; }
|
||||||
|
.nav-links { display: flex; gap: 2px; flex-wrap: wrap; }
|
||||||
|
.nav-links a { color: #666; text-decoration: none; font-size: 0.8rem; padding: 5px 10px; border-radius: 5px; transition: color 0.15s, background 0.15s; }
|
||||||
|
.nav-links a:hover { color: var(--text); background: rgba(255,255,255,0.06); }
|
||||||
|
.nav-links a.active { color: var(--accent); background: rgba(156,207,255,0.08); }
|
||||||
h1 { margin: 0 0 4px; }
|
h1 { margin: 0 0 4px; }
|
||||||
|
|
||||||
/* toolbar */
|
/* toolbar */
|
||||||
@@ -226,18 +255,16 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<nav>
|
<nav class="site-nav">
|
||||||
<a href="/">Home</a>
|
<a class="nav-brand" href="/">OnlyScavs</a>
|
||||||
|
|
<div class="nav-links">
|
||||||
<a href="/keys">Keys</a>
|
<a href="/keys">Keys</a>
|
||||||
|
|
<a href="/collector">Collector</a>
|
||||||
<a href="/collector">Collector Checklist</a>
|
<a href="/quests" class="active">Quests</a>
|
||||||
|
|
<a href="/loadout">Loadout</a>
|
||||||
<a href="/loadout">Loadout Planner</a>
|
|
||||||
|
|
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
|
|
|
||||||
<a href="/barters">Barters</a>
|
<a href="/barters">Barters</a>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Quest Trees</h1>
|
<h1>Quest Trees</h1>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user