diablo2-web/skills.html

653 lines
18 KiB
HTML

<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="referrer" content="no-referrer" />
<title>暗黑破坏神 2 · 全职业技能加点模拟器 (Diablo II Skill Calculator v1.13c)</title>
<style>
:root {
color-scheme: dark;
--bg-void: #060504;
--bg-main: #0c0906;
--bg-panel: rgba(18, 14, 10, 0.97);
--bg-card: #15110c;
--border-dim: #2b2219;
--border-gold: #73572d;
--border-gold-glow: #c8a15a;
--border-gold-bright: #eecf87;
--gold-primary: #c8a15a;
--gold-bright: #eecf87;
--gold-light: #f5eedc;
--text-dim: #998772;
--text-muted: #6e5e4d;
--text-red: #ff4d4d;
--text-green: #55ff55;
--text-blue: #4880ff;
--text-orange: #ff9900;
--text-yellow: #ffff55;
--btn-bg: #1c1611;
--btn-hover: #33271c;
}
* {
box-sizing: border-box;
}
html, body {
margin: 0;
padding: 0;
height: 100vh;
background: var(--bg-void);
color: var(--gold-light);
font: 12px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
overflow: hidden;
user-select: none;
-webkit-user-select: none;
}
/* Custom Scrollbars */
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: #0c0907; }
::-webkit-scrollbar-thumb { background: #2f251c; border-radius: 4px; border: 1px solid #4a3825; }
::-webkit-scrollbar-thumb:hover { background: #523f2f; }
/* Top Header */
header {
height: 50px;
background: linear-gradient(180deg, #1c140e 0%, #100b08 100%);
border-bottom: 2px solid var(--border-gold);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.7);
z-index: 100;
position: relative;
}
.logo-area {
display: flex;
align-items: center;
gap: 10px;
}
.logo-title {
font-family: 'Cinzel', 'Palatino Linotype', 'Book Antiqua', 'Georgia', serif;
font-size: 15px;
font-weight: 700;
color: var(--gold-bright);
letter-spacing: 1px;
text-shadow: 0 2px 4px #000;
}
.version-badge {
font-size: 10px;
padding: 1px 6px;
border-radius: 3px;
background: #251b12;
border: 1px solid var(--border-dim);
color: var(--text-dim);
}
/* Class Selection Tabs */
.class-tabs {
display: flex;
align-items: center;
gap: 4px;
}
.class-btn {
background: #140f0b;
border: 1px solid var(--border-dim);
border-radius: 3px;
color: var(--text-dim);
padding: 6px 12px;
font-size: 12px;
font-family: inherit;
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
gap: 6px;
}
.class-btn:hover {
background: #241b13;
color: var(--gold-bright);
border-color: var(--border-gold);
}
.class-btn.active {
background: linear-gradient(180deg, #38281a 0%, #20160e 100%);
color: var(--gold-bright);
border-color: var(--border-gold-glow);
box-shadow: 0 0 8px rgba(200, 161, 90, 0.35);
font-weight: bold;
}
.header-actions {
display: flex;
align-items: center;
gap: 8px;
}
.header-link {
color: var(--text-dim);
text-decoration: none;
font-size: 11px;
padding: 5px 8px;
border-radius: 3px;
border: 1px solid var(--border-dim);
background: #120e0a;
transition: all 0.15s;
}
.header-link:hover {
color: var(--gold-bright);
border-color: var(--border-gold);
}
.lang-btn {
background: #19120c;
border: 1px solid var(--border-gold);
color: var(--gold-bright);
border-radius: 3px;
padding: 5px 10px;
font-size: 11px;
cursor: pointer;
}
.lang-btn:hover {
background: #2b2014;
}
/* Stats & Summary Bar */
.stats-bar {
height: 44px;
background: #0f0b08;
border-bottom: 1px solid var(--border-dim);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}
.stats-left {
display: flex;
align-items: center;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
}
.stat-label {
color: var(--text-dim);
}
.stat-val {
color: var(--gold-bright);
font-weight: bold;
}
.stat-val.warning {
color: var(--text-red);
}
.stepper-control {
display: inline-flex;
align-items: center;
gap: 4px;
margin-left: 4px;
}
.stepper-btn {
width: 20px;
height: 20px;
border-radius: 3px;
border: 1px solid var(--border-dim);
background: #1b150f;
color: var(--gold-light);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
padding: 0;
font-size: 12px;
line-height: 1;
}
.stepper-btn:hover {
background: #33261a;
border-color: var(--border-gold);
color: var(--gold-bright);
}
.stepper-val {
min-width: 24px;
text-align: center;
color: var(--text-blue);
font-weight: bold;
}
.stats-right {
display: flex;
align-items: center;
gap: 10px;
}
.action-btn {
background: #19130d;
border: 1px solid var(--border-gold);
color: var(--gold-light);
padding: 5px 12px;
border-radius: 3px;
font-size: 12px;
cursor: pointer;
transition: all 0.15s;
display: flex;
align-items: center;
gap: 6px;
}
.action-btn:hover {
background: #312316;
color: var(--gold-bright);
border-color: var(--border-gold-glow);
}
.action-btn.danger:hover {
background: #3d1414;
border-color: #8c2e2e;
color: #ff9999;
}
/* Main Viewport (Isolated Scroll Context) */
main.trees-viewport {
height: calc(100vh - 94px);
overflow-y: auto;
overflow-x: auto;
padding: 24px 16px 40px 16px;
display: flex;
justify-content: center;
align-items: flex-start;
}
.trees-container {
display: flex;
gap: 20px;
justify-content: center;
align-items: flex-start;
}
/* Individual Skill Tree Column (Matching Screenshot) */
.tree-column {
width: 260px;
background: #110d09;
border: 2px solid var(--border-gold);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.8), inset 0 0 10px rgba(0, 0, 0, 0.7);
border-radius: 4px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.tree-header {
padding: 8px 10px;
background: linear-gradient(180deg, #201710 0%, #140e0a 100%);
border-bottom: 1px solid var(--border-dim);
text-align: center;
}
.tree-title {
font-family: 'Cinzel', 'Palatino Linotype', 'Book Antiqua', 'Georgia', serif;
font-size: 12.5px;
font-weight: bold;
color: var(--gold-bright);
letter-spacing: 1px;
text-transform: uppercase;
text-shadow: 0 2px 4px #000;
margin-bottom: 3px;
}
.tree-points-banner {
font-size: 10px;
color: var(--text-dim);
letter-spacing: 0.5px;
text-transform: uppercase;
}
/* Stone Tree Canvas (256x432) */
.tree-canvas {
width: 256px;
height: 432px;
position: relative;
background-color: #0c0907;
background-repeat: no-repeat;
background-position: 0 0;
background-size: 256px 432px;
margin: 0 auto;
}
/* Skill Socket */
.skill-socket {
position: absolute;
width: 48px;
height: 48px;
box-sizing: border-box;
}
.skill-icon {
width: 48px;
height: 48px;
display: block;
image-rendering: pixelated;
cursor: pointer;
transition: transform 0.05s ease, filter 0.15s ease;
border-radius: 2px;
}
.skill-icon:active {
transform: scale(0.96);
}
/* Locked state */
.skill-socket.locked .skill-icon {
filter: grayscale(85%) brightness(0.5);
opacity: 0.65;
cursor: not-allowed;
}
.skill-socket.locked .skill-icon:active {
transform: none;
}
/* Active allocated state */
.skill-socket.active .skill-icon {
filter: drop-shadow(0 0 4px rgba(200, 161, 90, 0.4));
}
/* Maxed 20-point state */
.skill-socket.maxed .skill-icon {
filter: drop-shadow(0 0 6px rgba(238, 207, 135, 0.7));
}
/* Point Badge (Next to icon or bottom-right) */
.skill-pts-badge {
position: absolute;
width: 18px;
height: 18px;
background: #110d09;
border: 1px solid #4a3825;
box-shadow: inset 0 0 4px #000;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
color: var(--text-muted);
pointer-events: none;
}
.skill-pts-badge.has-points {
color: #ffffff;
background: #1c150e;
border-color: var(--border-gold);
text-shadow: 0 1px 2px #000;
}
.skill-pts-badge.has-soft-points {
color: var(--text-blue);
background: #141b24;
border-color: #385d8a;
text-shadow: 0 0 4px rgba(72, 128, 255, 0.5);
}
.skill-pts-badge.is-maxed {
color: var(--gold-bright);
border-color: var(--border-gold-glow);
text-shadow: 0 0 4px rgba(238, 207, 135, 0.6);
}
/* Tree Footer (Tree Soft Points) */
.tree-footer {
padding: 8px 10px;
background: #140e0a;
border-top: 1px solid var(--border-dim);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: 11px;
color: var(--text-dim);
}
/* Floating Diablo II Tooltip */
#skill-tooltip {
position: fixed;
width: 320px;
max-width: 360px;
background: rgba(14, 10, 7, 0.97);
border: 2px solid var(--border-gold);
outline: 1px solid #2f2115;
outline-offset: -3px;
border-radius: 4px;
padding: 12px 14px;
box-shadow: 0 6px 24px rgba(0, 0, 0, 0.95), inset 0 0 12px rgba(0, 0, 0, 0.8);
pointer-events: none;
z-index: 9999;
display: none;
font-size: 12px;
line-height: 1.4;
}
.tt-header {
margin-bottom: 8px;
border-bottom: 1px solid var(--border-dim);
padding-bottom: 6px;
}
.tt-title {
font-family: 'Cinzel', 'Palatino Linotype', 'Book Antiqua', 'Georgia', serif;
font-size: 15px;
font-weight: bold;
color: var(--gold-bright);
text-shadow: 0 2px 4px #000;
}
.tt-subtitle {
font-size: 11px;
color: var(--text-dim);
}
.tt-level-tag {
font-size: 12px;
color: var(--gold-light);
margin-top: 2px;
}
.tt-level-tag .soft-highlight {
color: var(--text-blue);
font-weight: bold;
}
.tt-section {
margin-bottom: 8px;
}
.tt-row {
display: flex;
justify-content: space-between;
margin-bottom: 2px;
}
.tt-label {
color: var(--text-dim);
}
.tt-val {
color: var(--gold-light);
font-weight: 500;
}
.tt-val.err {
color: var(--text-red);
}
.tt-val.ok {
color: var(--text-green);
}
.tt-damage-box {
background: rgba(26, 18, 12, 0.6);
border: 1px solid var(--border-dim);
border-radius: 3px;
padding: 6px 8px;
margin: 6px 0;
}
.tt-damage-main {
font-size: 13.5px;
font-weight: bold;
color: var(--text-orange);
margin-bottom: 3px;
}
.tt-damage-sub {
font-size: 11px;
color: var(--text-dim);
}
.tt-effects-box {
background: rgba(16, 20, 32, 0.6);
border: 1px solid #2a3550;
border-radius: 3px;
padding: 6px 8px;
margin: 6px 0;
}
.tt-effect-line {
font-size: 12px;
color: #5588ee;
line-height: 1.45;
margin-bottom: 2px;
text-shadow: 0 1px 2px #000;
}
.tt-effect-line:last-child {
margin-bottom: 0;
}
.tt-next-level .tt-effect-line {
color: #4a77cc;
font-size: 11px;
}
.tt-synergies {
margin-top: 6px;
padding-top: 6px;
border-top: 1px dashed var(--border-dim);
}
.tt-syn-title {
font-size: 11px;
font-weight: bold;
color: var(--gold-primary);
margin-bottom: 3px;
}
.tt-syn-item {
font-size: 11px;
color: #b0a08e;
padding-left: 6px;
margin-bottom: 2px;
}
.tt-syn-item.active {
color: var(--text-green);
}
.tt-next-level {
margin-top: 8px;
padding-top: 6px;
border-top: 1px solid var(--border-dim);
color: var(--text-dim);
font-size: 11px;
}
.tt-next-title {
color: var(--gold-primary);
font-weight: bold;
margin-bottom: 2px;
}
.tt-desc {
margin-top: 8px;
padding-top: 6px;
border-top: 1px solid var(--border-dim);
font-size: 11px;
color: #a89885;
font-style: italic;
}
.tt-hint {
margin-top: 8px;
padding: 4px 6px;
background: #18120b;
border-radius: 2px;
font-size: 10px;
color: var(--gold-dim);
text-align: center;
}
/* Toast Notification */
#toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%) translateY(100px);
background: #1c150e;
border: 1px solid var(--border-gold-glow);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.8);
color: var(--gold-bright);
padding: 8px 18px;
border-radius: 4px;
font-size: 13px;
pointer-events: none;
transition: transform 0.25s ease, opacity 0.25s ease;
opacity: 0;
z-index: 10000;
}
#toast.show {
transform: translateX(-50%) translateY(0);
opacity: 1;
}
</style>
</head>
<body>
<header>
<div class="logo-area">
<span class="logo-title" id="txt-app-title">暗黑破坏神 2 · 技能加点模拟器</span>
<span class="version-badge">v1.13c</span>
</div>
<nav class="class-tabs" id="class-tabs-container">
<!-- Injected via JS -->
</nav>
<div class="header-actions">
<a href="./loot.html" class="header-link" id="lnk-loot">战利品模拟</a>
<a href="./boss.html" class="header-link" id="lnk-boss">首领挑战</a>
<a href="./acts.html" class="header-link" id="lnk-acts">大地图</a>
<button class="lang-btn" id="btn-lang" title="Toggle Language">中 / EN</button>
</div>
</header>
<div class="stats-bar">
<div class="stats-left">
<div class="stat-item">
<span class="stat-label" id="lbl-req-level">需求角色等级:</span>
<span class="stat-val" id="val-req-level">1</span>
</div>
<div class="stat-item">
<span class="stat-label" id="lbl-points-spent">已用技能点:</span>
<span class="stat-val" id="val-points-spent">0 / 110</span>
</div>
<div class="stat-item">
<span class="stat-label" id="lbl-points-remain">剩余技能点:</span>
<span class="stat-val" id="val-points-remain">110</span>
</div>
<div class="stat-item">
<span class="stat-label" id="lbl-all-skills">+ 全部技能:</span>
<div class="stepper-control">
<button class="stepper-btn" id="btn-all-skills-dec">-</button>
<span class="stepper-val" id="val-all-skills">0</span>
<button class="stepper-btn" id="btn-all-skills-inc">+</button>
</div>
</div>
</div>
<div class="stats-right">
<button class="action-btn danger" id="btn-respec">重置配点 (Respec)</button>
<button class="action-btn" id="btn-copy-link">复制配点链接 (Share)</button>
</div>
</div>
<main class="trees-viewport">
<div class="trees-container" id="trees-container">
<!-- 3 Skill Tree Panels Injected Here -->
</div>
</main>
<!-- Tooltip Element -->
<div id="skill-tooltip"></div>
<!-- Toast Notification -->
<div id="toast">已复制配点链接到剪贴板!</div>
<script type="module" src="/src/skills.ts"></script>
</body>
</html>