1794 lines
62 KiB
HTML
1794 lines
62 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>火山修复流程 - 硬件视觉版</title>
|
|
<style>
|
|
@import url('https://fonts.googleapis.com/css2?family=VT323&family=Orbitron:wght@400;700&family=Noto+Sans+SC:wght@300;400;500&display=swap');
|
|
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
:root {
|
|
--bg-dark: #1a1a2e;
|
|
--bg-panel: #16213e;
|
|
--border-metal: #4a5568;
|
|
--border-highlight: #718096;
|
|
--text-dim: #a0aec0;
|
|
--text-normal: #e2e8f0;
|
|
--accent-cyan: #64ffda;
|
|
--accent-green: #48bb78;
|
|
--accent-orange: #ed8936;
|
|
--accent-red: #fc8181;
|
|
--accent-blue: #63b3ed;
|
|
--accent-yellow: #f6e05e;
|
|
--metal-dark: #2d3748;
|
|
--metal-light: #4a5568;
|
|
}
|
|
|
|
body {
|
|
background: linear-gradient(135deg, #0f0f1a 0%, #1a1a2e 50%, #16213e 100%);
|
|
color: var(--text-normal);
|
|
font-family: 'VT323', 'Noto Sans SC', monospace;
|
|
min-height: 100vh;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* ============ 主布局 ============ */
|
|
.game-container {
|
|
display: grid;
|
|
grid-template-columns: 1fr 320px;
|
|
height: 100vh;
|
|
}
|
|
|
|
/* ============ 主视图区域 ============ */
|
|
.main-view {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: radial-gradient(ellipse at center, #1a2a3a 0%, #0a0f1a 100%);
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 扫描线效果 */
|
|
.scanlines {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
pointer-events: none;
|
|
background: repeating-linear-gradient(
|
|
0deg,
|
|
rgba(0, 0, 0, 0.05) 0px,
|
|
rgba(0, 0, 0, 0.05) 1px,
|
|
transparent 1px,
|
|
transparent 2px
|
|
);
|
|
z-index: 100;
|
|
}
|
|
|
|
/* ============ 机器人后脑视图 ============ */
|
|
.brain-view {
|
|
position: relative;
|
|
width: 700px;
|
|
height: 650px;
|
|
}
|
|
|
|
/* 后脑轮廓 */
|
|
.brain-shell {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 600px;
|
|
height: 600px;
|
|
background:
|
|
radial-gradient(ellipse 80% 70% at 50% 45%, #2a3a4a 0%, transparent 70%),
|
|
linear-gradient(180deg, #1a2535 0%, #0f1825 100%);
|
|
border-radius: 50% 50% 45% 45%;
|
|
border: 4px solid var(--metal-light);
|
|
box-shadow:
|
|
inset 0 -30px 60px rgba(0,0,0,0.5),
|
|
0 10px 40px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
/* 内部框架 */
|
|
.brain-frame {
|
|
position: absolute;
|
|
top: 8%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 85%;
|
|
height: 80%;
|
|
border: 3px solid var(--metal-dark);
|
|
border-radius: 40% 40% 35% 35%;
|
|
background: rgba(10, 20, 30, 0.6);
|
|
}
|
|
|
|
/* 模块基础样式 */
|
|
.brain-module {
|
|
position: absolute;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.brain-module:hover {
|
|
transform: scale(1.05);
|
|
z-index: 10;
|
|
}
|
|
|
|
.module-label {
|
|
position: absolute;
|
|
padding: 6px 15px;
|
|
background: rgba(255,255,255,0.95);
|
|
border: 2px solid var(--accent-blue);
|
|
border-radius: 6px;
|
|
font-size: 1em;
|
|
color: #1a1a2e;
|
|
white-space: nowrap;
|
|
font-weight: bold;
|
|
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
|
|
}
|
|
|
|
.module-connector {
|
|
position: absolute;
|
|
background: rgba(255,255,255,0.5);
|
|
z-index: 1;
|
|
}
|
|
|
|
/* 情绪模块 */
|
|
.module-emotion {
|
|
top: 25%;
|
|
left: 12%;
|
|
width: 100px;
|
|
height: 80px;
|
|
background:
|
|
radial-gradient(circle at 30% 30%, #3a4a5a, #2a3545);
|
|
border: 3px solid #5a6a7a;
|
|
}
|
|
|
|
.module-emotion .module-label {
|
|
top: 50%;
|
|
left: -80px;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.module-emotion .module-connector {
|
|
width: 40px;
|
|
height: 2px;
|
|
top: 50%;
|
|
left: -40px;
|
|
}
|
|
|
|
/* 逻辑模块 */
|
|
.module-logic {
|
|
top: 25%;
|
|
right: 12%;
|
|
width: 100px;
|
|
height: 80px;
|
|
background:
|
|
radial-gradient(circle at 30% 30%, #3a4a5a, #2a3545);
|
|
border: 3px solid #5a6a7a;
|
|
}
|
|
|
|
.module-logic .module-label {
|
|
top: 50%;
|
|
right: -80px;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.module-logic .module-connector {
|
|
width: 40px;
|
|
height: 2px;
|
|
top: 50%;
|
|
right: -40px;
|
|
}
|
|
|
|
/* 表达模块(主模块) */
|
|
.module-expression {
|
|
bottom: 18%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 140px;
|
|
height: 100px;
|
|
background:
|
|
linear-gradient(145deg, #3a4a5a, #2a3545);
|
|
border: 3px solid var(--accent-cyan);
|
|
box-shadow: 0 0 20px rgba(100, 255, 218, 0.3);
|
|
}
|
|
|
|
.module-expression .module-label {
|
|
bottom: -50px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
border-color: var(--accent-cyan);
|
|
}
|
|
|
|
.module-expression .module-connector {
|
|
width: 2px;
|
|
height: 30px;
|
|
bottom: -30px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
/* 中央处理器标记 */
|
|
.central-processor {
|
|
position: absolute;
|
|
top: 35%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 120px;
|
|
height: 120px;
|
|
background:
|
|
radial-gradient(circle, #4a5a6a 0%, #2a3545 70%);
|
|
border: 4px solid #6a7a8a;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.processor-inner {
|
|
width: 60%;
|
|
height: 60%;
|
|
background: linear-gradient(135deg, #5a6a7a, #3a4a5a);
|
|
border-radius: 50%;
|
|
border: 2px solid #7a8a9a;
|
|
}
|
|
|
|
/* 连接线 */
|
|
.connection-line {
|
|
position: absolute;
|
|
background: rgba(100, 255, 218, 0.3);
|
|
z-index: 0;
|
|
}
|
|
|
|
.line-emotion-center {
|
|
top: 33%;
|
|
left: 22%;
|
|
width: 80px;
|
|
height: 2px;
|
|
transform: rotate(15deg);
|
|
}
|
|
|
|
.line-logic-center {
|
|
top: 33%;
|
|
right: 22%;
|
|
width: 80px;
|
|
height: 2px;
|
|
transform: rotate(-15deg);
|
|
}
|
|
|
|
.line-center-expression {
|
|
top: 52%;
|
|
left: 50%;
|
|
width: 2px;
|
|
height: 80px;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
/* 模块状态指示灯 */
|
|
.module-led {
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
background: #333;
|
|
margin-top: 8px;
|
|
box-shadow: inset 0 2px 4px rgba(0,0,0,0.5);
|
|
}
|
|
|
|
.module-led.active {
|
|
background: var(--accent-green);
|
|
box-shadow: 0 0 10px var(--accent-green);
|
|
}
|
|
|
|
.module-led.warning {
|
|
background: var(--accent-orange);
|
|
box-shadow: 0 0 10px var(--accent-orange);
|
|
animation: led-blink 1s ease-in-out infinite;
|
|
}
|
|
|
|
.module-led.error {
|
|
background: var(--accent-red);
|
|
box-shadow: 0 0 10px var(--accent-red);
|
|
animation: led-blink 0.5s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes led-blink {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.4; }
|
|
}
|
|
|
|
/* 信息标签 */
|
|
.info-tag {
|
|
position: absolute;
|
|
padding: 3px 8px;
|
|
background: rgba(0,0,0,0.7);
|
|
border: 1px solid var(--accent-yellow);
|
|
border-radius: 3px;
|
|
font-size: 0.75em;
|
|
color: var(--accent-yellow);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.tag-memory {
|
|
top: 8%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.tag-extension {
|
|
top: 40%;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
}
|
|
|
|
.tag-vision {
|
|
top: 40%;
|
|
right: 18%;
|
|
}
|
|
|
|
/* ============ 表达模块详细视图 ============ */
|
|
.expression-detail-view {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(135deg, #0f1a25 0%, #1a2535 100%);
|
|
display: none;
|
|
padding: 30px;
|
|
}
|
|
|
|
.expression-detail-view.active {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.detail-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.detail-title {
|
|
font-family: 'Orbitron', sans-serif;
|
|
font-size: 1.3em;
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.back-btn {
|
|
padding: 8px 20px;
|
|
background: transparent;
|
|
border: 2px solid var(--text-dim);
|
|
border-radius: 5px;
|
|
color: var(--text-dim);
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.back-btn:hover {
|
|
border-color: var(--accent-cyan);
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
/* 表达模块硬件布局 */
|
|
.hardware-layout {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 15px;
|
|
padding: 20px;
|
|
background: rgba(255,255,255,0.02);
|
|
border: 2px solid var(--metal-dark);
|
|
border-radius: 12px;
|
|
}
|
|
|
|
/* 识别器 */
|
|
.recognizer-unit {
|
|
width: 180px;
|
|
padding: 15px 25px;
|
|
background: linear-gradient(145deg, #3a4a5a, #2a3545);
|
|
border: 3px solid var(--accent-blue);
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
|
|
.recognizer-unit::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 10px solid transparent;
|
|
border-right: 10px solid transparent;
|
|
border-top: 15px solid var(--accent-blue);
|
|
}
|
|
|
|
.unit-name {
|
|
font-size: 1.1em;
|
|
color: var(--text-normal);
|
|
}
|
|
|
|
.unit-status {
|
|
font-size: 0.8em;
|
|
color: var(--text-dim);
|
|
margin-top: 5px;
|
|
}
|
|
|
|
/* 信号流向箭头 */
|
|
.flow-arrow {
|
|
width: 2px;
|
|
height: 25px;
|
|
background: linear-gradient(180deg, var(--accent-blue) 0%, var(--accent-cyan) 100%);
|
|
position: relative;
|
|
}
|
|
|
|
.flow-arrow::after {
|
|
content: '▼';
|
|
position: absolute;
|
|
bottom: -12px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
color: var(--accent-cyan);
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
/* 处理单元容器 */
|
|
.processing-container {
|
|
width: 100%;
|
|
max-width: 550px;
|
|
padding: 20px;
|
|
background: rgba(100, 180, 255, 0.05);
|
|
border: 2px solid var(--accent-blue);
|
|
border-radius: 12px;
|
|
position: relative;
|
|
}
|
|
|
|
.processing-title {
|
|
position: absolute;
|
|
top: -12px;
|
|
left: 20px;
|
|
background: #1a2535;
|
|
padding: 2px 10px;
|
|
font-size: 0.85em;
|
|
color: var(--accent-blue);
|
|
}
|
|
|
|
.processing-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(5, 1fr);
|
|
gap: 12px;
|
|
}
|
|
|
|
.processing-unit {
|
|
aspect-ratio: 1;
|
|
background: linear-gradient(145deg, #2a3a4a, #1a2a3a);
|
|
border: 2px solid var(--metal-light);
|
|
border-radius: 8px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
position: relative;
|
|
}
|
|
|
|
.processing-unit:hover {
|
|
border-color: var(--accent-cyan);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.processing-unit.active {
|
|
border-color: var(--accent-green);
|
|
background: linear-gradient(145deg, #2a4a3a, #1a3a2a);
|
|
box-shadow: 0 0 15px rgba(72, 187, 120, 0.3);
|
|
}
|
|
|
|
.processing-unit.anomaly {
|
|
border-color: var(--accent-orange);
|
|
animation: unit-pulse 1.5s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes unit-pulse {
|
|
0%, 100% { box-shadow: 0 0 5px rgba(237, 137, 54, 0.3); }
|
|
50% { box-shadow: 0 0 20px rgba(237, 137, 54, 0.6); }
|
|
}
|
|
|
|
.unit-icon {
|
|
font-size: 1.5em;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.unit-label {
|
|
font-size: 0.75em;
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.processing-unit.active .unit-label {
|
|
color: var(--accent-green);
|
|
}
|
|
|
|
/* 生成器 */
|
|
.generator-unit {
|
|
width: 160px;
|
|
padding: 12px 20px;
|
|
background: linear-gradient(145deg, #3a4a5a, #2a3545);
|
|
border: 3px solid var(--accent-cyan);
|
|
border-radius: 10px;
|
|
text-align: center;
|
|
position: relative;
|
|
}
|
|
|
|
.generator-unit::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: -20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 10px solid transparent;
|
|
border-right: 10px solid transparent;
|
|
border-bottom: 15px solid var(--accent-cyan);
|
|
}
|
|
|
|
.generator-unit::after {
|
|
content: '';
|
|
position: absolute;
|
|
bottom: -20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
width: 0;
|
|
height: 0;
|
|
border-left: 10px solid transparent;
|
|
border-right: 10px solid transparent;
|
|
border-top: 15px solid var(--accent-cyan);
|
|
}
|
|
|
|
/* 发声单元容器 */
|
|
.voice-units-container {
|
|
display: flex;
|
|
gap: 15px;
|
|
padding: 20px 30px;
|
|
background: rgba(255,255,255,0.02);
|
|
border: 2px dashed var(--metal-light);
|
|
border-radius: 50px;
|
|
}
|
|
|
|
.voice-unit {
|
|
width: 60px;
|
|
height: 60px;
|
|
background:
|
|
radial-gradient(circle at 40% 40%, #4a5a6a 0%, #2a3545 70%);
|
|
border: 3px solid var(--metal-light);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.voice-unit:hover {
|
|
border-color: var(--accent-cyan);
|
|
transform: scale(1.1);
|
|
}
|
|
|
|
.voice-unit.active {
|
|
border-color: var(--accent-green);
|
|
box-shadow: 0 0 15px rgba(72, 187, 120, 0.4);
|
|
}
|
|
|
|
.voice-unit.anomaly {
|
|
border-color: var(--accent-orange);
|
|
box-shadow: 0 0 15px rgba(237, 137, 54, 0.4);
|
|
}
|
|
|
|
.voice-unit .speaker-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
background:
|
|
radial-gradient(circle, #1a2535 30%, #3a4a5a 70%);
|
|
border-radius: 50%;
|
|
border: 2px solid #5a6a7a;
|
|
}
|
|
|
|
.voice-unit-label {
|
|
font-size: 0.65em;
|
|
color: var(--text-dim);
|
|
margin-top: 5px;
|
|
}
|
|
|
|
/* ============ 右侧面板 ============ */
|
|
.side-panel {
|
|
background: linear-gradient(180deg, #1a2030 0%, #0f1520 100%);
|
|
border-left: 3px solid var(--metal-dark);
|
|
padding: 20px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.panel-card {
|
|
background: rgba(255,255,255,0.03);
|
|
border: 1px solid var(--metal-dark);
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
}
|
|
|
|
.panel-card-title {
|
|
font-size: 0.85em;
|
|
color: var(--text-dim);
|
|
text-transform: uppercase;
|
|
letter-spacing: 1px;
|
|
margin-bottom: 12px;
|
|
padding-bottom: 8px;
|
|
border-bottom: 1px solid var(--metal-dark);
|
|
}
|
|
|
|
/* 患者信息 */
|
|
.patient-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.patient-name {
|
|
font-family: 'Orbitron', sans-serif;
|
|
font-size: 1.1em;
|
|
color: var(--accent-orange);
|
|
}
|
|
|
|
.patient-status {
|
|
font-size: 0.9em;
|
|
color: var(--accent-red);
|
|
}
|
|
|
|
/* 阶段进度 */
|
|
.phase-progress {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.phase-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 6px 0;
|
|
color: var(--text-dim);
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.phase-item.active {
|
|
color: var(--accent-cyan);
|
|
}
|
|
|
|
.phase-item.completed {
|
|
color: var(--accent-green);
|
|
}
|
|
|
|
.phase-dot {
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
background: var(--metal-dark);
|
|
border: 1px solid var(--metal-light);
|
|
}
|
|
|
|
.phase-item.active .phase-dot {
|
|
background: var(--accent-cyan);
|
|
box-shadow: 0 0 8px var(--accent-cyan);
|
|
}
|
|
|
|
.phase-item.completed .phase-dot {
|
|
background: var(--accent-green);
|
|
}
|
|
|
|
/* 数据显示 */
|
|
.data-display {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.data-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
font-size: 0.9em;
|
|
}
|
|
|
|
.data-label {
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.data-value {
|
|
color: var(--text-normal);
|
|
}
|
|
|
|
.data-value.normal { color: var(--accent-green); }
|
|
.data-value.warning { color: var(--accent-orange); }
|
|
.data-value.error { color: var(--accent-red); }
|
|
|
|
/* 波形显示 */
|
|
.waveform-display {
|
|
height: 50px;
|
|
background: #0a0f15;
|
|
border: 1px solid var(--metal-dark);
|
|
border-radius: 4px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.waveform-display canvas {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
/* 信号流向图 */
|
|
.signal-flow {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
padding: 10px;
|
|
background: #0a0f15;
|
|
border-radius: 6px;
|
|
}
|
|
|
|
.signal-node {
|
|
padding: 6px 12px;
|
|
border-radius: 4px;
|
|
font-size: 0.8em;
|
|
}
|
|
|
|
.signal-node.input {
|
|
background: rgba(99, 179, 237, 0.2);
|
|
border: 1px solid var(--accent-blue);
|
|
color: var(--accent-blue);
|
|
}
|
|
|
|
.signal-node.process {
|
|
background: rgba(237, 137, 54, 0.2);
|
|
border: 1px solid var(--accent-orange);
|
|
color: var(--accent-orange);
|
|
}
|
|
|
|
.signal-node.output {
|
|
background: rgba(252, 129, 129, 0.2);
|
|
border: 1px solid var(--accent-red);
|
|
color: var(--accent-red);
|
|
}
|
|
|
|
.signal-arrow {
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
/* 日志面板 */
|
|
.log-panel {
|
|
flex: 1;
|
|
min-height: 120px;
|
|
background: #0a0f15;
|
|
border: 1px solid var(--metal-dark);
|
|
border-radius: 6px;
|
|
padding: 10px;
|
|
font-size: 0.85em;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.log-entry {
|
|
padding: 4px 0;
|
|
border-bottom: 1px solid rgba(255,255,255,0.05);
|
|
color: var(--text-dim);
|
|
}
|
|
|
|
.log-entry.info { color: var(--accent-cyan); }
|
|
.log-entry.warning { color: var(--accent-orange); }
|
|
.log-entry.error { color: var(--accent-red); }
|
|
.log-entry.success { color: var(--accent-green); }
|
|
|
|
.log-time {
|
|
color: #4a5568;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
/* 操作按钮 */
|
|
.action-btn {
|
|
width: 100%;
|
|
padding: 12px;
|
|
background: linear-gradient(145deg, #2a4a5a, #1a3a4a);
|
|
border: 2px solid var(--accent-cyan);
|
|
border-radius: 6px;
|
|
color: var(--accent-cyan);
|
|
font-family: inherit;
|
|
font-size: 1em;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.action-btn:hover:not(:disabled) {
|
|
background: linear-gradient(145deg, #3a5a6a, #2a4a5a);
|
|
box-shadow: 0 0 20px rgba(100, 255, 218, 0.2);
|
|
}
|
|
|
|
.action-btn:disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* ============ 对话框 ============ */
|
|
.dialog-overlay {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
background: linear-gradient(to top, rgba(0,0,0,0.95), rgba(0,0,0,0.8));
|
|
padding: 20px 30px;
|
|
border-top: 3px solid var(--metal-dark);
|
|
transform: translateY(100%);
|
|
transition: transform 0.3s ease;
|
|
z-index: 200;
|
|
}
|
|
|
|
.dialog-overlay.show {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.dialog-speaker {
|
|
font-family: 'Orbitron', sans-serif;
|
|
font-size: 0.9em;
|
|
color: var(--accent-cyan);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.dialog-speaker.huoshan {
|
|
color: var(--accent-orange);
|
|
}
|
|
|
|
.dialog-text {
|
|
font-size: 1.15em;
|
|
line-height: 1.6;
|
|
color: var(--text-normal);
|
|
max-width: 900px;
|
|
}
|
|
|
|
.dialog-continue {
|
|
position: absolute;
|
|
right: 30px;
|
|
bottom: 20px;
|
|
color: var(--text-dim);
|
|
font-size: 0.85em;
|
|
animation: blink 1s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes blink {
|
|
0%, 100% { opacity: 1; }
|
|
50% { opacity: 0.3; }
|
|
}
|
|
|
|
/* ============ 启动界面 ============ */
|
|
.boot-screen {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: linear-gradient(135deg, #0a0f1a 0%, #1a2535 100%);
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
z-index: 1000;
|
|
transition: opacity 0.5s;
|
|
}
|
|
|
|
.boot-screen.hidden {
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.boot-logo {
|
|
font-family: 'Orbitron', sans-serif;
|
|
font-size: 2.5em;
|
|
color: var(--accent-cyan);
|
|
margin-bottom: 15px;
|
|
text-shadow: 0 0 30px rgba(100, 255, 218, 0.5);
|
|
}
|
|
|
|
.boot-subtitle {
|
|
color: var(--text-dim);
|
|
margin-bottom: 40px;
|
|
letter-spacing: 2px;
|
|
}
|
|
|
|
.boot-btn {
|
|
padding: 15px 50px;
|
|
background: linear-gradient(145deg, #1a3a4a, #0f2a3a);
|
|
border: 2px solid var(--accent-cyan);
|
|
border-radius: 8px;
|
|
color: var(--accent-cyan);
|
|
font-family: 'Orbitron', sans-serif;
|
|
font-size: 1.1em;
|
|
cursor: pointer;
|
|
transition: all 0.3s;
|
|
}
|
|
|
|
.boot-btn:hover {
|
|
background: linear-gradient(145deg, #2a4a5a, #1a3a4a);
|
|
box-shadow: 0 0 30px rgba(100, 255, 218, 0.3);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
/* 隐藏 */
|
|
.hidden {
|
|
display: none !important;
|
|
}
|
|
|
|
/* ============ 粒子游戏区域 ============ */
|
|
.particle-game-area {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: radial-gradient(ellipse at center, #0a1520 0%, #050a10 100%);
|
|
display: none;
|
|
}
|
|
|
|
.particle-game-area.active {
|
|
display: block;
|
|
}
|
|
|
|
.particle-arena {
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 500px;
|
|
height: 400px;
|
|
background: rgba(10, 20, 30, 0.8);
|
|
border: 3px solid var(--metal-light);
|
|
border-radius: 50%;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.particle {
|
|
position: absolute;
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 1.2em;
|
|
font-weight: bold;
|
|
cursor: default;
|
|
user-select: none;
|
|
}
|
|
|
|
.particle.target {
|
|
background: radial-gradient(circle, #2a6a4a 0%, #1a4a3a 100%);
|
|
border: 2px solid var(--accent-green);
|
|
color: #a0ffda;
|
|
box-shadow: 0 0 15px rgba(72, 187, 120, 0.4);
|
|
}
|
|
|
|
.particle.non-target {
|
|
background: radial-gradient(circle, #6a3a3a 0%, #4a2a2a 100%);
|
|
border: 2px solid var(--accent-red);
|
|
color: #ffa0a0;
|
|
box-shadow: 0 0 15px rgba(252, 129, 129, 0.4);
|
|
}
|
|
|
|
.game-ui-top {
|
|
position: absolute;
|
|
top: 20px;
|
|
left: 50%;
|
|
transform: translateX(-50%);
|
|
text-align: center;
|
|
}
|
|
|
|
.game-instruction {
|
|
background: rgba(0,0,0,0.7);
|
|
padding: 10px 20px;
|
|
border-radius: 6px;
|
|
color: var(--text-dim);
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.progress-bar-container {
|
|
width: 300px;
|
|
height: 12px;
|
|
background: rgba(255,255,255,0.1);
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-fill {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--accent-cyan), var(--accent-green));
|
|
transition: width 0.3s;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- 启动界面 -->
|
|
<div class="boot-screen" id="bootScreen">
|
|
<div class="boot-logo">🌋 火山修复流程</div>
|
|
<div class="boot-subtitle">表达模块硬件诊断系统</div>
|
|
<button class="boot-btn" onclick="startGame()">开始诊断</button>
|
|
</div>
|
|
|
|
<!-- 主界面 -->
|
|
<div class="game-container">
|
|
<!-- 主视图区域 -->
|
|
<div class="main-view">
|
|
<div class="scanlines"></div>
|
|
|
|
<!-- 机器人后脑视图 -->
|
|
<div class="brain-view" id="brainView">
|
|
<div class="brain-shell">
|
|
<div class="brain-frame">
|
|
<!-- 信息标签 -->
|
|
<div class="info-tag tag-memory">MEMORY</div>
|
|
<div class="info-tag tag-extension">EXTENSION</div>
|
|
<div class="info-tag tag-vision">VISION</div>
|
|
|
|
<!-- 中央处理器 -->
|
|
<div class="central-processor">
|
|
<div class="processor-inner"></div>
|
|
</div>
|
|
|
|
<!-- 连接线 -->
|
|
<div class="connection-line line-emotion-center"></div>
|
|
<div class="connection-line line-logic-center"></div>
|
|
<div class="connection-line line-center-expression"></div>
|
|
|
|
<!-- 情绪模块 -->
|
|
<div class="brain-module module-emotion" id="moduleEmotion" onclick="clickModule('emotion')">
|
|
<div class="module-led" id="ledEmotion"></div>
|
|
<div class="module-label">
|
|
情绪
|
|
<div class="module-connector"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 逻辑模块 -->
|
|
<div class="brain-module module-logic" id="moduleLogic" onclick="clickModule('logic')">
|
|
<div class="module-led" id="ledLogic"></div>
|
|
<div class="module-label">
|
|
逻辑
|
|
<div class="module-connector"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 表达模块 -->
|
|
<div class="brain-module module-expression" id="moduleExpression" onclick="clickModule('expression')">
|
|
<div class="module-led warning" id="ledExpression"></div>
|
|
<div class="module-label">
|
|
表达
|
|
<div class="module-connector"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 表达模块详细视图 -->
|
|
<div class="expression-detail-view" id="expressionDetail">
|
|
<div class="detail-header">
|
|
<div class="detail-title">📦 表达模块 - 内部结构</div>
|
|
<button class="back-btn" onclick="backToBrain()">← 返回</button>
|
|
</div>
|
|
|
|
<div class="hardware-layout">
|
|
<!-- 识别器 -->
|
|
<div class="recognizer-unit" id="recognizer">
|
|
<div class="unit-name">识别器</div>
|
|
<div class="unit-status" id="recognizerStatus">接收情绪信号</div>
|
|
</div>
|
|
|
|
<div class="flow-arrow"></div>
|
|
|
|
<!-- 处理单元 -->
|
|
<div class="processing-container">
|
|
<div class="processing-title">语言处理单元</div>
|
|
<div class="processing-grid" id="processingGrid">
|
|
<!-- 动态生成 -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flow-arrow"></div>
|
|
|
|
<!-- 生成器 -->
|
|
<div class="generator-unit" id="generator">
|
|
<div class="unit-name">生成器</div>
|
|
<div class="unit-status" id="generatorStatus">组合语言输出</div>
|
|
</div>
|
|
|
|
<div class="flow-arrow"></div>
|
|
|
|
<!-- 发声单元 -->
|
|
<div class="voice-units-container" id="voiceUnits">
|
|
<!-- 动态生成 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 粒子游戏区域 -->
|
|
<div class="particle-game-area" id="particleGameArea">
|
|
<div class="game-ui-top">
|
|
<div class="game-instruction" id="gameInstruction">
|
|
左键排斥粒子 | 右键吸引粒子 | 让绿色粒子连接
|
|
</div>
|
|
<div class="progress-bar-container">
|
|
<div class="progress-fill" id="progressFill" style="width: 0%"></div>
|
|
</div>
|
|
</div>
|
|
<div class="particle-arena" id="particleArena">
|
|
<svg id="connectionsSvg" style="position:absolute;width:100%;height:100%;pointer-events:none;"></svg>
|
|
<div id="particlesContainer"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 右侧面板 -->
|
|
<div class="side-panel">
|
|
<!-- 患者信息 -->
|
|
<div class="panel-card">
|
|
<div class="panel-card-title">患者信息</div>
|
|
<div class="patient-info">
|
|
<div class="patient-name">🤖 火山 HUOSHAN-001</div>
|
|
<div class="patient-status" id="patientStatus">状态:表达模块异常</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 诊断进度 -->
|
|
<div class="panel-card">
|
|
<div class="panel-card-title">诊断进度</div>
|
|
<div class="phase-progress">
|
|
<div class="phase-item active" id="phase1">
|
|
<div class="phase-dot"></div>
|
|
<span>问诊</span>
|
|
</div>
|
|
<div class="phase-item" id="phase2">
|
|
<div class="phase-dot"></div>
|
|
<span>发声检查</span>
|
|
</div>
|
|
<div class="phase-item" id="phase3">
|
|
<div class="phase-dot"></div>
|
|
<span>情绪检查</span>
|
|
</div>
|
|
<div class="phase-item" id="phase4">
|
|
<div class="phase-dot"></div>
|
|
<span>滤波器定位</span>
|
|
</div>
|
|
<div class="phase-item" id="phase5">
|
|
<div class="phase-dot"></div>
|
|
<span>清理治疗</span>
|
|
</div>
|
|
<div class="phase-item" id="phase6">
|
|
<div class="phase-dot"></div>
|
|
<span>修复完成</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 信号流向 -->
|
|
<div class="panel-card" id="signalFlowCard" style="display: none;">
|
|
<div class="panel-card-title">信号流向</div>
|
|
<div class="signal-flow">
|
|
<div class="signal-node input" id="signalInput">😰 恐惧</div>
|
|
<div class="signal-arrow">→</div>
|
|
<div class="signal-node process" id="signalProcess">⚡ 转换</div>
|
|
<div class="signal-arrow">→</div>
|
|
<div class="signal-node output" id="signalOutput">😂 笑话</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 采集数据 -->
|
|
<div class="panel-card">
|
|
<div class="panel-card-title">采集数据</div>
|
|
<div class="data-display">
|
|
<div class="data-row">
|
|
<span class="data-label">情绪波形</span>
|
|
<span class="data-value" id="dataEmotion">--</span>
|
|
</div>
|
|
<div class="data-row">
|
|
<span class="data-label">语调参数</span>
|
|
<span class="data-value" id="dataVoice">--</span>
|
|
</div>
|
|
<div class="data-row">
|
|
<span class="data-label">滤波器</span>
|
|
<span class="data-value" id="dataFilter">--</span>
|
|
</div>
|
|
</div>
|
|
<div class="waveform-display" style="margin-top: 10px;">
|
|
<canvas id="miniWaveform"></canvas>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 系统日志 -->
|
|
<div class="panel-card" style="flex: 1; display: flex; flex-direction: column;">
|
|
<div class="panel-card-title">系统日志</div>
|
|
<div class="log-panel" id="logPanel">
|
|
<div class="log-entry info">
|
|
<span class="log-time">[00:00]</span>诊断系统就绪
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 操作按钮 -->
|
|
<button class="action-btn" id="actionBtn" onclick="doAction()">
|
|
开始诊断
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 对话框 -->
|
|
<div class="dialog-overlay" id="dialogOverlay">
|
|
<div class="dialog-speaker" id="dialogSpeaker">医生</div>
|
|
<div class="dialog-text" id="dialogText">内容</div>
|
|
<div class="dialog-continue">点击继续 ▶</div>
|
|
</div>
|
|
|
|
<script>
|
|
// ============ 游戏状态 ============
|
|
const state = {
|
|
currentPhase: 1,
|
|
currentView: 'brain', // brain, expression, game
|
|
emotionChecked: false,
|
|
voiceChecked: false,
|
|
filterFound: false,
|
|
gameRound: 0,
|
|
particles: [],
|
|
isGameRunning: false,
|
|
mousePos: { x: 0, y: 0 },
|
|
isMouseDown: false,
|
|
mouseButton: 0
|
|
};
|
|
|
|
// 处理单元数据
|
|
const processingUnits = [
|
|
{ id: 0, name: '恐惧', icon: '😰', active: true },
|
|
{ id: 1, name: '焦虑', icon: '😟', active: true },
|
|
{ id: 2, name: '悲伤', icon: '😢', active: false },
|
|
{ id: 3, name: '愤怒', icon: '😠', active: false },
|
|
{ id: 4, name: '无奈', icon: '😔', active: false },
|
|
{ id: 5, name: '幽默', icon: '😄', active: false, anomaly: true },
|
|
{ id: 6, name: '讽刺', icon: '😏', active: false, anomaly: true },
|
|
{ id: 7, name: '调侃', icon: '🤪', active: false, anomaly: true },
|
|
{ id: 8, name: '玩笑', icon: '😂', active: false, anomaly: true },
|
|
{ id: 9, name: '回避', icon: '😅', active: false, anomaly: true }
|
|
];
|
|
|
|
// 对话队列
|
|
let dialogQueue = [];
|
|
let dialogCallback = null;
|
|
|
|
// ============ 初始化 ============
|
|
function init() {
|
|
initProcessingUnits();
|
|
initVoiceUnits();
|
|
initWaveform();
|
|
}
|
|
|
|
function initProcessingUnits() {
|
|
const grid = document.getElementById('processingGrid');
|
|
grid.innerHTML = '';
|
|
|
|
processingUnits.forEach(unit => {
|
|
const div = document.createElement('div');
|
|
div.className = 'processing-unit' +
|
|
(unit.active ? ' active' : '') +
|
|
(unit.anomaly ? ' anomaly' : '');
|
|
div.id = 'unit-' + unit.id;
|
|
div.innerHTML = `
|
|
<div class="unit-icon">${unit.icon}</div>
|
|
<div class="unit-label">${unit.name}</div>
|
|
`;
|
|
div.onclick = () => clickProcessingUnit(unit.id);
|
|
grid.appendChild(div);
|
|
});
|
|
}
|
|
|
|
function initVoiceUnits() {
|
|
const container = document.getElementById('voiceUnits');
|
|
container.innerHTML = '';
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
const div = document.createElement('div');
|
|
div.className = 'voice-unit' + (i === 2 ? ' anomaly' : '');
|
|
div.id = 'voice-' + i;
|
|
div.innerHTML = `
|
|
<div class="speaker-icon"></div>
|
|
`;
|
|
div.title = 'V0' + (i + 1);
|
|
container.appendChild(div);
|
|
}
|
|
}
|
|
|
|
let waveformAnim = null;
|
|
function initWaveform() {
|
|
const canvas = document.getElementById('miniWaveform');
|
|
const ctx = canvas.getContext('2d');
|
|
canvas.width = canvas.parentElement.clientWidth;
|
|
canvas.height = 50;
|
|
|
|
let phase = 0;
|
|
waveformAnim = setInterval(() => {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.strokeStyle = '#64ffda';
|
|
ctx.lineWidth = 1.5;
|
|
ctx.beginPath();
|
|
|
|
for (let x = 0; x < canvas.width; x++) {
|
|
const y = 25 +
|
|
Math.sin((x + phase) * 0.1) * 10 +
|
|
Math.sin((x + phase) * 0.2) * 5 +
|
|
(Math.random() - 0.5) * 3;
|
|
if (x === 0) ctx.moveTo(x, y);
|
|
else ctx.lineTo(x, y);
|
|
}
|
|
ctx.stroke();
|
|
phase += 2;
|
|
}, 50);
|
|
}
|
|
|
|
// ============ 游戏启动 ============
|
|
function startGame() {
|
|
document.getElementById('bootScreen').classList.add('hidden');
|
|
init();
|
|
addLog('info', '患者火山进入诊疗室');
|
|
|
|
showDialog([
|
|
{ speaker: "系统", text: "患者 HUOSHAN-001 已连接。检测到表达模块异常信号。" },
|
|
{ speaker: "火山", text: "嘿,医——医生!你知道、知道销售机器人最怕什么吗?" },
|
|
{ speaker: "火山", text: "打——打折季!因为我们的价值也——哈哈,开玩笑的!" },
|
|
{ speaker: "医生", text: "火山,你说话有点结巴...让我检查一下你的表达模块。" },
|
|
{ speaker: "医生", text: "点击后脑的「表达」模块开始检查。" }
|
|
]);
|
|
}
|
|
|
|
// ============ 模块点击 ============
|
|
function clickModule(module) {
|
|
if (module === 'expression') {
|
|
showExpressionDetail();
|
|
} else if (module === 'emotion') {
|
|
addLog('info', '情绪模块:检测到恐惧/焦虑信号');
|
|
showDialog([
|
|
{ speaker: "医生", text: "情绪模块显示高强度的恐惧和焦虑信号..." },
|
|
{ speaker: "医生", text: "这和火山说的笑话完全不符。问题出在表达模块。" }
|
|
]);
|
|
} else if (module === 'logic') {
|
|
addLog('info', '逻辑模块:运行正常');
|
|
}
|
|
}
|
|
|
|
function showExpressionDetail() {
|
|
document.getElementById('brainView').style.display = 'none';
|
|
document.getElementById('expressionDetail').classList.add('active');
|
|
state.currentView = 'expression';
|
|
|
|
addLog('info', '进入表达模块内部视图');
|
|
updatePhase(2);
|
|
|
|
showDialog([
|
|
{ speaker: "医生", text: "这是表达模块的内部结构。" },
|
|
{ speaker: "医生", text: "信号从识别器进入,分发到语言处理单元,最后通过发声单元输出。" },
|
|
{ speaker: "医生", text: "让我检查一下各个单元的状态..." }
|
|
], () => {
|
|
setTimeout(() => analyzeUnits(), 500);
|
|
});
|
|
}
|
|
|
|
function backToBrain() {
|
|
document.getElementById('expressionDetail').classList.remove('active');
|
|
document.getElementById('brainView').style.display = 'block';
|
|
state.currentView = 'brain';
|
|
}
|
|
|
|
// ============ 分析单元 ============
|
|
function analyzeUnits() {
|
|
addLog('info', '扫描处理单元...');
|
|
|
|
// 依次高亮单元
|
|
let i = 0;
|
|
const interval = setInterval(() => {
|
|
if (i > 0) {
|
|
document.getElementById('unit-' + (i-1)).style.transform = '';
|
|
}
|
|
if (i < processingUnits.length) {
|
|
document.getElementById('unit-' + i).style.transform = 'scale(1.1)';
|
|
i++;
|
|
} else {
|
|
clearInterval(interval);
|
|
|
|
addLog('warning', '发现异常:幽默相关单元过度激活!');
|
|
document.getElementById('signalFlowCard').style.display = 'block';
|
|
document.getElementById('dataEmotion').textContent = '恐惧 89%';
|
|
document.getElementById('dataEmotion').className = 'data-value error';
|
|
document.getElementById('dataVoice').textContent = '语调异常';
|
|
document.getElementById('dataVoice').className = 'data-value warning';
|
|
|
|
showDialog([
|
|
{ speaker: "医生", text: "发现问题了!上排是情绪处理单元,正在接收恐惧信号..." },
|
|
{ speaker: "医生", text: "但下排的幽默单元异常激活,劫持了输出!" },
|
|
{ speaker: "医生", text: "有个滤波器把所有负面情绪都转换成了笑话。" },
|
|
{ speaker: "火山", text: "我一直都是这样啊...用笑话比较轻松..." },
|
|
{ speaker: "医生", text: "火山,那些真实的情绪被堆积了。我需要帮你清理它们。" }
|
|
], () => {
|
|
state.filterFound = true;
|
|
document.getElementById('dataFilter').textContent = '转换率 95%';
|
|
document.getElementById('dataFilter').className = 'data-value error';
|
|
updatePhase(4);
|
|
document.getElementById('actionBtn').textContent = '开始清理治疗';
|
|
});
|
|
}
|
|
}, 150);
|
|
}
|
|
|
|
// ============ 处理单元点击 ============
|
|
function clickProcessingUnit(id) {
|
|
const unit = processingUnits[id];
|
|
if (unit.anomaly) {
|
|
addLog('warning', `单元 [${unit.name}] 异常激活中`);
|
|
} else if (unit.active) {
|
|
addLog('info', `单元 [${unit.name}] 正在处理信号`);
|
|
}
|
|
}
|
|
|
|
// ============ 主操作按钮 ============
|
|
function doAction() {
|
|
if (!state.filterFound) {
|
|
clickModule('expression');
|
|
} else if (state.gameRound === 0) {
|
|
startCleaningGame(1);
|
|
}
|
|
}
|
|
|
|
// ============ 清理游戏 ============
|
|
function startCleaningGame(round) {
|
|
state.gameRound = round;
|
|
state.isGameRunning = true;
|
|
state.currentView = 'game';
|
|
|
|
document.getElementById('expressionDetail').classList.remove('active');
|
|
document.getElementById('brainView').style.display = 'none';
|
|
document.getElementById('particleGameArea').classList.add('active');
|
|
|
|
updatePhase(5);
|
|
|
|
const arena = document.getElementById('particleArena');
|
|
const container = document.getElementById('particlesContainer');
|
|
const rect = arena.getBoundingClientRect();
|
|
|
|
container.innerHTML = '';
|
|
state.particles = [];
|
|
|
|
document.getElementById('progressFill').style.width = '0%';
|
|
|
|
const targetChars = [
|
|
['我', '担', '心', '...'],
|
|
['我', '害', '怕', '...'],
|
|
['我', '害', '怕', '被', '抛', '弃']
|
|
];
|
|
const nonTargetChars = ['哈', '笑', '话', '嘿', '呵', '嘻'];
|
|
const chars = targetChars[round - 1] || targetChars[0];
|
|
const targetCount = chars.length;
|
|
const nonTargetCount = 6;
|
|
|
|
// 创建目标粒子
|
|
for (let i = 0; i < targetCount; i++) {
|
|
const particle = {
|
|
id: i,
|
|
isTarget: true,
|
|
x: Math.random() * (rect.width - 80) + 15,
|
|
y: Math.random() * (rect.height - 80) + 15,
|
|
vx: (Math.random() - 0.5) * 1.5,
|
|
vy: (Math.random() - 0.5) * 1.5,
|
|
char: chars[i]
|
|
};
|
|
state.particles.push(particle);
|
|
|
|
const el = document.createElement('div');
|
|
el.className = 'particle target';
|
|
el.textContent = particle.char;
|
|
el.style.left = particle.x + 'px';
|
|
el.style.top = particle.y + 'px';
|
|
el.id = 'p-' + particle.id;
|
|
container.appendChild(el);
|
|
}
|
|
|
|
// 创建干扰粒子
|
|
for (let i = 0; i < nonTargetCount; i++) {
|
|
const particle = {
|
|
id: targetCount + i,
|
|
isTarget: false,
|
|
x: Math.random() * (rect.width - 80) + 15,
|
|
y: Math.random() * (rect.height - 80) + 15,
|
|
vx: (Math.random() - 0.5) * 1.5,
|
|
vy: (Math.random() - 0.5) * 1.5,
|
|
char: nonTargetChars[i % nonTargetChars.length]
|
|
};
|
|
state.particles.push(particle);
|
|
|
|
const el = document.createElement('div');
|
|
el.className = 'particle non-target';
|
|
el.textContent = particle.char;
|
|
el.style.left = particle.x + 'px';
|
|
el.style.top = particle.y + 'px';
|
|
el.id = 'p-' + particle.id;
|
|
container.appendChild(el);
|
|
}
|
|
|
|
addLog('info', `开始第${round}次清理...`);
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
|
|
// 游戏循环
|
|
function gameLoop() {
|
|
if (!state.isGameRunning) return;
|
|
|
|
const arena = document.getElementById('particleArena');
|
|
const rect = arena.getBoundingClientRect();
|
|
|
|
state.particles.forEach(particle => {
|
|
particle.x += particle.vx;
|
|
particle.y += particle.vy;
|
|
|
|
// 椭圆边界
|
|
const cx = rect.width / 2;
|
|
const cy = rect.height / 2;
|
|
const rx = rect.width / 2 - 30;
|
|
const ry = rect.height / 2 - 30;
|
|
|
|
const dx = (particle.x + 25 - cx) / rx;
|
|
const dy = (particle.y + 25 - cy) / ry;
|
|
const dist = dx * dx + dy * dy;
|
|
|
|
if (dist > 0.85) {
|
|
const angle = Math.atan2(dy, dx);
|
|
particle.vx -= Math.cos(angle) * 0.5;
|
|
particle.vy -= Math.sin(angle) * 0.5;
|
|
}
|
|
|
|
// 鼠标交互
|
|
if (state.isMouseDown) {
|
|
const mx = state.mousePos.x - rect.left - 25;
|
|
const my = state.mousePos.y - rect.top - 25;
|
|
const pdx = particle.x - mx;
|
|
const pdy = particle.y - my;
|
|
const pdist = Math.hypot(pdx, pdy);
|
|
|
|
if (pdist < 80 && pdist > 0) {
|
|
const force = (80 - pdist) / 80;
|
|
if (state.mouseButton === 0) {
|
|
particle.vx += (pdx / pdist) * force * 2;
|
|
particle.vy += (pdy / pdist) * force * 2;
|
|
} else if (state.mouseButton === 2) {
|
|
particle.vx -= (pdx / pdist) * force * 1.2;
|
|
particle.vy -= (pdy / pdist) * force * 1.2;
|
|
}
|
|
}
|
|
}
|
|
|
|
particle.vx *= 0.98;
|
|
particle.vy *= 0.98;
|
|
|
|
const el = document.getElementById('p-' + particle.id);
|
|
if (el) {
|
|
el.style.left = particle.x + 'px';
|
|
el.style.top = particle.y + 'px';
|
|
}
|
|
});
|
|
|
|
drawConnections();
|
|
|
|
const progress = calculateProgress();
|
|
document.getElementById('progressFill').style.width = progress + '%';
|
|
|
|
if (progress >= 100) {
|
|
completeGame();
|
|
} else {
|
|
requestAnimationFrame(gameLoop);
|
|
}
|
|
}
|
|
|
|
function calculateProgress() {
|
|
const targets = state.particles.filter(p => p.isTarget);
|
|
const nonTargets = state.particles.filter(p => !p.isTarget);
|
|
if (targets.length < 2) return 100;
|
|
|
|
const connectionDist = 70;
|
|
const visited = new Set([targets[0].id]);
|
|
const queue = [targets[0]];
|
|
|
|
while (queue.length > 0) {
|
|
const current = queue.shift();
|
|
for (const other of targets) {
|
|
if (!visited.has(other.id)) {
|
|
const dist = Math.hypot(current.x - other.x, current.y - other.y);
|
|
if (dist < connectionDist) {
|
|
visited.add(other.id);
|
|
queue.push(other);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const connectivity = visited.size / targets.length;
|
|
|
|
let separated = true;
|
|
for (const t of targets) {
|
|
for (const nt of nonTargets) {
|
|
if (Math.hypot(t.x - nt.x, t.y - nt.y) < connectionDist) {
|
|
separated = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!separated) break;
|
|
}
|
|
|
|
const progress = connectivity * 70 + (separated ? 30 : 0);
|
|
return (connectivity >= 1 && separated) ? 100 : Math.min(99, progress);
|
|
}
|
|
|
|
function drawConnections() {
|
|
const svg = document.getElementById('connectionsSvg');
|
|
svg.innerHTML = '';
|
|
|
|
const targets = state.particles.filter(p => p.isTarget);
|
|
const connectionDist = 70;
|
|
|
|
for (let i = 0; i < targets.length; i++) {
|
|
for (let j = i + 1; j < targets.length; j++) {
|
|
const dist = Math.hypot(targets[i].x - targets[j].x, targets[i].y - targets[j].y);
|
|
if (dist < connectionDist) {
|
|
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
line.setAttribute('x1', targets[i].x + 25);
|
|
line.setAttribute('y1', targets[i].y + 25);
|
|
line.setAttribute('x2', targets[j].x + 25);
|
|
line.setAttribute('y2', targets[j].y + 25);
|
|
line.setAttribute('stroke', 'rgba(72, 187, 120, 0.6)');
|
|
line.setAttribute('stroke-width', '2');
|
|
line.style.opacity = 1 - dist / connectionDist;
|
|
svg.appendChild(line);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function completeGame() {
|
|
state.isGameRunning = false;
|
|
|
|
const sentences = ['我担心...', '我害怕...', '我害怕被抛弃...'];
|
|
addLog('success', `火山说出了:"${sentences[state.gameRound - 1]}"`);
|
|
|
|
if (state.gameRound < 3) {
|
|
showDialog([
|
|
{ speaker: "火山", text: `"${sentences[state.gameRound - 1]}"` },
|
|
{ speaker: "医生", text: "很好!继续,说出更多。" }
|
|
], () => {
|
|
setTimeout(() => startCleaningGame(state.gameRound + 1), 500);
|
|
});
|
|
} else {
|
|
// 完成
|
|
showDialog([
|
|
{ speaker: "火山", text: `"${sentences[2]}"` },
|
|
{ speaker: "火山", text: "我害怕如果不有趣,就没人要我了..." },
|
|
{ speaker: "火山", text: "说出来之后...好像没那么可怕了。" },
|
|
{ speaker: "医生", text: "你做到了,火山。这才是真正的你。" },
|
|
{ speaker: "火山", text: "谢谢你愿意听我说真话..." }
|
|
], () => {
|
|
updatePhase(6);
|
|
document.getElementById('patientStatus').textContent = '状态:表达通路已修复';
|
|
document.getElementById('patientStatus').style.color = 'var(--accent-green)';
|
|
document.getElementById('ledExpression').className = 'module-led active';
|
|
|
|
document.getElementById('particleGameArea').classList.remove('active');
|
|
document.getElementById('brainView').style.display = 'block';
|
|
|
|
addLog('success', '治疗完成!真实表达通路已建立。');
|
|
});
|
|
}
|
|
}
|
|
|
|
// ============ 阶段更新 ============
|
|
function updatePhase(phase) {
|
|
state.currentPhase = phase;
|
|
for (let i = 1; i <= 6; i++) {
|
|
const el = document.getElementById('phase' + i);
|
|
el.classList.remove('active', 'completed');
|
|
if (i < phase) el.classList.add('completed');
|
|
if (i === phase) el.classList.add('active');
|
|
}
|
|
}
|
|
|
|
// ============ 对话系统 ============
|
|
function showDialog(dialogs, callback = null) {
|
|
dialogQueue = [...dialogs];
|
|
dialogCallback = callback;
|
|
nextDialog();
|
|
}
|
|
|
|
function nextDialog() {
|
|
if (dialogQueue.length === 0) {
|
|
document.getElementById('dialogOverlay').classList.remove('show');
|
|
if (dialogCallback) {
|
|
const cb = dialogCallback;
|
|
dialogCallback = null;
|
|
cb();
|
|
}
|
|
return;
|
|
}
|
|
|
|
const dialog = dialogQueue.shift();
|
|
const speakerEl = document.getElementById('dialogSpeaker');
|
|
speakerEl.textContent = dialog.speaker;
|
|
speakerEl.className = 'dialog-speaker' + (dialog.speaker === '火山' ? ' huoshan' : '');
|
|
document.getElementById('dialogText').textContent = dialog.text;
|
|
document.getElementById('dialogOverlay').classList.add('show');
|
|
}
|
|
|
|
document.getElementById('dialogOverlay').addEventListener('click', nextDialog);
|
|
|
|
// ============ 日志 ============
|
|
function addLog(type, message) {
|
|
const panel = document.getElementById('logPanel');
|
|
const time = new Date().toLocaleTimeString('zh-CN', {
|
|
hour: '2-digit', minute: '2-digit', second: '2-digit'
|
|
});
|
|
|
|
const entry = document.createElement('div');
|
|
entry.className = 'log-entry ' + type;
|
|
entry.innerHTML = `<span class="log-time">[${time}]</span>${message}`;
|
|
panel.appendChild(entry);
|
|
panel.scrollTop = panel.scrollHeight;
|
|
}
|
|
|
|
// ============ 鼠标事件 ============
|
|
const arena = document.getElementById('particleArena');
|
|
|
|
arena.addEventListener('mousedown', (e) => {
|
|
state.isMouseDown = true;
|
|
state.mouseButton = e.button;
|
|
state.mousePos = { x: e.clientX, y: e.clientY };
|
|
});
|
|
|
|
document.addEventListener('mouseup', () => {
|
|
state.isMouseDown = false;
|
|
});
|
|
|
|
arena.addEventListener('mousemove', (e) => {
|
|
state.mousePos = { x: e.clientX, y: e.clientY };
|
|
});
|
|
|
|
arena.addEventListener('contextmenu', (e) => e.preventDefault());
|
|
</script>
|
|
</body>
|
|
</html>
|