612 lines
21 KiB
HTML
612 lines
21 KiB
HTML
<!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>
|