diablo2-web/loot.html

612 lines
21 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 lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>d2web · 掉落模拟器与精确概率表(Issue #110)</title>
<style>
:root {
color-scheme: dark;
--bg-main: #0b0906;
--bg-panel: rgba(22, 18, 14, 0.94);
--border-color: #382d22;
--gold-primary: #c8a15a;
--gold-light: #e8dcc8;
--text-dim: #9c8d78;
--btn-bg: #221c15;
--btn-hover: #3a2f24;
}
* { box-sizing: border-box; }
html, body {
margin: 0;
padding: 0;
height: 100%;
background: var(--bg-main);
color: var(--gold-light);
font: 13px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
overflow: hidden;
}
/* 顶部导航 */
header {
height: 42px;
background: #14100c;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
}
header h1 {
margin: 0;
font-size: 14px;
color: var(--gold-primary);
letter-spacing: 0.06em;
display: flex;
align-items: center;
gap: 8px;
}
header a {
color: var(--gold-primary);
text-decoration: none;
margin-left: 12px;
}
header a:hover { text-decoration: underline; }
/* 双栏主容器 */
#app-container {
display: grid;
grid-template-columns: 320px 1fr;
height: calc(100vh - 42px);
overflow: hidden;
}
/* 侧边控制栏 */
.sidebar {
background: var(--bg-panel);
border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
height: 100%;
overflow-y: auto;
}
.panel-header {
padding: 10px 14px;
background: #1a1510;
border-bottom: 1px solid var(--border-color);
color: var(--gold-primary);
font-weight: bold;
font-size: 13px;
display: flex;
align-items: center;
justify-content: space-between;
}
.panel-content {
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 10px;
overflow-y: auto;
}
/* 表单元素 */
.form-group {
display: flex;
flex-direction: column;
gap: 4px;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
label {
font-size: 11px;
color: var(--text-dim);
}
input, select {
background: #18140f;
border: 1px solid var(--border-color);
color: var(--gold-light);
padding: 5px 8px;
border-radius: 4px;
font: inherit;
font-size: 12px;
}
input[type="number"] {
font-variant-numeric: tabular-nums;
}
input:focus, select:focus {
outline: none;
border-color: var(--gold-primary);
}
.badge-section {
border-top: 1px dashed var(--border-color);
padding-top: 10px;
margin-top: 4px;
display: flex;
flex-direction: column;
gap: 8px;
}
.section-title {
font-size: 11px;
color: var(--gold-primary);
font-weight: 600;
}
/* 按钮组 */
.btn {
background: var(--btn-bg);
border: 1px solid var(--border-color);
color: var(--gold-light);
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font: inherit;
font-size: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
transition: all 0.15s ease;
}
.btn:hover:not(:disabled) {
background: var(--btn-hover);
border-color: var(--gold-primary);
color: #fff;
}
.btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.btn.primary {
background: #3d2b18;
border-color: #8c6a38;
color: #ffdfa9;
font-weight: bold;
}
.btn.primary:hover:not(:disabled) {
background: #543b20;
border-color: var(--gold-primary);
}
.btn.danger { color: #ff6b6b; }
.btn.danger:hover:not(:disabled) { border-color: #ff6b6b; }
/* 右侧主视口区 */
#main-area {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
/* Tab 切换导航 */
.tabs-nav {
display: flex;
background: #14100c;
border-bottom: 1px solid var(--border-color);
}
.tab-btn {
background: transparent;
border: none;
border-right: 1px solid var(--border-color);
color: var(--text-dim);
padding: 10px 18px;
font: inherit;
font-size: 13px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
gap: 6px;
transition: all 0.15s ease;
}
.tab-btn:hover {
color: var(--gold-light);
background: #1c1813;
}
.tab-btn.active {
color: var(--gold-primary);
background: #1f1913;
border-bottom: 2px solid var(--gold-primary);
}
.tab-content {
flex: 1;
overflow-y: auto;
position: relative;
}
.tab-pane {
display: none;
padding: 16px;
height: 100%;
overflow-y: auto;
}
.tab-pane.active {
display: block;
}
/* 统计卡片网格 */
.stats-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin-bottom: 16px;
}
.stat-card {
background: #14100c;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 10px 14px;
display: flex;
flex-direction: column;
gap: 4px;
}
.stat-title {
font-size: 11px;
color: var(--text-dim);
}
.stat-value {
font-size: 18px;
font-weight: bold;
color: var(--gold-primary);
font-variant-numeric: tabular-nums;
}
/* 进度条 */
.progress-container {
background: #14100c;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 10px 14px;
margin-bottom: 16px;
display: flex;
flex-direction: column;
gap: 6px;
}
.progress-bar-track {
background: #1c1813;
border: 1px solid #382d22;
border-radius: 3px;
height: 10px;
overflow: hidden;
}
.progress-bar-fill {
background: linear-gradient(90deg, #8c6a38, #c8a15a);
height: 100%;
width: 0%;
transition: width 0.1s linear;
}
.progress-info {
display: flex;
justify-content: space-between;
font-size: 11px;
color: var(--text-dim);
}
/* 表格样式 */
.data-table-container {
background: #14100c;
border: 1px solid var(--border-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 16px;
}
.table-header-bar {
padding: 8px 12px;
background: #1c1813;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
font-weight: bold;
font-size: 12px;
color: var(--gold-primary);
}
table {
width: 100%;
border-collapse: collapse;
font-size: 12px;
text-align: left;
}
th {
background: #17130e;
color: var(--text-dim);
padding: 8px 10px;
border-bottom: 1px solid var(--border-color);
font-weight: 600;
font-size: 11px;
}
td {
padding: 6px 10px;
border-bottom: 1px solid #1f1913;
}
tr:hover td {
background: rgba(200, 161, 90, 0.04);
}
/* 品质颜色标识 */
.q-normal { color: #c8c8c8; }
.q-superior { color: #e8e8e8; }
.q-magic { color: #4880ff; }
.q-rare { color: #ffff60; }
.q-set { color: #00ff00; }
.q-unique { color: #c8a15a; }
.q-rune { color: #ff8000; }
.q-gold { color: #ffd700; }
.tag {
display: inline-block;
padding: 1px 5px;
border-radius: 3px;
font-size: 10px;
font-weight: bold;
}
</style>
</head>
<body>
<header>
<h1>
<span>💎</span>
<span>暗黑破坏神 2 · 掉落模拟器与精确概率表 (Drop Simulator & Exact Odds)</span>
<span style="font-size: 11px; color: var(--text-dim); font-weight: normal;">(Issue #110)</span>
</h1>
<div>
<a href="/acts.html">🎮 战役漫步 (acts.html)</a>
<a href="/arena.html">⚔️ 战斗竞技场 (arena.html)</a>
</div>
</header>
<div id="app-container">
<!-- 左栏:掉落参数与控制 -->
<aside class="sidebar" id="control-sidebar">
<div class="panel-header">
<span>🎮 掉落参数设定 (Drop Config)</span>
</div>
<div class="panel-content">
<div class="form-group">
<label>怪物 / 预设 TC (Monster / TC Preset)</label>
<select id="tc-preset">
<option value="Act 1 H2H A">Act 1 H2H A (第一幕 近战小怪)</option>
<option value="Cow (H)">Cow (H) (秘密牛关 母牛)</option>
<option value="Cow King (H)">Cow King (H) (秘密牛关 牛王)</option>
<option value="Andariel (H)">Andariel (H) (第一幕 安达利尔)</option>
<option value="Countess (H)">Countess (H) (第一幕 女伯爵)</option>
<option value="Duriel (H)">Duriel (H) (第二幕 督瑞尔)</option>
<option value="Mephisto (H)">Mephisto (H) (第三幕 墨菲斯托)</option>
<option value="Council (H)">Council (H) (第三幕 议会成员/瘸子)</option>
<option value="Diablo (H)">Diablo (H) (第四幕 暗黑破坏神)</option>
<option value="Baal (H)">Baal (H) (第五幕 巴尔)</option>
<option value="Nihlathak (H)">Nihlathak (H) (第五幕 尼拉塞克)</option>
</select>
</div>
<div class="form-group">
<label>自定义 TC 名称 (Custom TC Input)</label>
<input type="text" id="tc-input" value="Act 1 H2H A" placeholder="例如 Act 1 H2H A, Cow (H), Baal (H)" />
</div>
<div class="form-group" style="margin-top: 2px;">
<label style="display: flex; align-items: center; gap: 6px; cursor: pointer; color: var(--gold-light); font-size: 11px;">
<input type="checkbox" id="quest-drop" style="cursor: pointer;" />
<span>👑 Boss 首次击杀 / Bug 首杀 (Quest Drop)</span>
</label>
</div>
<div class="form-row">
<div class="form-group">
<label>游戏难度 (Difficulty)</label>
<select id="difficulty">
<option value="normal">普通 (Normal)</option>
<option value="nightmare">噩梦 (Nightmare)</option>
<option value="hell" selected>地狱 (Hell)</option>
</select>
</div>
<div class="form-group">
<label>寻宝率 MF % (Magic Find)</label>
<input type="number" id="magic-find" min="0" max="2000" value="0" />
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>游戏总人数 (Game /players)</label>
<input type="number" id="game-players" min="1" max="8" value="1" />
</div>
<div class="form-group">
<label>同队附近人数 (Party Players)</label>
<input type="number" id="party-players" min="1" max="8" value="1" />
</div>
</div>
<div class="badge-section">
<div class="section-title">🎲 蒙特卡洛模拟配置 (Simulation)</div>
<div class="form-row">
<div class="form-group">
<label>模拟轮数 (Runs N)</label>
<select id="sim-runs">
<option value="1000">1,000 轮</option>
<option value="10000" selected>10,000 轮</option>
<option value="100000">100,000 轮</option>
<option value="1000000">1,000,000 轮 (百万)</option>
</select>
</div>
<div class="form-group">
<label>随机种子 (Seed, 可选)</label>
<input type="number" id="sim-seed" placeholder="随机 (留空)" />
</div>
</div>
<div style="display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap;">
<button class="btn primary" id="btn-start" style="flex: 1;">▶ 开始模拟</button>
<button class="btn" id="btn-pause" disabled>⏸ 暂停</button>
<button class="btn danger" id="btn-stop" disabled>⏹ 停止</button>
</div>
<div style="margin-top: 4px;">
<button class="btn" id="btn-calc-exact" style="width: 100%;">📐 计算精确概率表</button>
</div>
</div>
<div class="badge-section" style="font-size: 11px; color: var(--text-dim); line-height: 1.6;">
<div><strong>💡 核心数学机制:</strong></div>
<div>• 真实 1.13c <code>rollTreasureClass</code> 递归展开</div>
<div>• NoDrop 动态双精度人数缩放</div>
<div>• 负数 Picks 确定性枚举,正数 Picks 独立抽取</div>
<div>• 多次抽取独立复合概率 <code>1 - (1 - p)^picks</code></div>
<div>• 真实 ItemRatio 魔法寻宝递减与品质掷骰</div>
</div>
</div>
</aside>
<!-- 右栏:双 Tab 主展示区 -->
<main id="main-area">
<div class="tabs-nav">
<button class="tab-btn active" data-tab="tab-sim">🎲 蒙特卡洛掉落模拟器 (Drop Simulator)</button>
<button class="tab-btn" data-tab="tab-exact">📐 精确掉落概率表与反查 (Exact Odds & Search)</button>
</div>
<div class="tab-content">
<!-- Tab 1: 蒙特卡洛掉落模拟器 -->
<div class="tab-pane active" id="tab-sim">
<!-- 运行进度条 -->
<div class="progress-container">
<div class="progress-info">
<span id="sim-progress-text">就绪:点击「开始模拟」运行 Monte Carlo 掉落抽样</span>
<span>Chunked non-blocking async loop</span>
</div>
<div class="progress-bar-track">
<div class="progress-bar-fill" id="sim-progress-bar"></div>
</div>
</div>
<!-- 统计指标网格 -->
<div class="stats-grid">
<div class="stat-card">
<span class="stat-title">总模拟轮数 (Total Runs)</span>
<span class="stat-value" id="stat-total-runs">0</span>
</div>
<div class="stat-card">
<span class="stat-title">总掉落物品数 (Items Dropped)</span>
<span class="stat-value" id="stat-total-drops">0</span>
</div>
<div class="stat-card">
<span class="stat-title">平均每轮掉落 (Avg Drops/Run)</span>
<span class="stat-value" id="stat-avg-drops">0.00</span>
</div>
<div class="stat-card">
<span class="stat-title">空轮 NoDrop 占比 (Empty Runs)</span>
<span class="stat-value" id="stat-nodrop-runs">0 (0.00%)</span>
</div>
</div>
<!-- 品质分布直方图 -->
<div class="data-table-container">
<div class="table-header-bar">
<span>📊 物品品质分布统计 (Quality Distribution & Histogram)</span>
</div>
<table>
<thead>
<tr>
<th style="width: 180px;">品质类别 (Quality Tier)</th>
<th style="width: 110px;">掉落件数 (Count)</th>
<th style="width: 100px;">占比 (Percentage)</th>
<th>分布直方图 (Histogram)</th>
</tr>
</thead>
<tbody id="quality-tbody">
<tr><td colspan="4" style="text-align: center; color: var(--text-dim);">暂无模拟数据</td></tr>
</tbody>
</table>
</div>
<!-- 详细掉落物列表 -->
<div class="data-table-container">
<div class="table-header-bar">
<span>📦 模拟掉落物品汇总清单 (Dropped Items Summary)</span>
<input type="search" id="items-filter" placeholder="筛选物品代码或名称..." style="padding: 2px 8px; font-size: 11px; width: 180px;" />
</div>
<table>
<thead>
<tr>
<th style="width: 100px;">物品代码 (Code)</th>
<th style="width: 240px;">物品名称 (Name)</th>
<th style="width: 110px;">品质 (Quality)</th>
<th style="width: 120px;">掉落总数 (Count)</th>
<th style="width: 120px;">每轮期望掉落率 (%/Run)</th>
</tr>
</thead>
<tbody id="items-tbody">
<tr><td colspan="5" style="text-align: center; color: var(--text-dim);">暂无掉落物记录</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Tab 2: 精确掉落概率表与反查 -->
<div class="tab-pane" id="tab-exact">
<!-- 概要指标条 -->
<div class="stat-card" style="margin-bottom: 16px;" id="exact-tc-info">
正在加载 TreasureClass 树形解析数据...
</div>
<!-- 物品反查搜索栏 -->
<div class="data-table-container">
<div class="table-header-bar">
<span>🔍 物品全局掉落源反查 (Reverse TC Lookup: type Shako, uap, r33, cap...)</span>
<span id="reverse-match-count" style="font-size: 11px; color: var(--text-dim); font-weight: normal;"></span>
</div>
<div style="padding: 10px 14px; background: #16120d; border-bottom: 1px solid var(--border-color);">
<input type="search" id="reverse-search-input" placeholder="输入物品代码或名称,例如 Shako, uap, r33, cap..." style="width: 100%; padding: 7px 10px; font-size: 13px;" />
</div>
<table>
<thead>
<tr>
<th style="width: 220px;">所属掉落表 (Treasure Class)</th>
<th style="width: 200px;">匹配物品 (Matched Item)</th>
<th style="width: 70px;">挑选次数</th>
<th style="width: 110px;">单次概率 (Single)</th>
<th style="width: 120px;">综合概率 (Compound)</th>
<th style="width: 140px;">掉落几率比 (1 in X Odds)</th>
</tr>
</thead>
<tbody id="reverse-tbody">
<tr><td colspan="6" style="text-align: center; color: var(--text-dim); padding: 18px;">在上方搜索框输入物品代码或名称(例如 Shako、uap、r33、cap)以反查可掉落的 TC 列表</td></tr>
</tbody>
</table>
</div>
<!-- 当前 TC 全叶子节点精确概率表 -->
<div class="data-table-container">
<div class="table-header-bar">
<span>📐 当前 TC 叶子物品精确概率表 (Exact Analytic Drop Table)</span>
</div>
<table>
<thead>
<tr>
<th style="width: 50px;">排名</th>
<th style="width: 100px;">物品代码</th>
<th style="width: 240px;">物品名称</th>
<th style="width: 120px;">单次掉落概率</th>
<th style="width: 120px;">综合概率 1-(1-p)^picks</th>
<th style="width: 120px;">每轮期望掉落数</th>
<th style="width: 140px;">几率比 (1 in X Odds)</th>
</tr>
</thead>
<tbody id="exact-tbody">
<tr><td colspan="7" style="text-align: center; color: var(--text-dim);">正在计算...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</main>
</div>
<script type="module" src="/src/loot.ts"></script>
</body>
</html>