Files
onlyscavs/templates/quests.html
serversdwn 84768ae587 Add Loadout Planner and Quest Trees templates
- Created loadout.html for a comprehensive loadout planner, allowing users to filter and view gear options across various categories including guns, armor, helmets, headwear, backpacks, and rigs.
- Implemented a build builder feature to calculate total loadout weight and save builds.
- Added quests.html to display quest trees with trader dependencies, filtering options, and quest completion tracking.
2026-02-22 08:51:28 +00:00

245 lines
7.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html>
<head>
<title>OnlyScavs Quest Trees</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
:root {
--bg: #121212;
--panel: #1a1a1a;
--text: #eee;
--muted: #888;
--border: #2a2a2a;
--accent: #9ccfff;
--done-text: #6ec96e;
--done-bg: #1a2a1a;
--kappa: #f0c040;
}
* { box-sizing: border-box; }
body {
font-family: sans-serif;
background: var(--bg);
color: var(--text);
margin: 0;
padding: 16px;
}
.page { max-width: 960px; margin: 0 auto; }
nav { margin-bottom: 16px; font-size: 0.9rem; }
nav a { color: var(--accent); }
h1 { margin: 0 0 4px; }
.toolbar {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 20px;
}
.filter-btn {
background: var(--panel);
border: 1px solid #444;
color: var(--text);
border-radius: 6px;
padding: 6px 14px;
cursor: pointer;
font-size: 0.9rem;
text-decoration: none;
}
.filter-btn.active {
border-color: var(--kappa);
color: var(--kappa);
}
.legend {
display: flex;
gap: 16px;
font-size: 0.8rem;
color: var(--muted);
flex-wrap: wrap;
}
.legend span { display: flex; align-items: center; gap: 5px; }
/* Trader section */
.trader-section { margin-bottom: 8px; }
.trader-header {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 8px;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 6px;
cursor: pointer;
user-select: none;
}
.trader-header:hover { border-color: #444; }
.trader-name {
font-weight: bold;
font-size: 0.95rem;
flex: 1;
}
.trader-counts { font-size: 0.8rem; color: var(--muted); }
.chevron { color: var(--muted); font-size: 0.8rem; transition: transform 0.15s; }
.trader-section.collapsed .chevron { transform: rotate(-90deg); }
.trader-body { padding: 6px 0 6px 8px; }
.trader-section.collapsed .trader-body { display: none; }
/* Tree nodes */
.tree-root { margin: 4px 0; }
.tree-children {
margin-left: 20px;
border-left: 1px solid var(--border);
padding-left: 10px;
}
.quest-node {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 6px;
border-radius: 4px;
margin: 2px 0;
}
.quest-node:hover { background: #1e1e1e; }
.quest-node.done .quest-label { text-decoration: line-through; color: var(--done-text); }
.quest-node.done { background: var(--done-bg); }
.quest-label { flex: 1; font-size: 0.9rem; }
.quest-label a { color: var(--accent); font-size: 0.75rem; margin-left: 6px; }
.kappa-star { color: var(--kappa); font-size: 0.75rem; flex-shrink: 0; }
.cross-trader {
font-size: 0.75rem;
color: var(--muted);
font-style: italic;
flex-shrink: 0;
}
.toggle-btn {
background: transparent;
border: 1px solid #444;
color: var(--muted);
border-radius: 4px;
padding: 2px 7px;
cursor: pointer;
font-size: 0.75rem;
flex-shrink: 0;
}
.quest-node.done .toggle-btn {
border-color: #3a6a3a;
color: var(--done-text);
}
</style>
</head>
<body>
<div class="page">
<nav>
<a href="/">← Keys</a>
&nbsp;|&nbsp;
<a href="/collector">Collector Checklist</a>
</nav>
<h1>Quest Trees</h1>
<div class="toolbar">
<a class="filter-btn {% if not only_collector %}active{% endif %}" href="/quests">All quests</a>
<a class="filter-btn {% if only_collector %}active{% endif %}" href="/quests?collector=1">★ Collector only</a>
<div class="legend">
<span><span style="color:var(--kappa)"></span> Required for Collector</span>
<span><span style="color:var(--done-text)"></span> Marked done</span>
<span><span style="color:var(--muted);font-style:italic">← Trader</span> Cross-trader dependency</span>
</div>
</div>
{% macro render_node(qid, quest_by_id, children, visible, collector_prereqs) %}
{% set q = quest_by_id[qid] %}
{% set visible_kids = [] %}
{% for cid in children.get(qid, []) %}
{% if cid in visible %}{% set _ = visible_kids.append(cid) %}{% endif %}
{% endfor %}
<div class="tree-root">
<div class="quest-node {% if q.done %}done{% endif %}"
id="qnode-{{ qid }}" data-id="{{ qid }}" data-done="{{ '1' if q.done else '0' }}">
{% if qid in collector_prereqs %}<span class="kappa-star" title="Required for Collector"></span>{% endif %}
<span class="quest-label">
{{ q.name }}
{% if q.wiki_link %}<a href="{{ q.wiki_link }}" target="_blank">wiki</a>{% endif %}
</span>
<button class="toggle-btn" onclick="toggle(this)">
{{ '✓' if q.done else '○' }}
</button>
</div>
{% if visible_kids %}
<div class="tree-children">
{% for cid in visible_kids %}
{% set child = quest_by_id[cid] %}
{% if child.trader != q.trader %}
<div class="quest-node {% if child.done %}done{% endif %}"
data-id="{{ cid }}" data-done="{{ '1' if child.done else '0' }}">
{% if cid in collector_prereqs %}<span class="kappa-star"></span>{% endif %}
<span class="quest-label">
{{ child.name }}
{% if child.wiki_link %}<a href="{{ child.wiki_link }}" target="_blank">wiki</a>{% endif %}
</span>
<span class="cross-trader">← {{ child.trader }}</span>
<button class="toggle-btn" onclick="toggle(this)">{{ '✓' if child.done else '○' }}</button>
</div>
{% else %}
{{ render_node(cid, quest_by_id, children, visible, collector_prereqs) }}
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
{% endmacro %}
{% for trader in traders %}
{% set roots = trader_roots[trader] %}
{% set total_trader = namespace(n=0) %}
{% set done_trader = namespace(n=0) %}
{# count visible quests for this trader #}
{% for qid in visible %}
{% if quest_by_id[qid].trader == trader %}
{% set total_trader.n = total_trader.n + 1 %}
{% if quest_by_id[qid].done %}{% set done_trader.n = done_trader.n + 1 %}{% endif %}
{% endif %}
{% endfor %}
<div class="trader-section" id="trader-{{ trader | replace(' ', '-') }}">
<div class="trader-header" onclick="toggleTrader(this)">
<span class="trader-name">{{ trader }}</span>
<span class="trader-counts">{{ done_trader.n }} / {{ total_trader.n }}</span>
<span class="chevron"></span>
</div>
<div class="trader-body">
{% for root_id in roots %}
{{ render_node(root_id, quest_by_id, children, visible, collector_prereqs) }}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<script>
function toggleTrader(header) {
header.closest('.trader-section').classList.toggle('collapsed');
}
function toggle(btn) {
const node = btn.closest('.quest-node');
const id = node.dataset.id;
const nowDone = node.dataset.done === '1' ? 0 : 1;
fetch('/collector/toggle', {
method: 'POST',
body: new URLSearchParams({ quest_id: id, done: nowDone })
})
.then(r => r.json())
.then(() => {
// Update all nodes with this quest id (may appear as cross-trader duplicate)
document.querySelectorAll(`.quest-node[data-id="${id}"]`).forEach(n => {
n.dataset.done = nowDone;
const b = n.querySelector('.toggle-btn');
if (nowDone) { n.classList.add('done'); b.textContent = '✓'; }
else { n.classList.remove('done'); b.textContent = '○'; }
});
});
}
</script>
</body>
</html>