feat: add barter calculator page
This commit is contained in:
71
app.py
71
app.py
@@ -945,5 +945,76 @@ def meds():
|
|||||||
situations=situations)
|
situations=situations)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/barters")
|
||||||
|
def barters():
|
||||||
|
import requests as _req
|
||||||
|
|
||||||
|
API_URL = "https://api.tarkov.dev/graphql"
|
||||||
|
query = """
|
||||||
|
{
|
||||||
|
barters(lang: en) {
|
||||||
|
id
|
||||||
|
trader { name }
|
||||||
|
level
|
||||||
|
taskUnlock { name }
|
||||||
|
requiredItems {
|
||||||
|
item { id name shortName iconLink wikiLink }
|
||||||
|
count
|
||||||
|
}
|
||||||
|
rewardItems {
|
||||||
|
item { id name shortName iconLink wikiLink }
|
||||||
|
count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
resp = _req.post(API_URL, json={"query": query}, timeout=15)
|
||||||
|
data = resp.json()
|
||||||
|
raw_barters = data.get("data", {}).get("barters", [])
|
||||||
|
except Exception:
|
||||||
|
raw_barters = []
|
||||||
|
|
||||||
|
barter_list = []
|
||||||
|
for b in raw_barters:
|
||||||
|
reward_items = b.get("rewardItems", [])
|
||||||
|
required_items = b.get("requiredItems", [])
|
||||||
|
if not reward_items or not required_items:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Use first reward item as the "output" item
|
||||||
|
reward = reward_items[0]
|
||||||
|
reward_item = reward.get("item") or {}
|
||||||
|
reward_count = reward.get("count", 1)
|
||||||
|
|
||||||
|
required = []
|
||||||
|
for ri in required_items:
|
||||||
|
item = ri.get("item") or {}
|
||||||
|
required.append({
|
||||||
|
"id": item.get("id", ""),
|
||||||
|
"name": item.get("name", "Unknown"),
|
||||||
|
"short": item.get("shortName", ""),
|
||||||
|
"icon": item.get("iconLink"),
|
||||||
|
"count": ri.get("count", 1),
|
||||||
|
})
|
||||||
|
|
||||||
|
task_unlock = b.get("taskUnlock")
|
||||||
|
barter_list.append({
|
||||||
|
"id": b.get("id", ""),
|
||||||
|
"trader": (b.get("trader") or {}).get("name", "Unknown"),
|
||||||
|
"level": b.get("level", 1),
|
||||||
|
"task_unlock": task_unlock.get("name") if task_unlock else None,
|
||||||
|
"reward_name": reward_item.get("name", "Unknown"),
|
||||||
|
"reward_short": reward_item.get("shortName", ""),
|
||||||
|
"reward_icon": reward_item.get("iconLink"),
|
||||||
|
"reward_wiki": reward_item.get("wikiLink"),
|
||||||
|
"reward_count": reward_count,
|
||||||
|
"required": required,
|
||||||
|
})
|
||||||
|
|
||||||
|
barter_list.sort(key=lambda b: (b["trader"], b["level"], b["reward_name"]))
|
||||||
|
return render_template("barters.html", barters=barter_list)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host="0.0.0.0", port=5000, debug=True)
|
app.run(host="0.0.0.0", port=5000, debug=True)
|
||||||
|
|||||||
398
templates/barters.html
Normal file
398
templates/barters.html
Normal file
@@ -0,0 +1,398 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OnlyScavs – Barter Calculator</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--bg: #121212;
|
||||||
|
--panel: #1a1a1a;
|
||||||
|
--panel2: #1e1e1e;
|
||||||
|
--text: #eee;
|
||||||
|
--muted: #888;
|
||||||
|
--border: #2a2a2a;
|
||||||
|
--accent: #9ccfff;
|
||||||
|
--accent2: #ffd580;
|
||||||
|
--good: #6ec96e;
|
||||||
|
--bad: #e06060;
|
||||||
|
--warn: #e0a040;
|
||||||
|
}
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: var(--bg);
|
||||||
|
color: var(--text);
|
||||||
|
margin: 0;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
.page { max-width: 1100px; margin: 0 auto; }
|
||||||
|
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
nav a {
|
||||||
|
color: var(--muted);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 4px 10px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
nav a:hover { color: var(--accent); border-color: var(--accent); }
|
||||||
|
nav a.active { color: var(--accent); border-color: var(--accent); background: #1a2533; }
|
||||||
|
|
||||||
|
h1 { font-size: 1.4rem; margin: 0 0 4px; }
|
||||||
|
.subtitle { color: var(--muted); font-size: 0.88rem; margin: 0 0 20px; }
|
||||||
|
|
||||||
|
/* ── Filters ── */
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.filters input[type=text] {
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
.filters input[type=text]:focus { outline: none; border-color: var(--accent); }
|
||||||
|
.filters select {
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text);
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
.filters label { color: var(--muted); font-size: 0.82rem; }
|
||||||
|
|
||||||
|
/* ── Barter Cards ── */
|
||||||
|
.barter-list { display: flex; flex-direction: column; gap: 12px; }
|
||||||
|
|
||||||
|
.barter-card {
|
||||||
|
background: var(--panel);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 16px 18px;
|
||||||
|
}
|
||||||
|
.barter-card.task-locked {
|
||||||
|
border-color: #3a3020;
|
||||||
|
}
|
||||||
|
|
||||||
|
.barter-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.reward-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
object-fit: contain;
|
||||||
|
background: #111;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.reward-info { flex: 1; min-width: 0; }
|
||||||
|
.reward-name {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.reward-name a { color: inherit; text-decoration: none; }
|
||||||
|
.reward-name a:hover { text-decoration: underline; }
|
||||||
|
.reward-meta {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
.trader-badge {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 99px;
|
||||||
|
background: #1e2a1e;
|
||||||
|
color: var(--good);
|
||||||
|
border: 1px solid #2a3e2a;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.task-badge {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 99px;
|
||||||
|
background: #2a2010;
|
||||||
|
color: var(--warn);
|
||||||
|
border: 1px solid #3a3020;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Required items ── */
|
||||||
|
.required-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--muted);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.required-items {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.req-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
background: var(--panel2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.req-icon {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
object-fit: contain;
|
||||||
|
background: #111;
|
||||||
|
border-radius: 3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.req-icon-placeholder {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background: #222;
|
||||||
|
border-radius: 3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.req-text { flex: 1; min-width: 0; }
|
||||||
|
.req-name {
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.req-count { font-size: 0.75rem; color: var(--muted); }
|
||||||
|
.req-price-wrap {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
.req-price-input {
|
||||||
|
width: 90px;
|
||||||
|
background: #111;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text);
|
||||||
|
font-size: 0.82rem;
|
||||||
|
padding: 3px 6px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.req-price-input:focus { outline: none; border-color: var(--accent); }
|
||||||
|
.req-price-unit { font-size: 0.72rem; color: var(--muted); }
|
||||||
|
|
||||||
|
/* ── Total cost row ── */
|
||||||
|
.total-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
.total-label { font-size: 0.82rem; color: var(--muted); }
|
||||||
|
.total-cost {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent2);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
.total-hint { font-size: 0.75rem; color: var(--muted); }
|
||||||
|
|
||||||
|
/* ── Empty state ── */
|
||||||
|
.empty { color: var(--muted); text-align: center; padding: 48px 0; font-size: 0.9rem; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="page">
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<a href="/">Home</a>
|
||||||
|
<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" class="active">Barters</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<h1>Barter Calculator</h1>
|
||||||
|
<p class="subtitle">Enter flea market prices for required items to see the total rouble cost of any barter.</p>
|
||||||
|
|
||||||
|
<div class="filters">
|
||||||
|
<div>
|
||||||
|
<label>Search</label><br>
|
||||||
|
<input type="text" id="search" placeholder="item name, trader…" oninput="applyFilters()">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Trader</label><br>
|
||||||
|
<select id="traderFilter" onchange="applyFilters()">
|
||||||
|
<option value="">All traders</option>
|
||||||
|
{% set traders = barters | map(attribute='trader') | unique | sort %}
|
||||||
|
{% for t in traders %}
|
||||||
|
<option value="{{ t }}">{{ t }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>LL (min)</label><br>
|
||||||
|
<select id="llFilter" onchange="applyFilters()">
|
||||||
|
<option value="0">Any</option>
|
||||||
|
<option value="1">LL1+</option>
|
||||||
|
<option value="2">LL2+</option>
|
||||||
|
<option value="3">LL3+</option>
|
||||||
|
<option value="4">LL4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>Task locked</label><br>
|
||||||
|
<select id="taskFilter" onchange="applyFilters()">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="no">No task required</option>
|
||||||
|
<option value="yes">Task required</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if barters %}
|
||||||
|
<div class="barter-list" id="barterList">
|
||||||
|
{% for b in barters %}
|
||||||
|
<div class="barter-card{% if b.task_unlock %} task-locked{% endif %}"
|
||||||
|
data-trader="{{ b.trader }}"
|
||||||
|
data-level="{{ b.level }}"
|
||||||
|
data-task="{{ 'yes' if b.task_unlock else 'no' }}"
|
||||||
|
data-search="{{ (b.reward_name ~ ' ' ~ b.trader ~ ' ' ~ (b.required | map(attribute='name') | join(' '))) | lower }}">
|
||||||
|
|
||||||
|
<div class="barter-header">
|
||||||
|
{% if b.reward_icon %}
|
||||||
|
<img class="reward-icon" src="{{ b.reward_icon }}" alt="{{ b.reward_short }}" loading="lazy">
|
||||||
|
{% endif %}
|
||||||
|
<div class="reward-info">
|
||||||
|
<div class="reward-name">
|
||||||
|
{% if b.reward_wiki %}
|
||||||
|
<a href="{{ b.reward_wiki }}" target="_blank" rel="noopener">{{ b.reward_name }}{% if b.reward_count > 1 %} ×{{ b.reward_count }}{% endif %}</a>
|
||||||
|
{% else %}
|
||||||
|
{{ b.reward_name }}{% if b.reward_count > 1 %} ×{{ b.reward_count }}{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="reward-meta">{{ b.trader }} · LL{{ b.level }}</div>
|
||||||
|
</div>
|
||||||
|
<span class="trader-badge">{{ b.trader }} LL{{ b.level }}</span>
|
||||||
|
{% if b.task_unlock %}
|
||||||
|
<span class="task-badge" title="Requires task: {{ b.task_unlock }}">🔒 {{ b.task_unlock }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="required-label">Required items</div>
|
||||||
|
<div class="required-items">
|
||||||
|
{% for ri in b.required %}
|
||||||
|
<div class="req-item">
|
||||||
|
{% if ri.icon %}
|
||||||
|
<img class="req-icon" src="{{ ri.icon }}" alt="{{ ri.short }}" loading="lazy">
|
||||||
|
{% else %}
|
||||||
|
<div class="req-icon-placeholder"></div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="req-text">
|
||||||
|
<div class="req-name" title="{{ ri.name }}">{{ ri.name }}</div>
|
||||||
|
<div class="req-count">× {{ ri.count }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="req-price-wrap">
|
||||||
|
<input class="req-price-input"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
step="1"
|
||||||
|
placeholder="price"
|
||||||
|
title="Price per unit in roubles"
|
||||||
|
data-count="{{ ri.count }}"
|
||||||
|
oninput="recalc(this)">
|
||||||
|
<span class="req-price-unit">₽</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="total-row">
|
||||||
|
<span class="total-label">Total cost:</span>
|
||||||
|
<span class="total-cost" data-total="0">—</span>
|
||||||
|
<span class="total-hint">Enter prices above to calculate</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="empty">No barter data available. Check your connection to tarkov.dev.</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
// Recalculate total cost for a barter card when any price input changes
|
||||||
|
function recalc(input) {
|
||||||
|
const card = input.closest('.barter-card');
|
||||||
|
const inputs = card.querySelectorAll('.req-price-input');
|
||||||
|
let total = 0;
|
||||||
|
let allFilled = true;
|
||||||
|
|
||||||
|
inputs.forEach(inp => {
|
||||||
|
const price = parseFloat(inp.value) || 0;
|
||||||
|
const count = parseInt(inp.dataset.count, 10) || 1;
|
||||||
|
if (inp.value.trim() === '') allFilled = false;
|
||||||
|
total += price * count;
|
||||||
|
});
|
||||||
|
|
||||||
|
const totalEl = card.querySelector('.total-cost');
|
||||||
|
const hintEl = card.querySelector('.total-hint');
|
||||||
|
|
||||||
|
if (total > 0) {
|
||||||
|
totalEl.textContent = total.toLocaleString() + ' ₽';
|
||||||
|
hintEl.textContent = allFilled ? 'all items priced' : 'some items unpriced';
|
||||||
|
} else {
|
||||||
|
totalEl.textContent = '—';
|
||||||
|
hintEl.textContent = 'Enter prices above to calculate';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter cards by search text, trader, level, and task lock
|
||||||
|
function applyFilters() {
|
||||||
|
const search = document.getElementById('search').value.toLowerCase();
|
||||||
|
const trader = document.getElementById('traderFilter').value;
|
||||||
|
const ll = parseInt(document.getElementById('llFilter').value, 10) || 0;
|
||||||
|
const task = document.getElementById('taskFilter').value;
|
||||||
|
|
||||||
|
document.querySelectorAll('.barter-card').forEach(card => {
|
||||||
|
const matchSearch = !search || card.dataset.search.includes(search);
|
||||||
|
const matchTrader = !trader || card.dataset.trader === trader;
|
||||||
|
const matchLL = !ll || parseInt(card.dataset.level, 10) >= ll;
|
||||||
|
const matchTask = !task || card.dataset.task === task;
|
||||||
|
card.style.display = (matchSearch && matchTrader && matchLL && matchTask) ? '' : 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -299,6 +299,8 @@
|
|||||||
<a href="/loadout">Loadout Planner</a>
|
<a href="/loadout">Loadout Planner</a>
|
||||||
|
|
|
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
|
|
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Collector Checklist</h1>
|
<h1>Collector Checklist</h1>
|
||||||
<p class="subtitle">
|
<p class="subtitle">
|
||||||
|
|||||||
@@ -176,6 +176,8 @@
|
|||||||
<a href="/loadout">Loadout Planner</a>
|
<a href="/loadout">Loadout Planner</a>
|
||||||
|
|
|
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
|
|
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>OnlyScavs – Keys</h1>
|
<h1>OnlyScavs – Keys</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,7 @@
|
|||||||
<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>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Key Ratings</h1>
|
<h1>Key Ratings</h1>
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,11 @@
|
|||||||
<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>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="card" href="/barters">
|
||||||
|
<div class="card-icon">🔄</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>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -226,7 +226,8 @@
|
|||||||
<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="/meds">Injectors</a>
|
<a href="/meds">Injectors</a> |
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
</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;">
|
||||||
|
|||||||
@@ -253,6 +253,7 @@
|
|||||||
<a href="/collector">Collector</a>
|
<a href="/collector">Collector</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>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<h1>Injector Quick Reference</h1>
|
<h1>Injector Quick Reference</h1>
|
||||||
|
|||||||
@@ -236,6 +236,8 @@
|
|||||||
<a href="/loadout">Loadout Planner</a>
|
<a href="/loadout">Loadout Planner</a>
|
||||||
|
|
|
|
||||||
<a href="/meds">Injectors</a>
|
<a href="/meds">Injectors</a>
|
||||||
|
|
|
||||||
|
<a href="/barters">Barters</a>
|
||||||
</nav>
|
</nav>
|
||||||
<h1>Quest Trees</h1>
|
<h1>Quest Trees</h1>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user