吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 302|回复: 12
上一主题 下一主题
收起左侧

[其他原创] 俄罗斯方块-夜未央魔改版 - 童年回忆杀!

[复制链接]
跳转到指定楼层
楼主
午夜飘雪 发表于 2026-3-28 14:59 回帖奖励
俄罗斯方块-夜未央魔改版 - 童年回忆杀!

游戏特色:
  • 复古掌机风格界面,完美还原经典掌机手感
  • 多种特殊方块(炸弹、绿宝石、护盾、变换、闪电)带来丰富combo体验
  • 灵活的按键自定义,支持键盘/触屏双操作
  • 背景音乐循环播放,暂停即停、恢复续播
  • 排行榜系统,记录你的最高分
  • 支持自定义音乐上传
  • 手机端自适应布局,随时随地畅玩
操作说明:
  • 方向键/WASD:移动/旋转
  • J/K/L:左旋/右旋/硬降
  • 回车:开始游戏
  • 空格:暂停
  • H:排行榜
  • O:设置
计分规则:
  • 单行消除:10分
  • 双行消除:25分
  • 三行消除:40分
  • 四行消除:60分
  • 特殊方块触发额外加分


目前就这些了. 有修改会在2楼更新!!!
1.0成品在这里
elsfk.zip (2.18 MB, 下载次数: 15)

PC端截图










手机端截图





[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>俄罗斯方块 - 夜未央魔改版</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }

        body {
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Courier New', monospace;
            overflow: hidden;
            padding: 10px;
        }

        .game-wrapper {
            transform-origin: top center;
        }

        .game-body {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 24px;
            padding: 40px 35px;
            box-shadow: 
                0 0 0 5px #1a1a1a,
                0 0 0 10px #3d3d3d,
                0 0 50px rgba(0,0,0,0.8),
                inset 0 3px 15px rgba(255,255,255,0.1);
            position: relative;
        }

        .game-body::before {
            content: 'TETRIS';
            position: absolute;
            top: 12px;
            left: 50%;
            transform: translateX(-50%);
            font-size: 14px;
            font-weight: bold;
            color: #666;
            letter-spacing: 6px;
            text-shadow: 0 2px 3px rgba(0,0,0,0.5);
        }

        .screen-frame {
            background: #4a4a4a;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 
                inset 0 0 30px rgba(0,0,0,0.8),
                0 3px 8px rgba(0,0,0,0.5);
        }

        .screen {
            background: #9bbc0f;
            border-radius: 6px;
            padding: 15px;
            box-shadow: inset 0 0 40px rgba(0,0,0,0.3);
            position: relative;
        }

        .screen::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: repeating-linear-gradient(
                0deg,
                transparent,
                transparent 3px,
                rgba(0,0,0,0.03) 3px,
                rgba(0,0,0,0.03) 6px
            );
            pointer-events: none;
            border-radius: 6px;
        }

        .game-container {
            display: flex;
            gap: 20px;
        }

        .main-area {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        #game-canvas {
            background: #9bbc0f;
            border: 3px solid #306230;
            image-rendering: pixelated;
            display: block;
        }

        .side-panel {
            display: flex;
            flex-direction: column;
            gap: 8px;
            min-width: 120px;
        }

        .info-box {
            background: #8bac0f;
            border: 3px solid #306230;
            padding: 10px;
            text-align: center;
        }

        .info-box .label {
            font-size: 12px;
            color: #306230;
            margin-bottom: 4px;
            text-transform: uppercase;
            letter-spacing: 1px;
            font-weight: bold;
        }

        .info-box .value {
            font-size: 22px;
            font-weight: bold;
            color: #0f380f;
            font-family: 'Courier New', monospace;
        }

        .info-box .sub-value {
            font-size: 14px;
            color: #4a6a2a;
            margin-top: 2px;
            font-weight: bold;
        }

        #next-canvas {
            background: #8bac0f;
            border: 3px solid #306230;
            display: block;
        }

        .controls-section {
            margin-top: 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .d-pad {
            position: relative;
            width: 160px;
            height: 160px;
        }

        .d-pad::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 50px;
            height: 50px;
            background: #1a1a1a;
            border-radius: 50%;
            z-index: 1;
        }

        .d-pad-btn {
            position: absolute;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.1s;
            box-shadow: 0 4px 8px rgba(0,0,0,0.5);
        }

        .d-pad-btn:active {
            transform: scale(0.95);
            filter: brightness(1.5);
        }

        .d-pad-up {
            top: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 50px;
            border-radius: 8px 8px 0 0;
        }

        .d-pad-down {
            bottom: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 50px;
            border-radius: 0 0 8px 8px;
        }

        .d-pad-left {
            left: 8px;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 50px;
            border-radius: 8px 0 0 8px;
        }

        .d-pad-right {
            right: 8px;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 50px;
            border-radius: 0 8px 8px 0;
        }

        .buttons-section {
            display: flex;
            gap: 15px;
            position: relative;
            width: 200px;
            height: 120px;
            justify-content: center;
        }

        .btn-a, .btn-b, .btn-c {
            position: absolute;
            width: 55px;
            height: 55px;
            background: linear-gradient(145deg, #8b0000, #5c0000);
            border-radius: 50%;
            border: none;
            cursor: pointer;
            box-shadow: 
                0 6px 12px rgba(0,0,0,0.5),
                inset 0 3px 6px rgba(255,255,255,0.2);
            font-size: 18px;
            font-weight: bold;
            color: #ff6666;
            text-shadow: 0 1px 3px rgba(0,0,0,0.5);
        }

        .btn-a {
            bottom: 10px;
            left: 10px;
        }

        .btn-b {
            bottom: 35px;
            left: 72px;
        }

        .btn-c {
            bottom: 60px;
            left: 134px;
        }

        .btn-a:active, .btn-b:active, .btn-c:active {
            box-shadow: 
                0 3px 6px rgba(0,0,0,0.5),
                inset 0 3px 6px rgba(0,0,0,0.3);
            transform: scale(0.95);
        }

        .start-select {
            display: flex;
            gap: 12px;
            margin-top: 20px;
            justify-content: center;
            flex-wrap: wrap;
            align-items: center;
        }

        .btn-ss {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #888;
            letter-spacing: 1px;
        }

        .btn-ss:active {
            transform: scale(0.95);
        }

        .btn-reset {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #4a3d2d, #3d2d1d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #aa8866;
            letter-spacing: 1px;
        }

        .btn-reset:active {
            transform: scale(0.95);
        }

        .btn-rank {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #2d3d2d, #1d2d1d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #66aa66;
            letter-spacing: 1px;
        }

        .btn-rank:active {
            transform: scale(0.95);
        }

        .title-section {
            text-align: center;
            margin-bottom: 20px;
        }

        .game-title {
            font-size: 24px;
            font-weight: bold;
            color: #9bbc0f;
            text-shadow: 0 0 15px rgba(155,188,15,0.6);
            letter-spacing: 4px;
        }

        @keyframes blink {
            0%, 50% { opacity: 1; }
            51%, 100% { opacity: 0; }
        }

        .blink {
            animation: blink 1s infinite;
        }

        .speaker {
            position: absolute;
            right: 35px;
            bottom: 35px;
            width: 70px;
            height: 70px;
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            color: #666;
        }

        .speaker:active {
            transform: scale(0.95);
        }

        .speaker-volume {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.85);
            color: #9bbc0f;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 14px;
            font-weight: bold;
            white-space: nowrap;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            z-index: 10;
        }

        .speaker-volume.show {
            opacity: 1;
        }

        .speaker-grille {
            display: grid;
            grid-template-columns: repeat(4, 8px);
            gap: 4px;
        }

        .speaker-hole {
            width: 8px;
            height: 8px;
            background: #0a0a0a;
            border-radius: 50%;
        }

        .speaker-hole.active {
            background: #4a4a4a;
        }

        .overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(155, 188, 15, 0.92);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
            z-index: 10;
            padding: 20px;
        }

        .overlay h2 {
            color: #0f380f;
            font-size: 32px;
            margin-bottom: 15px;
            text-shadow: 3px 3px 0 rgba(0,0,0,0.1);
            font-weight: bold;
        }

        .overlay p {
            color: #306230;
            font-size: 16px;
            margin-bottom: 10px;
            text-align: center;
            font-weight: bold;
        }

        .overlay .final-score {
            color: #0f380f;
            font-size: 26px;
            font-weight: bold;
            margin: 15px 0;
        }

        .mobile-controls {
            display: none;
            margin-top: 25px;
            justify-content: center;
            gap: 15px;
        }

        [url=home.php?mod=space&uid=945662]@media[/url] (max-width: 700px) {
            .mobile-controls {
                display: flex;
                justify-content: space-between;
                align-items: flex-end;
                padding: 0 15px;
                margin-top: 15px;
            }
            .controls-section {
                display: none;
            }
            .game-body {
                padding: 15px 10px;
            }
            .screen-frame {
                padding: 8px;
            }
            .screen {
                padding: 4px;
            }
            .game-container {
                gap: 8px;
            }
            .side-panel {
                min-width: 50px;
                gap: 4px;
            }
            .info-box {
                padding: 4px;
            }
            .info-box .label {
                font-size: 9px;
            }
            .info-box .value {
                font-size: 16px;
            }
            .info-box .sub-value {
                font-size: 10px;
            }
            #next-canvas {
                width: 40px;
                height: 40px;
            }
            .start-select {
                gap: 8px;
                margin-top: 8px;
                flex-direction: row;
                justify-content: center;
                flex-wrap: wrap;
            }
            .btn-ss, .btn-reset, .btn-rank {
                width: 55px;
                height: 14px;
                font-size: 9px;
            }
            .speaker {
                width: 40px;
                height: 40px;
                right: 10px;
                bottom: 10px;
            }
            .speaker-grille {
                grid-template-columns: repeat(4, 5px);
                gap: 2px;
            }
            .speaker-hole {
                width: 5px;
                height: 5px;
            }
            .speaker-volume {
                font-size: 11px;
            }
            .title-section {
                margin-bottom: 10px;
            }
            .game-title {
                font-size: 18px;
            }
            .mobile-dpad {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 5px;
            }
            .mobile-abt-group {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 8px;
            }
            .mobile-row {
                display: flex;
                gap: 6px;
                justify-content: center;
            }
            .mobile-btn {
                width: 50px;
                height: 50px;
                background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
                border: none;
                border-radius: 12px;
                color: #9bbc0f;
                font-size: 20px;
                cursor: pointer;
                box-shadow: 0 5px 10px rgba(0,0,0,0.5);
            }
            .mobile-btn:active {
                transform: scale(0.92);
            }
            .mobile-vol {
                width: 40px;
                height: 40px;
                font-size: 18px;
            }
            .settings-btn {
                width: 30px;
                height: 30px;
                font-size: 16px;
                top: 8px;
                right: 55px;
            }
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .mobile-btn:active {
            transform: scale(0.92);
        }

        .mobile-row {
            display: flex;
            gap: 8px;
        }

        .settings-btn {
            position: absolute;
            top: 12px;
            right: 55px;
            width: 36px;
            height: 36px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-size: 20px;
            color: #888;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            display: none;
        }

        .settings-btn:hover {
            color: #9bbc0f;
        }

        @media (max-width: 700px) {
            body {
                padding: 5px;
            }
            .game-body {
                padding: 12px 8px;
                border-radius: 16px;
            }
            .game-body::before {
                top: 6px;
                font-size: 10px;
            }
            .title-section {
                margin-bottom: 6px;
            }
            .game-title {
                font-size: 16px;
            }
            .screen-frame {
                padding: 5px;
            }
            .screen {
                padding: 3px;
            }
            .game-container {
                gap: 5px;
            }
            .main-area {
                flex: 1;
            }
            .side-panel {
                min-width: 40px;
                gap: 2px;
            }
            .info-box {
                padding: 2px;
            }
            .info-box .label {
                font-size: 7px;
            }
            .info-box .value {
                font-size: 13px;
            }
            .info-box .sub-value {
                font-size: 8px;
            }
            #next-canvas {
                width: 32px;
                height: 32px;
            }
            .start-select {
                gap: 5px;
                margin-top: 5px;
            }
            .btn-ss, .btn-reset, .btn-rank {
                width: 45px;
                height: 11px;
                font-size: 7px;
            }
            .speaker {
                width: 30px;
                height: 30px;
                right: 6px;
                bottom: 6px;
            }
            .speaker-grille {
                grid-template-columns: repeat(4, 4px);
                gap: 1px;
            }
            .speaker-hole {
                width: 4px;
                height: 4px;
            }
            .speaker-volume {
                font-size: 9px;
            }
            .settings-btn {
                width: 26px;
                height: 26px;
                font-size: 13px;
                top: 5px;
                right: 45px;
            }
            .mobile-controls {
                display: flex;
                justify-content: space-between;
                align-items: flex-end;
                padding: 0 8px;
                margin-top: 8px;
            }
            .controls-section {
                display: none;
            }
            .mobile-dpad {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 3px;
            }
            .mobile-abt-group {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 5px;
            }
            .mobile-row {
                display: flex;
                gap: 4px;
            }
            .mobile-btn {
                width: 42px;
                height: 42px;
                background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
                border: none;
                border-radius: 10px;
                color: #9bbc0f;
                font-size: 16px;
                cursor: pointer;
                box-shadow: 0 4px 8px rgba(0,0,0,0.5);
            }
            .mobile-btn:active {
                transform: scale(0.92);
            }
            .mobile-vol {
                width: 32px;
                height: 32px;
                font-size: 14px;
            }
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .mobile-btn:active {
            transform: scale(0.92);
        }

        .mobile-row {
            display: flex;
            gap: 8px;
        }

        .settings-btn {
            position: absolute;
            top: 12px;
            right: 55px;
            width: 36px;
            height: 36px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-size: 20px;
            color: #888;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            display: none;
        }

        .settings-btn:hover {
            color: #9bbc0f;
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .settings-panel {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #2d2d2d;
            border-radius: 12px;
            padding: 25px;
            z-index: 100;
            box-shadow: 0 10px 40px rgba(0,0,0,0.8);
            display: none;
            min-width: 400px;
            max-height: 80vh;
            overflow-y: auto;
        }

        .settings-panel.active {
            display: block;
        }

        .settings-panel h3 {
            color: #9bbc0f;
            margin-bottom: 20px;
            text-align: center;
            font-size: 24px;
            font-weight: bold;
        }

        .settings-section {
            margin-bottom: 20px;
        }

        .settings-section-title {
            color: #888;
            font-size: 13px;
            margin-bottom: 10px;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid #4a4a4a;
            padding-bottom: 5px;
            font-weight: bold;
        }

        .settings-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
            padding: 8px 0;
        }

        .settings-row label {
            color: #aaa;
            font-size: 15px;
            min-width: 90px;
            font-weight: bold;
        }

        .settings-row .key-display {
            background: #1a1a1a;
            padding: 10px 18px;
            border-radius: 6px;
            color: #9bbc0f;
            font-size: 14px;
            min-width: 90px;
            text-align: center;
            cursor: pointer;
            border: 2px solid #4a4a4a;
            font-weight: bold;
        }

        .settings-row .key-display:hover {
            border-color: #9bbc0f;
        }

        .settings-row .key-display.listening {
            border-color: #ff6600;
            color: #ff6600;
        }

        .settings-panel .btn-reset-defaults {
            width: 100%;
            padding: 14px;
            background: #3d3d2d;
            border: none;
            border-radius: 8px;
            color: #ffaa66;
            cursor: pointer;
            font-size: 15px;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .settings-panel .btn-reset-defaults:hover {
            background: #5a5a4a;
        }

        .settings-panel .btn-close {
            width: 100%;
            padding: 16px;
            background: #4a4a4a;
            border: none;
            border-radius: 8px;
            color: #9bbc0f;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            margin-top: 15px;
        }

        .settings-panel .btn-close:hover {
            background: #5a5a5a;
        }

        .settings-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.6);
            z-index: 99;
            display: none;
        }

        .settings-overlay.active {
            display: block;
        }

        .leaderboard-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.85);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 200;
            display: none;
        }

        .leaderboard-overlay.active {
            display: flex;
        }

        .leaderboard-panel {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 16px;
            padding: 30px;
            min-width: 350px;
            max-width: 90vw;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 10px 50px rgba(0,0,0,0.8);
        }

        .leaderboard-panel h2 {
            color: #9bbc0f;
            text-align: center;
            margin-bottom: 20px;
            font-size: 28px;
            font-weight: bold;
        }

        .leaderboard-list {
            list-style: none;
        }

        .leaderboard-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 14px 16px;
            border-bottom: 1px solid #3d3d3d;
            color: #aaa;
            font-size: 15px;
            font-weight: bold;
        }

        .leaderboard-item:first-child {
            color: #ffd700;
            font-weight: bold;
        }

        .leaderboard-item:nth-child(2) {
            color: #c0c0c0;
        }

        .leaderboard-item:nth-child(3) {
            color: #cd7f32;
        }

        .leaderboard-rank {
            min-width: 35px;
            font-weight: bold;
        }

        .leaderboard-name {
            flex: 1;
            text-align: center;
            margin: 0 15px;
            font-weight: bold;
        }

        .leaderboard-score {
            min-width: 90px;
            text-align: right;
            font-weight: bold;
        }

        .leaderboard-empty {
            text-align: center;
            color: #666;
            padding: 30px;
            font-size: 16px;
            font-weight: bold;
        }

        .leaderboard-btn {
            width: 100%;
            padding: 16px;
            background: #4a4a4a;
            border: none;
            border-radius: 8px;
            color: #9bbc0f;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            margin-top: 20px;
        }

        .name-input-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.85);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 200;
            display: none;
        }

        .name-input-overlay.active {
            display: flex;
        }

        .name-input-panel {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 16px;
            padding: 30px;
            min-width: 320px;
            text-align: center;
            box-shadow: 0 10px 50px rgba(0,0,0,0.8);
        }

        .name-input-panel h3 {
            color: #9bbc0f;
            margin-bottom: 20px;
            font-size: 24px;
            font-weight: bold;
        }

        .name-input {
            width: 100%;
            padding: 14px;
            background: #1a1a1a;
            border: 2px solid #4a4a4a;
            border-radius: 8px;
            color: #9bbc0f;
            font-size: 18px;
            font-family: 'Courier New', monospace;
            text-align: center;
            margin-bottom: 15px;
            font-weight: bold;
        }

        .name-input:focus {
            outline: none;
            border-color: #9bbc0f;
        }

        .name-input-btn {
            padding: 14px 35px;
            background: #8b0000;
            border: none;
            border-radius: 8px;
            color: #fff;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
        }

        .name-input-panel p {
            color: #aaa;
            font-size: 15px;
            font-weight: bold;
            margin-bottom: 15px;
        }

        .name-input {
            width: 100%;
            padding: 12px;
            background: #1a1a1a;
            border: 2px solid #4a4a4a;
            border-radius: 8px;
            color: #9bbc0f;
            font-size: 16px;
            font-family: 'Courier New', monospace;
            text-align: center;
            margin-bottom: 15px;
        }

        .name-input:focus {
            outline: none;
            border-color: #9bbc0f;
        }

        .name-input-btn {
            padding: 12px 30px;
            background: #8b0000;
            border: none;
            border-radius: 8px;
            color: #fff;
            cursor: pointer;
            font-size: 14px;
        }

        .effect-text {
            position: absolute;
            font-size: 20px;
            font-weight: bold;
            color: #0f380f;
            text-shadow: 2px 2px 0 #9bbc0f;
            pointer-events: none;
            z-index: 50;
            animation: effectPop 1s ease-out forwards;
        }

        @keyframes effectPop {
            0% { transform: scale(0.5); opacity: 1; }
            50% { transform: scale(1.2); opacity: 1; }
            100% { transform: scale(1) translateY(-30px); opacity: 0; }
        }
    </style>
</head>
<body>
    <div class="game-wrapper" id="gameWrapper">
        <div class="game-body">
            <button class="settings-btn" id="settingsBtn">&#9881;</button>
            
            <div class="title-section">
                <div class="game-title">俄罗斯方块 - 夜未央魔改版</div>
            </div>
            
            <div class="screen-frame">
                <div class="screen">
                    <div class="game-container">
                        <div class="main-area">
                            <canvas id="game-canvas"></canvas>
                        </div>
                        <div class="side-panel">
                            <div class="info-box">
                                <div class="label">分数</div>
                                <div class="value" id="score">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">等级</div>
                                <div class="value" id="level">1</div>
                                <div class="sub-value" id="speed">速度: 1.0x</div>
                            </div>
                            <div class="info-box">
                                <div class="label">行数</div>
                                <div class="value" id="lines">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">最高</div>
                                <div class="value" id="highscore">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">下一个</div>
                                <canvas id="next-canvas"></canvas>
                            </div>
                        </div>
                    </div>
                    <div id="start-overlay" class="overlay">
                        <h2>俄罗斯方块</h2>
                        <h2>夜未央魔改版</h2>
                        <p>按 开始 键开始游戏</p>
                        <p class="blink">▼</p>
                    </div>
                    <div id="gameover-overlay" class="overlay" style="display: none;">
                        <h2>游戏结束</h2>
                        <div class="final-score">得分: <span id="final-score">0</span></div>
                        <p>按 开始 键重新开始</p>
                    </div>
                </div>
            </div>

            <div class="controls-section">
                <div class="d-pad">
                    <button class="d-pad-btn d-pad-up" id="key-up">▲</button>
                    <button class="d-pad-btn d-pad-down" id="key-down">▼</button>
                    <button class="d-pad-btn d-pad-left" id="key-left">&#9664;</button>
                    <button class="d-pad-btn d-pad-right" id="key-right">&#9654;</button>
                </div>
                <div class="buttons-section">
                    <button class="btn-a" id="btn-a">A</button>
                    <button class="btn-b" id="btn-b">B</button>
                    <button class="btn-c" id="btn-c">C</button>
                </div>
            </div>

            <div class="start-select">
                <button class="btn-reset" id="btn-reset">重置</button>
                <button class="btn-ss" id="btn-select">暂停</button>
                <button class="btn-ss" id="btn-start">开始</button>
                <button class="btn-rank" id="btn-rank">排行</button>
            </div>

            <div class="speaker" id="speaker">
                <div class="speaker-volume" id="volumeDisplay">100%</div>
                <div class="speaker-grille">
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                </div>
            </div>

            <div class="mobile-controls">
                <div class="mobile-dpad">
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-up">↑</button>
                    </div>
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-left">←</button>
                        <button class="mobile-btn" id="m-down">↓</button>
                        <button class="mobile-btn" id="m-right">→</button>
                    </div>
                </div>
                <div class="mobile-abt-group">
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-a">A</button>
                        <button class="mobile-btn" id="m-b">B</button>
                        <button class="mobile-btn" id="m-c">C</button>
                    </div>
                    <div class="mobile-row">
                        <button class="mobile-btn mobile-vol" id="m-vol-down">-</button>
                        <button class="mobile-btn mobile-vol" id="m-vol-up">+</button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="settings-overlay" id="settingsOverlay"></div>
        <div class="settings-panel" id="settingsPanel">
        <h3>按键设置</h3>
        
        <div class="settings-section">
            <div class="settings-section-title">移动控制</div>
            <div class="settings-row">
                <label>向左移动</label>
                <div class="key-display" data-action="left">A</div>
            </div>
            <div class="settings-row">
                <label>向右移动</label>
                <div class="key-display" data-action="right">D</div>
            </div>
            <div class="settings-row">
                <label>向下移动</label>
                <div class="key-display" data-action="down">S</div>
            </div>
            <div class="settings-row">
                <label>旋转</label>
                <div class="key-display" data-action="up">W</div>
            </div>
        </div>
        
        <div class="settings-section">
            <div class="settings-section-title">功能按钮</div>
            <div class="settings-row">
                <label>J(左旋)</label>
                <div class="key-display" data-action="btnA">J</div>
            </div>
            <div class="settings-row">
                <label>K(右旋)</label>
                <div class="key-display" data-action="btnB">K</div>
            </div>
            <div class="settings-row">
                <label>L(硬降)</label>
                <div class="key-display" data-action="btnC">L</div>
            </div>
        </div>
        
        <div class="settings-section">
            <div class="settings-section-title">游戏控制</div>
            <div class="settings-row">
                <label>开始游戏</label>
                <div class="key-display" data-action="start">Enter</div>
            </div>
            <div class="settings-row">
                <label>暂停</label>
                <div class="key-display" data-action="pause">空格</div>
            </div>
            <div class="settings-row">
                <label>重置</label>
                <div class="key-display" data-action="reset">R</div>
            </div>
            <div class="settings-row">
                <label>排行榜</label>
                <div class="key-display" data-action="leaderboard">H</div>
            </div>
            <div class="settings-row">
                <label>设置</label>
                <div class="key-display" data-action="settings">O</div>
            </div>
        </div>

        <div class="settings-section">
            <div class="settings-section-title">音乐设置</div>
            <div class="settings-row">
                <label>选择音乐</label>
                <select id="musicSelect" style="background:#1a1a1a;color:#9bbc0f;padding:8px 12px;border-radius:6px;border:2px solid #4a4a4a;font-size:13px;min-width:120px;cursor:pointer;">
                    <option value="">苍天黄土.mp3</option>
                </select>
            </div>
            <div class="settings-row">
                <label>上传音乐</label>
                <input type="file" id="musicFileInput" accept="audio/*" style="display:none;">
                <button id="uploadMusicBtn" style="background:#1a1a1a;color:#9bbc0f;padding:8px 12px;border-radius:6px;border:2px solid #4a4a4a;font-size:12px;cursor:pointer;">上传</button>
            </div>
            <div class="settings-row">
                <label>音乐音量</label>
                <input type="range" id="musicVolume" min="0" max="100" value="100" style="width:120px;cursor:pointer;">
            </div>
        </div>
        
        <button class="btn-reset-defaults" id="resetDefaultsBtn" style="width:100%;padding:14px;background:#4a4a4a;border:none;border-radius:8px;color:#9bbc0f;cursor:pointer;font-size:15px;font-weight:bold;margin-bottom:10px;">恢复默认值</button>
        <button class="btn-close" id="closeSettings">关闭</button>
    </div>

    <div class="name-input-overlay" id="nameInputOverlay">
        <div class="name-input-panel">
            <h3>游戏结束!</h3>
            <p style="color:#aaa;margin-bottom:15px;">得分为 <span id="nameInputScore" style="color:#9bbc0f;font-weight:bold;">0</span></p>
            <input type="text" class="name-input" id="playerNameInput" placeholder="输入你的名字" maxlength="10">
            <button class="name-input-btn" id="submitName">提交</button>
        </div>
    </div>

    <div class="leaderboard-overlay" id="leaderboardOverlay">
        <div class="leaderboard-panel">
            <h2>排行榜</h2>
            <ul class="leaderboard-list" id="leaderboardList"></ul>
            <button class="leaderboard-btn" id="closeLeaderboard">关闭</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        const nextCanvas = document.getElementById('next-canvas');
        const nextCtx = nextCanvas.getContext('2d');

        const COLS = 10;
        const ROWS = 18;
        let BLOCK_SIZE = 40;

        const PIECE_TYPES = ['bomb', 'gem', 'shield', 'ingot', 'lightning', 'black'];
        const PIECE_COLORS = {
            bomb: '#ff4444',
            gem: '#44ff44',
            shield: '#4488ff',
            ingot: '#ff8800',
            lightning: '#aa44ff',
            black: '#333333'
        };
        
        const PIECE_TEXT = {
            bomb: '炸',
            gem: '石',
            shield: '时',
            ingot: '变',
            lightning: '&#9889;',
            black: ''
        };

        const SHAPES = {
            I: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],
            O: [[1,1],[1,1]],
            T: [[0,1,0],[1,1,1],[0,0,0]],
            S: [[0,1,1],[1,1,0],[0,0,0]],
            Z: [[1,1,0],[0,1,1],[0,0,0]],
            J: [[1,0,0],[1,1,1],[0,0,0]],
            L: [[0,0,1],[1,1,1],[0,0,0]]
        };

        const PIECE_NAMES = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];

        let board = [];
        let currentPiece = null;
        let currentX = 0;
        let currentY = 0;
        let nextPiece = null;
        let score = 0;
        let level = 1;
        let lines = 0;
        let highScore = localStorage.getItem('tetrisHighScore') || 0;
        let gameOver = false;
        let gameStarted = false;
        let dropInterval = 1000;
        let lastDrop = 0;
        let isPaused = false;
        let wasPausedBeforeOverlay = false;
        let speedModifier = 1;
        let speedModifierEnd = 0;

        const DEFAULT_KEYS = {
            up: 'KeyW',
            down: 'KeyS',
            left: 'KeyA',
            right: 'KeyD',
            rotateLeft: 'KeyJ',
            rotateRight: 'KeyK',
            btnA: 'KeyJ',
            btnB: 'KeyK',
            btnC: 'KeyL',
            start: 'Enter',
            pause: 'Space',
            reset: 'KeyR',
            leaderboard: 'KeyH',
            settings: 'KeyO'
        };

        let keyBindings = JSON.parse(localStorage.getItem('tetrisKeyBindings')) || {...DEFAULT_KEYS};
        let volume = 1.0;
        let volumeDisplayTimeout = null;

        let audioCtx = null;
        let musicPlaying = false;
        let musicInterval = null;
        let musicInitialized = false;
        let musicVolume = 0.5;
        let bgMusic = null;
        let availableMusicFiles = [];
        let musicPausedAt = 0;

        const MUSIC_CANG_TIAN_HUANG_TU = [
            { freq: 196, dur: 0.4 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.2 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 247, dur: 0.4 },
            { freq: 220, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.6 },
            { freq: 262, dur: 0.4 },
            { freq: 247, dur: 0.2 },
            { freq: 220, dur: 0.2 },
            { freq: 196, dur: 0.6 },
            { freq: 220, dur: 0.4 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.2 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 247, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 220, dur: 0.6 },
            { freq: 196, dur: 0.4 },
            { freq: 220, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.4 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.4 },
            { freq: 262, dur: 0.4 },
            { freq: 247, dur: 0.4 },
            { freq: 220, dur: 0.6 }
        ];

        let musicNoteIndex = 0;

        function initAudio() {
            if (!audioCtx) {
                audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            }
            if (audioCtx.state === 'suspended') {
                audioCtx.resume();
            }
        }

        function playTone(freq, duration, type = 'square', vol = 0.3) {
            if (!audioCtx || volume === 0) return;
            
            const oscillator = audioCtx.createOscillator();
            const gainNode = audioCtx.createGain();
            
            oscillator.connect(gainNode);
            gainNode.connect(audioCtx.destination);
            
            oscillator.type = type;
            oscillator.frequency.setValueAtTime(freq, audioCtx.currentTime);
            
            gainNode.gain.setValueAtTime(vol * volume, audioCtx.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
            
            oscillator.start(audioCtx.currentTime);
            oscillator.stop(audioCtx.currentTime + duration);
        }

        function playMoveSound() {
            playTone(200, 0.05, 'square', 0.15);
        }

        function playRotateSound() {
            playTone(350, 0.08, 'square', 0.15);
        }

        function playDropSound() {
            playTone(150, 0.1, 'square', 0.2);
        }

        function playClearSound() {
            playTone(523, 0.1, 'square', 0.2);
            setTimeout(() => playTone(659, 0.1, 'square', 0.2), 80);
            setTimeout(() => playTone(784, 0.15, 'square', 0.2), 160);
        }

        function playGameOverSound() {
            playTone(200, 0.3, 'sawtooth', 0.2);
            setTimeout(() => playTone(150, 0.3, 'sawtooth', 0.2), 200);
            setTimeout(() => playTone(100, 0.5, 'sawtooth', 0.2), 400);
        }

        function playEffectSound(type) {
            switch(type) {
                case 'bomb':
                    playTone(80, 0.4, 'sawtooth', 0.3);
                    break;
                case 'gem':
                    playTone(800, 0.2, 'sine', 0.2);
                    setTimeout(() => playTone(1000, 0.2, 'sine', 0.2), 80);
                    break;
                case 'shield':
                    playTone(300, 0.3, 'triangle', 0.2);
                    break;
                case 'ingot':
                    playTone(500, 0.15, 'sine', 0.15);
                    setTimeout(() => playTone(700, 0.15, 'sine', 0.15), 60);
                    break;
                case 'lightning':
                    playTone(120, 0.5, 'sawtooth', 0.3);
                    break;
            }
        }

        function playMusicNote() {
            if (!musicPlaying || volume === 0) return;
            
            if (bgMusic && bgMusic.paused === false) return;
            
            const note = MUSIC_CANG_TIAN_HUANG_TU[musicNoteIndex];
            playTone(note.freq, note.dur * 0.9, 'square', 0.12 * musicVolume);
            
            musicNoteIndex = (musicNoteIndex + 1) % MUSIC_CANG_TIAN_HUANG_TU.length;
        }

        function playBgMusic() {
            if (!bgMusic || bgMusic.paused) return;
            playMusicNote();
        }

        function startMusic() {
            if (bgMusic && bgMusic.paused === false) return;
            initAudio();
            musicPlaying = true;
            
            const musicFile = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
            
            if (bgMusic) {
                bgMusic.pause();
                bgMusic = null;
            }
            
            bgMusic = new Audio(musicFile);
            bgMusic.loop = true;
            bgMusic.volume = musicVolume;
            
            bgMusic.play().catch(() => {});
            
            musicInterval = setInterval(playBgMusic, 400);
        }

        function stopMusic() {
            musicPlaying = false;
            if (musicInterval) {
                clearInterval(musicInterval);
                musicInterval = null;
            }
            if (bgMusic) {
                musicPausedAt = bgMusic.currentTime;
                bgMusic.pause();
            }
        }

        function updateMusicVolume(val) {
            musicVolume = val;
            if (bgMusic) {
                bgMusic.volume = musicVolume;
            }
            localStorage.setItem('tetrisMusicVolume', musicVolume);
        }

        function loadAvailableMusic() {
            const musicSelect = document.getElementById('musicSelect');
            const savedMusic = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
            
            availableMusicFiles = ['苍天黄土.mp3'];
            
            musicSelect.innerHTML = '';
            availableMusicFiles.forEach(file => {
                const option = document.createElement('option');
                option.value = file;
                option.textContent = file;
                option.selected = file === savedMusic;
                musicSelect.appendChild(option);
            });
        }

        function isMobile() {
            return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 700;
        }

        function setupCanvasSize() {
            if (isMobile()) {
                BLOCK_SIZE = 18;
                canvas.width = COLS * BLOCK_SIZE;
                canvas.height = ROWS * BLOCK_SIZE;
                nextCanvas.width = 50;
                nextCanvas.height = 50;
            } else {
                BLOCK_SIZE = 40;
                canvas.width = COLS * BLOCK_SIZE;
                canvas.height = ROWS * BLOCK_SIZE;
                nextCanvas.width = 80;
                nextCanvas.height = 80;
            }
        }

        function setupScale() {
            const wrapper = document.getElementById('gameWrapper');
            const gameBody = document.querySelector('.game-body');
            
            if (!gameBody || !wrapper) return;
            
            const screenWidth = window.innerWidth;
            const screenHeight = window.innerHeight;
            
            if (screenWidth <= 700) {
                wrapper.style.transform = 'scale(1)';
                return;
            }
            
            const gameWidth = gameBody.offsetWidth;
            const gameHeight = gameBody.offsetHeight;
            
            if (gameWidth === 0 || gameHeight === 0) return;
            
            if (screenWidth < gameWidth || screenHeight < gameHeight) {
                const scaleX = (screenWidth * 0.95) / gameWidth;
                const scaleY = (screenHeight * 0.95) / gameHeight;
                const scale = Math.min(scaleX, scaleY, 1);
                wrapper.style.transform = `scale(${scale})`;
            } else {
                wrapper.style.transform = 'scale(1)';
            }
        }

        function getRandomPieceType() {
            const rand = Math.random();
            if (rand < 0.6) return PIECE_TYPES[Math.floor(Math.random() * 5)];
            return 'black';
        }

        document.getElementById('highscore').textContent = highScore;

        function initBoard() {
            board = [];
            for (let r = 0; r < ROWS; r++) {
                board[r] = [];
                for (let c = 0; c < COLS; c++) {
                    board[r][c] = { type: null };
                }
            }
        }

        function createPiece(type) {
            const shape = SHAPES[type].map(row => [...row]);
            const cells = [];
            for (let r = 0; r < shape.length; r++) {
                for (let c = 0; c < shape[r].length; c++) {
                    if (shape[r][c]) {
                        cells.push(getRandomPieceType());
                    }
                }
            }
            return { type, shape, cells };
        }

        function getRandomPiece() {
            const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)];
            return createPiece(type);
        }

        function drawPieceGraphic(ctx, x, y, type, size) {
            const color = PIECE_COLORS[type];
            const padding = 2;
            const text = PIECE_TEXT[type];
            
            ctx.fillStyle = color;
            ctx.fillRect(x + padding, y + padding, size - padding * 2, size - padding * 2);
            
            ctx.fillStyle = '#000';
            ctx.fillRect(x + padding, y + padding, size - padding * 2, 2);
            ctx.fillRect(x + padding, y + padding, 2, size - padding * 2);
            
            ctx.fillStyle = '#fff';
            ctx.globalAlpha = 0.3;
            ctx.fillRect(x + size - padding - 2, y + padding, 2, size - padding * 2);
            ctx.fillRect(x + padding, y + size - padding - 2, size - padding * 2, 2);
            ctx.globalAlpha = 1;
            
            if (text) {
                ctx.fillStyle = '#fff';
                ctx.font = `bold ${size * 0.45}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(text, x + size/2, y + size/2);
            }
        }

        function drawBlock(ctx, x, y, type, size = BLOCK_SIZE) {
            drawPieceGraphic(ctx, x * size, y * size, type, size);
        }

        function drawBoard() {
            ctx.fillStyle = '#9bbc0f';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            for (let r = 0; r < ROWS; r++) {
                for (let c = 0; c < COLS; c++) {
                    if (board[r][c].type) {
                        drawBlock(ctx, c, r, board[r][c].type);
                    }
                }
            }

            ctx.strokeStyle = '#306230';
            ctx.lineWidth = 1;
            for (let r = 0; r <= ROWS; r++) {
                ctx.beginPath();
                ctx.moveTo(0, r * BLOCK_SIZE);
                ctx.lineTo(canvas.width, r * BLOCK_SIZE);
                ctx.stroke();
            }
            for (let c = 0; c <= COLS; c++) {
                ctx.beginPath();
                ctx.moveTo(c * BLOCK_SIZE, 0);
                ctx.lineTo(c * BLOCK_SIZE, canvas.height);
                ctx.stroke();
            }
        }

        function drawNextPiece() {
            nextCtx.fillStyle = '#8bac0f';
            nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);

            if (!nextPiece) return;

            const blockSize = isMobile() ? 12 : 18;
            const offsetX = (nextCanvas.width - nextPiece.shape[0].length * blockSize) / 2;
            const offsetY = (nextCanvas.height - nextPiece.shape.length * blockSize) / 2;

            let cellIndex = 0;
            for (let r = 0; r < nextPiece.shape.length; r++) {
                for (let c = 0; c < nextPiece.shape[r].length; c++) {
                    if (nextPiece.shape[r][c]) {
                        const type = nextPiece.cells[cellIndex++];
                        drawPieceGraphic(nextCtx, offsetX + c * blockSize, offsetY + r * blockSize, type, blockSize);
                    }
                }
            }
        }

        function validMove(piece, x, y) {
            for (let r = 0; r < piece.shape.length; r++) {
                for (let c = 0; c < piece.shape[r].length; c++) {
                    if (piece.shape[r][c]) {
                        const newX = x + c;
                        const newY = y + r;
                        if (newX < 0 || newX >= COLS || newY >= ROWS) {
                            return false;
                        }
                        if (newY >= 0 && board[newY][newX].type) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }

        function rotateMatrixCW(matrix) {
            const n = matrix.length;
            const result = [];
            for (let i = 0; i < n; i++) {
                result[i] = [];
                for (let j = 0; j < n; j++) {
                    result[i][j] = matrix[n - 1 - j][i];
                }
            }
            return result;
        }

        function rotateMatrixCCW(matrix) {
            const n = matrix.length;
            const result = [];
            for (let i = 0; i < n; i++) {
                result[i] = [];
                for (let j = 0; j < n; j++) {
                    result[i][j] = matrix[j][n - 1 - i];
                }
            }
            return result;
        }

        function rotateLeft() {
            if (!currentPiece || gameOver || isPaused) return;
            const rotated = rotateMatrixCCW(currentPiece.shape);
            const original = currentPiece.shape;
            currentPiece.shape = rotated;
            if (!validMove(currentPiece, currentX, currentY)) {
                currentPiece.shape = original;
            } else {
                playRotateSound();
            }
        }

        function rotateRight() {
            if (!currentPiece || gameOver || isPaused) return;
            const rotated = rotateMatrixCW(currentPiece.shape);
            const original = currentPiece.shape;
            currentPiece.shape = rotated;
            if (!validMove(currentPiece, currentX, currentY)) {
                currentPiece.shape = original;
            } else {
                playRotateSound();
            }
        }

        function showEffectText(text, x, y) {
            const effectEl = document.createElement('div');
            effectEl.className = 'effect-text';
            effectEl.textContent = text;
            effectEl.style.left = (x * BLOCK_SIZE + 50) + 'px';
            effectEl.style.top = (y * BLOCK_SIZE + 100) + 'px';
            document.querySelector('.screen').appendChild(effectEl);
            setTimeout(() => effectEl.remove(), 1000);
        }

        function triggerBombEffect(count) {
            const size = count + 2;
            const centerX = Math.floor(Math.random() * (COLS - size));
            const centerY = Math.floor(Math.random() * (ROWS - size));
            
            let cleared = 0;
            for (let r = Math.max(0, centerY - 1); r < Math.min(ROWS, centerY + size); r++) {
                for (let c = Math.max(0, centerX - 1); c < Math.min(COLS, centerX + size); c++) {
                    if (board[r][c].type) {
                        board[r][c] = { type: null };
                        cleared++;
                    }
                }
            }
            
            playEffectSound('bomb');
            showEffectText(`炸弹${size}x${size}!`, centerX, centerY);
            return cleared;
        }

        function triggerGemEffect(count) {
            let added = 0;
            
            for (let i = 0; i < count; i++) {
                const blackCells = [];
                for (let r = 0; r < ROWS; r++) {
                    for (let c = 0; c < COLS; c++) {
                        if (board[r][c].type === 'black') {
                            blackCells.push({r, c});
                        }
                    }
                }
                
                if (blackCells.length > 0) {
                    const cell = blackCells[Math.floor(Math.random() * blackCells.length)];
                    board[cell.r][cell.c] = { type: PIECE_TYPES[Math.floor(Math.random() * 5)] };
                    added++;
                }
            }
            
            playEffectSound('gem');
            showEffectText(`绿宝石+${added}!`, 5, 5);
            return added;
        }

        function triggerShieldEffect(count) {
            const duration = count * 1000;
            speedModifier = 2.0;
            speedModifierEnd = Date.now() + duration;
            
            playEffectSound('shield');
            showEffectText(`减速${count}秒!`, 5, 5);
        }

        function triggerIngotEffect(count) {
            let changed = 0;
            for (let i = 0; i < count; i++) {
                const x = Math.floor(Math.random() * COLS);
                const y = Math.floor(Math.random() * ROWS);
                if (board[y][x].type && board[y][x].type !== 'black') {
                    board[y][x] = { type: PIECE_TYPES[Math.floor(Math.random() * 5)] };
                    changed++;
                }
            }
            
            playEffectSound('ingot');
            showEffectText(`换图${changed}!`, 5, 5);
        }

        function triggerLightningEffect(count) {
            let cleared = 0;
            for (let r = 0; r < ROWS; r++) {
                for (let c = 0; c < COLS; c++) {
                    if (board[r][c].type === 'lightning') {
                        board[r][c] = { type: null };
                        cleared++;
                    }
                }
            }
            
            playEffectSound('lightning');
            showEffectText(`闪电${cleared}!`, 5, 5);
            return cleared;
        }

        function processLineEffects(clearedTypes) {
            let extraScore = 0;
            
            const counts = {};
            for (const type of clearedTypes) {
                if (type !== 'black') {
                    counts[type] = (counts[type] || 0) + 1;
                }
            }
            
            let effectCount = 0;
            for (const [type, count] of Object.entries(counts)) {
                const triggerCount = Math.floor(count / 3);
                for (let i = 0; i < triggerCount; i++) {
                    effectCount++;
                    switch(type) {
                        case 'bomb':
                            extraScore += triggerBombEffect(3);
                            break;
                        case 'gem':
                            extraScore += triggerGemEffect(1);
                            extraScore += 1;
                            break;
                        case 'shield':
                            triggerShieldEffect(1);
                            break;
                        case 'ingot':
                            triggerIngotEffect(1);
                            break;
                        case 'lightning':
                            extraScore += triggerLightningEffect(1);
                            break;
                    }
                }
            }
            
            extraScore += effectCount * 10;
            
            return extraScore;
        }

        function lockPiece() {
            if (!currentPiece) return;
            
            let cellIndex = 0;
            for (let r = 0; r < currentPiece.shape.length; r++) {
                for (let c = 0; c < currentPiece.shape[r].length; c++) {
                    if (currentPiece.shape[r][c]) {
                        if (currentY + r < 0) {
                            endGame();
                            return;
                        }
                        board[currentY + r][currentX + c] = { type: currentPiece.cells[cellIndex] };
                        cellIndex++;
                    }
                }
            }
            currentPiece = null;
            clearLines();
            spawnPiece();
        }

        function clearLines() {
            let cleared = 0;
            let clearedTypes = [];
            
            for (let r = ROWS - 1; r >= 0; r--) {
                if (board[r].every(cell => cell.type !== null)) {
                    const rowTypes = [];
                    for (let c = 0; c < COLS; c++) {
                        rowTypes.push(board[r][c].type);
                    }
                    clearedTypes.push(...rowTypes);
                    board.splice(r, 1);
                    board.unshift(Array(COLS).fill(0).map(() => ({ type: null })));
                    cleared++;
                    r++;
                }
            }

            if (cleared > 0) {
                const points = [0, 10, 25, 40, 60];
                score += points[cleared];
                
                const extraScore = processLineEffects(clearedTypes);
                score += extraScore;
                
                let gemBonus = 0;
                const gemCounts = {};
                for (const type of clearedTypes) {
                    if (type !== 'black') {
                        gemCounts[type] = (gemCounts[type] || 0) + 1;
                    }
                }
                for (const [type, count] of Object.entries(gemCounts)) {
                    if (count >= 4) {
                        gemBonus += count - 3;
                    }
                }
                score += gemBonus;
                
                lines += cleared;
                level = Math.min(100, Math.floor(score / 1000) + 1);
                const speedBonus = 1 + (level - 1) * 0.01;
                dropInterval = Math.max(100, 1000 / speedBonus);
                
                playClearSound();
                updateUI();
            }
        }

        function spawnPiece() {
            currentPiece = nextPiece || getRandomPiece();
            nextPiece = getRandomPiece();
            currentX = Math.floor((COLS - currentPiece.shape[0].length) / 2);
            currentY = 0;

            if (!validMove(currentPiece, currentX, currentY)) {
                endGame();
            }
            drawNextPiece();
        }

        function updateUI() {
            document.getElementById('score').textContent = score;
            document.getElementById('level').textContent = level;
            document.getElementById('lines').textContent = lines;
            
            const currentSpeed = (1 + (level - 1) * 0.01).toFixed(2) + 'x';
            document.getElementById('speed').textContent = '速度: ' + currentSpeed;
            
            if (score > highScore) {
                highScore = score;
                localStorage.setItem('tetrisHighScore', highScore);
                document.getElementById('highscore').textContent = highScore;
            }
        }

        function endGame() {
            gameOver = true;
            gameStarted = false;
            stopMusic();
            if (bgMusic) {
                bgMusic.pause();
            }
            playGameOverSound();
            
            document.getElementById('final-score').textContent = score;
            document.getElementById('gameover-overlay').style.display = 'flex';
            
            setTimeout(() => {
                document.getElementById('nameInputScore').textContent = score;
                const lastName = localStorage.getItem('tetrisLastName') || '';
                document.getElementById('playerNameInput').value = lastName;
                document.getElementById('playerNameInput').focus();
                document.getElementById('playerNameInput').select();
                document.getElementById('nameInputOverlay').classList.add('active');
            }, 500);
        }

        function saveToLeaderboard(name, gameScore) {
            localStorage.setItem('tetrisLastName', name);
            let leaderboard = JSON.parse(localStorage.getItem('tetrisLeaderboard')) || [];
            leaderboard.push({ name, score: gameScore, date: new Date().toISOString() });
            leaderboard.sort((a, b) => b.score - a.score);
            leaderboard = leaderboard.slice(0, 10);
            localStorage.setItem('tetrisLeaderboard', JSON.stringify(leaderboard));
        }

        function showLeaderboard() {
            const leaderboard = JSON.parse(localStorage.getItem('tetrisLeaderboard')) || [];
            const listEl = document.getElementById('leaderboardList');
            
            if (leaderboard.length === 0) {
                listEl.innerHTML = '<li class="leaderboard-empty">暂无记录</li>';
            } else {
                listEl.innerHTML = leaderboard.map((entry, i) => `
                    <li class="leaderboard-item">
                        <span class="leaderboard-rank">#${i + 1}</span>
                        <span class="leaderboard-name">${entry.name}</span>
                        <span class="leaderboard-score">${entry.score}</span>
                    </li>
                `).join('');
            }
            
            document.getElementById('leaderboardOverlay').classList.add('active');
        }

        function hideLeaderboard() {
            document.getElementById('leaderboardOverlay').classList.remove('active');
        }

        function startGame() {
            initBoard();
            score = 0;
            level = 1;
            lines = 0;
            dropInterval = 1000;
            gameOver = false;
            gameStarted = true;
            isPaused = false;
            speedModifier = 1;
            speedModifierEnd = 0;
            
            document.getElementById('start-overlay').style.display = 'none';
            document.getElementById('gameover-overlay').style.display = 'none';
            
            nextPiece = getRandomPiece();
            spawnPiece();
            updateUI();
            lastDrop = performance.now();
            
            if (bgMusic) {
                bgMusic.currentTime = musicPausedAt;
                bgMusic.play().catch(() => {});
            }
            musicPlaying = true;
            musicInterval = setInterval(playBgMusic, 400);
        }

        function resetGame() {
            stopMusic();
            if (bgMusic) {
                bgMusic.pause();
                musicPausedAt = 0;
                const musicFile = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
                bgMusic = new Audio(musicFile);
                bgMusic.loop = true;
                bgMusic.volume = musicVolume;
            }
            initBoard();
            score = 0;
            level = 1;
            lines = 0;
            dropInterval = 1000;
            gameOver = false;
            gameStarted = false;
            isPaused = false;
            speedModifier = 1;
            speedModifierEnd = 0;
            currentPiece = null;
            nextPiece = null;
            
            document.getElementById('start-overlay').style.display = 'none';
            document.getElementById('gameover-overlay').style.display = 'none';
            document.getElementById('score').textContent = '0';
            document.getElementById('level').textContent = '1';
            document.getElementById('lines').textContent = '0';
            document.getElementById('speed').textContent = '速度: 1.00x';
            
            startGame();
            if (bgMusic) {
                bgMusic.play().catch(() => {});
            }
        }

        function togglePause() {
            if (!gameStarted || gameOver) return;
            isPaused = !isPaused;
            if (isPaused) {
                stopMusic();
            } else {
                if (bgMusic) {
                    bgMusic.currentTime = musicPausedAt;
                    bgMusic.play().catch(() => {});
                }
                musicPlaying = true;
                musicInterval = setInterval(playBgMusic, 400);
            }
        }

        function moveDown() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX, currentY + 1)) {
                currentY++;
            } else {
                lockPiece();
            }
        }

        function moveLeft() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX - 1, currentY)) {
                currentX--;
                playMoveSound();
            }
        }

        function moveRight() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX + 1, currentY)) {
                currentX++;
                playMoveSound();
            }
        }

        function hardDrop() {
            if (gameOver || isPaused || !currentPiece) return;
            while (validMove(currentPiece, currentX, currentY + 1)) {
                currentY++;
            }
            playDropSound();
            lockPiece();
            updateUI();
        }

        function gameLoop(timestamp) {
            if (Date.now() > speedModifierEnd) {
                speedModifier = 1;
            }
            
            if (gameStarted && !gameOver && !isPaused) {
                const effectiveInterval = dropInterval / speedModifier;
                if (timestamp - lastDrop > effectiveInterval) {
                    moveDown();
                    lastDrop = timestamp;
                }
            }

            drawBoard();

            if (currentPiece && !gameOver) {
                let cellIndex = 0;
                for (let r = 0; r < currentPiece.shape.length; r++) {
                    for (let c = 0; c < currentPiece.shape[r].length; c++) {
                        if (currentPiece.shape[r][c]) {
                            const drawX = currentX + c;
                            const drawY = currentY + r;
                            if (drawY >= 0) {
                                drawBlock(ctx, drawX, drawY, currentPiece.cells[cellIndex]);
                            }
                            cellIndex++;
                        }
                    }
                }
            }

            requestAnimationFrame(gameLoop);
        }

        function getKeyDisplayName(key) {
            const keyMap = {
                'ArrowLeft': '←',
                'ArrowRight': '→',
                'ArrowUp': '↑',
                'ArrowDown': '↓',
                'Space': '空格',
                'Enter': 'Enter',
                'Escape': 'Esc',
                'Backspace': '退格',
                'KeyR': 'R',
                'KeyL': 'L',
                'KeyH': 'H',
                'KeyO': 'O',
                'KeyZ': 'Z',
                'KeyX': 'X',
                'KeyW': 'W',
                'KeyS': 'S',
                'KeyA': 'A',
                'KeyD': 'D',
                'KeyJ': 'J',
                'KeyK': 'K'
            };
            if (keyMap[key]) return keyMap[key];
            if (key.startsWith('Key')) return key.substring(3);
            if (key.startsWith('Digit')) return key.substring(5);
            return key.toUpperCase();
        }

        function updateKeyDisplay() {
            document.querySelectorAll('.key-display').forEach(el => {
                const action = el.dataset.action;
                if (keyBindings[action]) {
                    el.textContent = getKeyDisplayName(keyBindings[action]);
                }
            });
        }

        let listeningFor = null;

        document.querySelectorAll('.key-display').forEach(el => {
            el.addEventListener('click', () => {
                if (listeningFor) {
                    document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                }
                listeningFor = el.dataset.action;
                el.classList.add('listening');
                el.textContent = '按下按键...';
            });
        });

        document.addEventListener('keydown', (e) => {
            if (listeningFor) {
                e.preventDefault();
                keyBindings[listeningFor] = e.code;
                localStorage.setItem('tetrisKeyBindings', JSON.stringify(keyBindings));
                
                const el = document.querySelector(`[data-action="${listeningFor}"]`);
                el.textContent = getKeyDisplayName(e.code);
                el.classList.remove('listening');
                listeningFor = null;
                return;
            }

            const nameInputOverlay = document.getElementById('nameInputOverlay');
            if (nameInputOverlay.classList.contains('active')) {
                if (e.code === 'Enter') {
                    e.preventDefault();
                    document.getElementById('submitName').click();
                    return;
                }
            }

            const leaderboardOverlay = document.getElementById('leaderboardOverlay');
            if (leaderboardOverlay.classList.contains('active')) {
                if (e.code === 'Enter' || e.code === 'Escape') {
                    e.preventDefault();
                    hideLeaderboard();
                    return;
                }
            }

            const settingsPanel = document.getElementById('settingsPanel');
            if (settingsPanel.classList.contains('active')) {
                if (e.code === 'Escape') {
                    e.preventDefault();
                    document.getElementById('closeSettings').click();
                    return;
                }
            }

            const action = Object.keys(keyBindings).find(k => keyBindings[k] === e.code);
            if (action) {
                e.preventDefault();
                switch(action) {
                    case 'down': moveDown(); break;
                    case 'left': moveLeft(); highlightKey('key-left'); break;
                    case 'right': moveRight(); highlightKey('key-right'); break;
                    case 'up': rotateLeft(); highlightKey('key-up'); break;
                    case 'rotateLeft': rotateLeft(); break;
                    case 'rotateRight': rotateRight(); break;
                    case 'btnA': rotateLeft(); highlightKey('btn-a'); break;
                    case 'btnB': rotateRight(); highlightKey('btn-b'); break;
                    case 'btnC': hardDrop(); highlightKey('btn-c'); break;
                    case 'start': 
                        if (!gameStarted || gameOver) {
                            startGame();
                        }
                        break;
                    case 'pause': togglePause(); break;
                    case 'reset': resetGame(); break;
                    case 'leaderboard': 
                        if (document.getElementById('leaderboardOverlay').classList.contains('active')) {
                            hideLeaderboard();
                            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                                togglePause();
                            }
                            wasPausedBeforeOverlay = false;
                        } else {
                            wasPausedBeforeOverlay = isPaused;
                            if (gameStarted && !gameOver && !isPaused) {
                                togglePause();
                            }
                            showLeaderboard();
                        }
                        break;
                    case 'settings': 
                        if (document.getElementById('settingsPanel').classList.contains('active')) {
                            document.getElementById('settingsPanel').classList.remove('active');
                            document.getElementById('settingsOverlay').classList.remove('active');
                            if (listeningFor) {
                                document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                                listeningFor = null;
                                updateKeyDisplay();
                            }
                            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                                togglePause();
                            }
                            wasPausedBeforeOverlay = false;
                        } else {
                            wasPausedBeforeOverlay = isPaused;
                            document.getElementById('settingsPanel').classList.add('active');
                            document.getElementById('settingsOverlay').classList.add('active');
                            updateKeyDisplay();
                            loadAvailableMusic();
                            document.getElementById('musicVolume').value = Math.round(musicVolume * 100);
                            if (gameStarted && !gameOver && !isPaused) {
                                togglePause();
                            }
                        }
                        break;
                }
            }
        });

        function highlightKey(id) {
            const el = document.getElementById(id);
            if (el) {
                el.style.filter = 'brightness(2)';
                setTimeout(() => el.style.filter = '', 150);
            }
        }

        document.getElementById('settingsBtn').addEventListener('click', () => {
            if (document.getElementById('settingsPanel').classList.contains('active')) {
                document.getElementById('settingsPanel').classList.remove('active');
                document.getElementById('settingsOverlay').classList.remove('active');
                if (listeningFor) {
                    document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                    listeningFor = null;
                    updateKeyDisplay();
                }
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            } else {
                wasPausedBeforeOverlay = isPaused;
                document.getElementById('settingsPanel').classList.add('active');
                document.getElementById('settingsOverlay').classList.add('active');
                updateKeyDisplay();
                loadAvailableMusic();
                document.getElementById('musicVolume').value = Math.round(musicVolume * 100);
                if (gameStarted && !gameOver && !isPaused) {
                    togglePause();
                }
            }
        });

        document.getElementById('resetDefaultsBtn').addEventListener('click', () => {
            keyBindings = {...DEFAULT_KEYS};
            localStorage.setItem('tetrisKeyBindings', JSON.stringify(keyBindings));
            updateKeyDisplay();
        });

        document.getElementById('closeSettings').addEventListener('click', () => {
            document.getElementById('settingsPanel').classList.remove('active');
            document.getElementById('settingsOverlay').classList.remove('active');
            if (listeningFor) {
                document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                listeningFor = null;
                updateKeyDisplay();
            }
            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                togglePause();
            }
            wasPausedBeforeOverlay = false;
        });

        document.getElementById('settingsOverlay').addEventListener('click', () => {
            document.getElementById('closeSettings').click();
        });

        document.getElementById('btn-start').addEventListener('click', () => {
            initAudio();
            if (!gameStarted || gameOver) {
                startGame();
            }
        });

        document.getElementById('btn-select').addEventListener('click', togglePause);
        document.getElementById('btn-reset').addEventListener('click', resetGame);
        document.getElementById('btn-rank').addEventListener('click', () => {
            if (document.getElementById('leaderboardOverlay').classList.contains('active')) {
                hideLeaderboard();
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            } else {
                wasPausedBeforeOverlay = isPaused;
                if (gameStarted && !gameOver && !isPaused) {
                    togglePause();
                }
                showLeaderboard();
            }
        });

        document.getElementById('btn-a').addEventListener('click', rotateLeft);
        document.getElementById('btn-b').addEventListener('click', rotateRight);
        document.getElementById('btn-c').addEventListener('click', hardDrop);

        document.getElementById('key-left').addEventListener('click', moveLeft);
        document.getElementById('key-right').addEventListener('click', moveRight);
        document.getElementById('key-down').addEventListener('click', moveDown);
        document.getElementById('key-up').addEventListener('click', rotateLeft);

        document.getElementById('m-left').addEventListener('touchstart', (e) => { e.preventDefault(); moveLeft(); });
        document.getElementById('m-right').addEventListener('touchstart', (e) => { e.preventDefault(); moveRight(); });
        document.getElementById('m-down').addEventListener('touchstart', (e) => { e.preventDefault(); moveDown(); });
        document.getElementById('m-up').addEventListener('touchstart', (e) => { e.preventDefault(); rotateLeft(); });
        document.getElementById('m-a').addEventListener('touchstart', (e) => { e.preventDefault(); rotateLeft(); });
        document.getElementById('m-b').addEventListener('touchstart', (e) => { e.preventDefault(); rotateRight(); });
        document.getElementById('m-c').addEventListener('touchstart', (e) => { e.preventDefault(); hardDrop(); });
        document.getElementById('m-vol-up').addEventListener('touchstart', (e) => { e.preventDefault(); initAudio(); volume = Math.min(1, volume + 0.1); localStorage.setItem('tetrisVolume', volume); updateVolumeDisplay(); showVolume(); playTone(440, 0.1, 'square', 0.2); });
        document.getElementById('m-vol-down').addEventListener('touchstart', (e) => { e.preventDefault(); initAudio(); volume = Math.max(0, volume - 0.1); localStorage.setItem('tetrisVolume', volume); updateVolumeDisplay(); showVolume(); playTone(440, 0.1, 'square', 0.2); });

        function showVolume() {
            const display = document.getElementById('volumeDisplay');
            display.classList.add('show');
            if (volumeDisplayTimeout) {
                clearTimeout(volumeDisplayTimeout);
            }
            volumeDisplayTimeout = setTimeout(() => {
                display.classList.remove('show');
            }, 3000);
        }

        function updateVolumeDisplay() {
            const percent = Math.round(volume * 100);
            document.getElementById('volumeDisplay').textContent = percent + '%';
            
            const holes = document.querySelectorAll('.speaker-hole');
            const activeCount = Math.ceil(percent / 12.5);
            holes.forEach((hole, i) => {
                hole.classList.toggle('active', i < activeCount);
            });
        }

        document.getElementById('speaker').addEventListener('click', () => {
            initAudio();
            volume = volume - 0.1;
            if (volume < 0) {
                volume = 1.0;
            }
            localStorage.setItem('tetrisVolume', volume);
            updateVolumeDisplay();
            showVolume();
            playTone(440, 0.1, 'square', 0.2);
        });

        document.getElementById('speaker').addEventListener('contextmenu', (e) => {
            e.preventDefault();
            volume = volume + 0.1;
            if (volume > 1.0) {
                volume = 0;
            }
            localStorage.setItem('tetrisVolume', volume);
            updateVolumeDisplay();
            showVolume();
            playTone(440, 0.1, 'square', 0.2);
        });

        document.getElementById('submitName').addEventListener('click', () => {
            const name = document.getElementById('playerNameInput').value.trim() || '匿名';
            saveToLeaderboard(name, score);
            document.getElementById('nameInputOverlay').classList.remove('active');
            showLeaderboard();
        });

        document.getElementById('playerNameInput').addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                document.getElementById('submitName').click();
            }
        });

        document.getElementById('closeLeaderboard').addEventListener('click', () => {
            hideLeaderboard();
            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                togglePause();
            }
            wasPausedBeforeOverlay = false;
        });
        document.getElementById('leaderboardOverlay').addEventListener('click', (e) => {
            if (e.target === document.getElementById('leaderboardOverlay')) {
                hideLeaderboard();
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            }
        });
        document.addEventListener('keydown', (e) => {
            if (e.code === 'Enter' || e.code === 'Escape') {
                const leaderboardOverlay = document.getElementById('leaderboardOverlay');
                if (leaderboardOverlay.classList.contains('active')) {
                    hideLeaderboard();
                    if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                        togglePause();
                    }
                    wasPausedBeforeOverlay = false;
                }
            }
        });

        let resizeTimeout;
        window.addEventListener('resize', () => {
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(setupScale, 100);
        });

        volume = parseFloat(localStorage.getItem('tetrisVolume')) || 1.0;
        musicVolume = parseFloat(localStorage.getItem('tetrisMusicVolume')) || 0.5;
        updateVolumeDisplay();
        loadAvailableMusic();

        const savedMusicFile = localStorage.getItem('tetrisMusicFile');
        if (savedMusicFile) {
            bgMusic = new Audio(savedMusicFile);
            bgMusic.volume = musicVolume;
        }

        setupCanvasSize();
        initBoard();
        drawBoard();
        drawNextPiece();
        startMusic();
        requestAnimationFrame(gameLoop);
        
        setTimeout(setupScale, 100);

        document.getElementById('musicSelect').addEventListener('change', (e) => {
            const file = e.target.value;
            localStorage.setItem('tetrisMusicFile', file);
            
            if (bgMusic) {
                bgMusic.pause();
            }
            
            bgMusic = new Audio(file);
            bgMusic.loop = true;
            bgMusic.volume = musicVolume;
            
            if (gameStarted && !isPaused && !gameOver) {
                bgMusic.play().catch(() => {});
            }
        });

        document.getElementById('musicVolume').addEventListener('input', (e) => {
            updateMusicVolume(e.target.value / 100);
        });

        document.getElementById('musicVolume').value = Math.round(musicVolume * 100);

        document.getElementById('uploadMusicBtn').addEventListener('click', () => {
            document.getElementById('musicFileInput').click();
        });

        document.getElementById('musicFileInput').addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                const url = URL.createObjectURL(file);
                localStorage.setItem('tetrisMusicFile', url);
                localStorage.setItem('tetrisMusicName', file.name);
                
                if (bgMusic) {
                    bgMusic.pause();
                }
                
                bgMusic = new Audio(url);
                bgMusic.loop = true;
                bgMusic.volume = musicVolume;
                
                const musicSelect = document.getElementById('musicSelect');
                const option = document.createElement('option');
                option.value = url;
                option.textContent = file.name;
                option.selected = true;
                
                const existingOption = Array.from(musicSelect.options).find(opt => opt.textContent === file.name);
                if (!existingOption) {
                    musicSelect.appendChild(option);
                } else {
                    existingOption.selected = true;
                }
                
                if (gameStarted && !isPaused && !gameOver) {
                    bgMusic.play().catch(() => {});
                }
            }
        });
    </script>
</body>
</html>


完结!










免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
zyrwl + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
3834888 发表于 2026-3-28 22:46
这么多行,纯手写的话,还是挺佩服的
推荐
 楼主| 午夜飘雪 发表于 2026-3-28 15:00 |楼主
本帖最后由 午夜飘雪 于 2026-3-28 15:46 编辑

更新日志
2026年3月28日更新版本v1.1
     1.调整s键下坠顺滑度
     2.调整左右移动的手感  
成品v1.1
俄罗斯方块_夜未央魔改版 v1.1.zip (775.48 KB, 下载次数: 18)
代码:
[HTML] 纯文本查看 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>俄罗斯方块 - 夜未央魔改版</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            -webkit-tap-highlight-color: transparent;
        }

        body {
            background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: 'Courier New', monospace;
            overflow: hidden;
            padding: 10px;
        }

        .game-wrapper {
            transform-origin: top center;
        }

        .game-body {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 24px;
            padding: 40px 35px;
            box-shadow: 
                0 0 0 5px #1a1a1a,
                0 0 0 10px #3d3d3d,
                0 0 50px rgba(0,0,0,0.8),
                inset 0 3px 15px rgba(255,255,255,0.1);
            position: relative;
        }

        .game-body::before {
            content: 'TETRIS';
            position: absolute;
            top: 12px;
            left: 50%;
            transform: translateX(-50%);
            font-size: 14px;
            font-weight: bold;
            color: #666;
            letter-spacing: 6px;
            text-shadow: 0 2px 3px rgba(0,0,0,0.5);
        }

        .screen-frame {
            background: #4a4a4a;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 
                inset 0 0 30px rgba(0,0,0,0.8),
                0 3px 8px rgba(0,0,0,0.5);
        }

        .screen {
            background: #9bbc0f;
            border-radius: 6px;
            padding: 15px;
            box-shadow: inset 0 0 40px rgba(0,0,0,0.3);
            position: relative;
        }

        .screen::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: repeating-linear-gradient(
                0deg,
                transparent,
                transparent 3px,
                rgba(0,0,0,0.03) 3px,
                rgba(0,0,0,0.03) 6px
            );
            pointer-events: none;
            border-radius: 6px;
        }

        .game-container {
            display: flex;
            gap: 20px;
        }

        .main-area {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        #game-canvas {
            background: #9bbc0f;
            border: 3px solid #306230;
            image-rendering: pixelated;
            display: block;
        }

        .side-panel {
            display: flex;
            flex-direction: column;
            gap: 8px;
            min-width: 120px;
        }

        .info-box {
            background: #8bac0f;
            border: 3px solid #306230;
            padding: 10px;
            text-align: center;
        }

        .info-box .label {
            font-size: 12px;
            color: #306230;
            margin-bottom: 4px;
            text-transform: uppercase;
            letter-spacing: 1px;
            font-weight: bold;
        }

        .info-box .value {
            font-size: 22px;
            font-weight: bold;
            color: #0f380f;
            font-family: 'Courier New', monospace;
        }

        .info-box .sub-value {
            font-size: 14px;
            color: #4a6a2a;
            margin-top: 2px;
            font-weight: bold;
        }

        #next-canvas {
            background: #8bac0f;
            border: 3px solid #306230;
            display: block;
        }

        .controls-section {
            margin-top: 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .d-pad {
            position: relative;
            width: 160px;
            height: 160px;
        }

        .d-pad::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            width: 50px;
            height: 50px;
            background: #1a1a1a;
            border-radius: 50%;
            z-index: 1;
        }

        .d-pad-btn {
            position: absolute;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.1s;
            box-shadow: 0 4px 8px rgba(0,0,0,0.5);
        }

        .d-pad-btn:active {
            transform: scale(0.95);
            filter: brightness(1.5);
        }

        .d-pad-up {
            top: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 50px;
            border-radius: 8px 8px 0 0;
        }

        .d-pad-down {
            bottom: 8px;
            left: 50%;
            transform: translateX(-50%);
            width: 50px;
            height: 50px;
            border-radius: 0 0 8px 8px;
        }

        .d-pad-left {
            left: 8px;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 50px;
            border-radius: 8px 0 0 8px;
        }

        .d-pad-right {
            right: 8px;
            top: 50%;
            transform: translateY(-50%);
            width: 50px;
            height: 50px;
            border-radius: 0 8px 8px 0;
        }

        .buttons-section {
            display: flex;
            gap: 15px;
            position: relative;
            width: 200px;
            height: 120px;
            justify-content: center;
        }

        .btn-a, .btn-b, .btn-c {
            position: absolute;
            width: 55px;
            height: 55px;
            background: linear-gradient(145deg, #8b0000, #5c0000);
            border-radius: 50%;
            border: none;
            cursor: pointer;
            box-shadow: 
                0 6px 12px rgba(0,0,0,0.5),
                inset 0 3px 6px rgba(255,255,255,0.2);
            font-size: 18px;
            font-weight: bold;
            color: #ff6666;
            text-shadow: 0 1px 3px rgba(0,0,0,0.5);
        }

        .btn-a {
            bottom: 10px;
            left: 10px;
        }

        .btn-b {
            bottom: 35px;
            left: 72px;
        }

        .btn-c {
            bottom: 60px;
            left: 134px;
        }

        .btn-a:active, .btn-b:active, .btn-c:active {
            box-shadow: 
                0 3px 6px rgba(0,0,0,0.5),
                inset 0 3px 6px rgba(0,0,0,0.3);
            transform: scale(0.95);
        }

        .start-select {
            display: flex;
            gap: 12px;
            margin-top: 20px;
            justify-content: center;
            flex-wrap: wrap;
            align-items: center;
        }

        .btn-ss {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #888;
            letter-spacing: 1px;
        }

        .btn-ss:active {
            transform: scale(0.95);
        }

        .btn-reset {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #4a3d2d, #3d2d1d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #aa8866;
            letter-spacing: 1px;
        }

        .btn-reset:active {
            transform: scale(0.95);
        }

        .btn-rank {
            width: 65px;
            height: 18px;
            background: linear-gradient(145deg, #2d3d2d, #1d2d1d);
            border: none;
            border-radius: 9px;
            cursor: pointer;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            font-size: 10px;
            color: #66aa66;
            letter-spacing: 1px;
        }

        .btn-rank:active {
            transform: scale(0.95);
        }

        .title-section {
            text-align: center;
            margin-bottom: 20px;
        }

        .game-title {
            font-size: 24px;
            font-weight: bold;
            color: #9bbc0f;
            text-shadow: 0 0 15px rgba(155,188,15,0.6);
            letter-spacing: 4px;
        }

        @keyframes blink {
            0%, 50% { opacity: 1; }
            51%, 100% { opacity: 0; }
        }

        .blink {
            animation: blink 1s infinite;
        }

        .speaker {
            position: absolute;
            right: 35px;
            bottom: 35px;
            width: 70px;
            height: 70px;
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            box-shadow: 0 4px 8px rgba(0,0,0,0.5);
            font-size: 14px;
            font-weight: bold;
            color: #666;
        }

        .speaker:active {
            transform: scale(0.95);
        }

        .speaker-volume {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(0,0,0,0.85);
            color: #9bbc0f;
            padding: 8px 12px;
            border-radius: 6px;
            font-size: 14px;
            font-weight: bold;
            white-space: nowrap;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            z-index: 10;
        }

        .speaker-volume.show {
            opacity: 1;
        }

        .speaker-grille {
            display: grid;
            grid-template-columns: repeat(4, 8px);
            gap: 4px;
        }

        .speaker-hole {
            width: 8px;
            height: 8px;
            background: #0a0a0a;
            border-radius: 50%;
        }

        .speaker-hole.active {
            background: #4a4a4a;
        }

        .overlay {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(155, 188, 15, 0.92);
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            border-radius: 6px;
            z-index: 10;
            padding: 20px;
        }

        .overlay h2 {
            color: #0f380f;
            font-size: 32px;
            margin-bottom: 15px;
            text-shadow: 3px 3px 0 rgba(0,0,0,0.1);
            font-weight: bold;
        }

        .overlay p {
            color: #306230;
            font-size: 16px;
            margin-bottom: 10px;
            text-align: center;
            font-weight: bold;
        }

        .overlay .final-score {
            color: #0f380f;
            font-size: 26px;
            font-weight: bold;
            margin: 15px 0;
        }

        .mobile-controls {
            display: none;
            margin-top: 25px;
            justify-content: center;
            gap: 15px;
        }

        @media (max-width: 700px) {
            .mobile-controls {
                display: flex;
                justify-content: space-between;
                align-items: flex-end;
                padding: 0 15px;
                margin-top: 15px;
            }
            .controls-section {
                display: none;
            }
            .game-body {
                padding: 15px 10px;
            }
            .screen-frame {
                padding: 8px;
            }
            .screen {
                padding: 4px;
            }
            .game-container {
                gap: 8px;
            }
            .side-panel {
                min-width: 50px;
                gap: 4px;
            }
            .info-box {
                padding: 4px;
            }
            .info-box .label {
                font-size: 9px;
            }
            .info-box .value {
                font-size: 16px;
            }
            .info-box .sub-value {
                font-size: 10px;
            }
            #next-canvas {
                width: 40px;
                height: 40px;
            }
            .start-select {
                gap: 8px;
                margin-top: 8px;
                flex-direction: row;
                justify-content: center;
                flex-wrap: wrap;
            }
            .btn-ss, .btn-reset, .btn-rank {
                width: 55px;
                height: 14px;
                font-size: 9px;
            }
            .speaker {
                width: 40px;
                height: 40px;
                right: 10px;
                bottom: 10px;
            }
            .speaker-grille {
                grid-template-columns: repeat(4, 5px);
                gap: 2px;
            }
            .speaker-hole {
                width: 5px;
                height: 5px;
            }
            .speaker-volume {
                font-size: 11px;
            }
            .title-section {
                margin-bottom: 10px;
            }
            .game-title {
                font-size: 18px;
            }
            .mobile-dpad {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 5px;
            }
            .mobile-abt-group {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 8px;
            }
            .mobile-row {
                display: flex;
                gap: 6px;
                justify-content: center;
            }
            .mobile-btn {
                width: 50px;
                height: 50px;
                background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
                border: none;
                border-radius: 12px;
                color: #9bbc0f;
                font-size: 20px;
                cursor: pointer;
                box-shadow: 0 5px 10px rgba(0,0,0,0.5);
            }
            .mobile-btn:active {
                transform: scale(0.92);
            }
            .mobile-vol {
                width: 40px;
                height: 40px;
                font-size: 18px;
            }
            .settings-btn {
                width: 30px;
                height: 30px;
                font-size: 16px;
                top: 8px;
                right: 55px;
            }
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .mobile-btn:active {
            transform: scale(0.92);
        }

        .mobile-row {
            display: flex;
            gap: 8px;
        }

        .settings-btn {
            position: absolute;
            top: 12px;
            right: 55px;
            width: 36px;
            height: 36px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-size: 20px;
            color: #888;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            display: none;
        }

        .settings-btn:hover {
            color: #9bbc0f;
        }

        @media (max-width: 700px) {
            body {
                padding: 5px;
            }
            .game-body {
                padding: 12px 8px;
                border-radius: 16px;
            }
            .game-body::before {
                top: 6px;
                font-size: 10px;
            }
            .title-section {
                margin-bottom: 6px;
            }
            .game-title {
                font-size: 16px;
            }
            .screen-frame {
                padding: 5px;
            }
            .screen {
                padding: 3px;
            }
            .game-container {
                gap: 5px;
            }
            .main-area {
                flex: 1;
            }
            .side-panel {
                min-width: 40px;
                gap: 2px;
            }
            .info-box {
                padding: 2px;
            }
            .info-box .label {
                font-size: 7px;
            }
            .info-box .value {
                font-size: 13px;
            }
            .info-box .sub-value {
                font-size: 8px;
            }
            #next-canvas {
                width: 32px;
                height: 32px;
            }
            .start-select {
                gap: 5px;
                margin-top: 5px;
            }
            .btn-ss, .btn-reset, .btn-rank {
                width: 45px;
                height: 11px;
                font-size: 7px;
            }
            .speaker {
                width: 30px;
                height: 30px;
                right: 6px;
                bottom: 6px;
            }
            .speaker-grille {
                grid-template-columns: repeat(4, 4px);
                gap: 1px;
            }
            .speaker-hole {
                width: 4px;
                height: 4px;
            }
            .speaker-volume {
                font-size: 9px;
            }
            .settings-btn {
                width: 26px;
                height: 26px;
                font-size: 13px;
                top: 5px;
                right: 45px;
            }
            .mobile-controls {
                display: flex;
                justify-content: space-between;
                align-items: flex-end;
                padding: 0 8px;
                margin-top: 8px;
            }
            .controls-section {
                display: none;
            }
            .mobile-dpad {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 3px;
            }
            .mobile-abt-group {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 5px;
            }
            .mobile-row {
                display: flex;
                gap: 4px;
            }
            .mobile-btn {
                width: 42px;
                height: 42px;
                background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
                border: none;
                border-radius: 10px;
                color: #9bbc0f;
                font-size: 16px;
                cursor: pointer;
                box-shadow: 0 4px 8px rgba(0,0,0,0.5);
            }
            .mobile-btn:active {
                transform: scale(0.92);
            }
            .mobile-vol {
                width: 32px;
                height: 32px;
                font-size: 14px;
            }
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .mobile-btn:active {
            transform: scale(0.92);
        }

        .mobile-row {
            display: flex;
            gap: 8px;
        }

        .settings-btn {
            position: absolute;
            top: 12px;
            right: 55px;
            width: 36px;
            height: 36px;
            background: linear-gradient(145deg, #3d3d3d, #2d2d2d);
            border: none;
            border-radius: 50%;
            cursor: pointer;
            font-size: 20px;
            color: #888;
            box-shadow: 0 3px 6px rgba(0,0,0,0.5);
            display: none;
        }

        .settings-btn:hover {
            color: #9bbc0f;
        }

        @media (min-width: 701px) {
            .settings-btn {
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }

        .settings-panel {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: #2d2d2d;
            border-radius: 12px;
            padding: 25px;
            z-index: 100;
            box-shadow: 0 10px 40px rgba(0,0,0,0.8);
            display: none;
            min-width: 400px;
            max-height: 80vh;
            overflow-y: auto;
        }

        .settings-panel.active {
            display: block;
        }

        .settings-panel h3 {
            color: #9bbc0f;
            margin-bottom: 20px;
            text-align: center;
            font-size: 24px;
            font-weight: bold;
        }

        .settings-section {
            margin-bottom: 20px;
        }

        .settings-section-title {
            color: #888;
            font-size: 13px;
            margin-bottom: 10px;
            text-transform: uppercase;
            letter-spacing: 1px;
            border-bottom: 1px solid #4a4a4a;
            padding-bottom: 5px;
            font-weight: bold;
        }

        .settings-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 12px;
            padding: 8px 0;
        }

        .settings-row label {
            color: #aaa;
            font-size: 15px;
            min-width: 90px;
            font-weight: bold;
        }

        .settings-row .key-display {
            background: #1a1a1a;
            padding: 10px 18px;
            border-radius: 6px;
            color: #9bbc0f;
            font-size: 14px;
            min-width: 90px;
            text-align: center;
            cursor: pointer;
            border: 2px solid #4a4a4a;
            font-weight: bold;
        }

        .settings-row .key-display:hover {
            border-color: #9bbc0f;
        }

        .settings-row .key-display.listening {
            border-color: #ff6600;
            color: #ff6600;
        }

        .settings-panel .btn-reset-defaults {
            width: 100%;
            padding: 14px;
            background: #3d3d2d;
            border: none;
            border-radius: 8px;
            color: #ffaa66;
            cursor: pointer;
            font-size: 15px;
            font-weight: bold;
            margin-bottom: 10px;
        }

        .settings-panel .btn-reset-defaults:hover {
            background: #5a5a4a;
        }

        .settings-panel .btn-close {
            width: 100%;
            padding: 16px;
            background: #4a4a4a;
            border: none;
            border-radius: 8px;
            color: #9bbc0f;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            margin-top: 15px;
        }

        .settings-panel .btn-close:hover {
            background: #5a5a5a;
        }

        .settings-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.6);
            z-index: 99;
            display: none;
        }

        .settings-overlay.active {
            display: block;
        }

        .leaderboard-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.85);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 200;
            display: none;
        }

        .leaderboard-overlay.active {
            display: flex;
        }

        .leaderboard-panel {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 16px;
            padding: 30px;
            min-width: 350px;
            max-width: 90vw;
            max-height: 80vh;
            overflow-y: auto;
            box-shadow: 0 10px 50px rgba(0,0,0,0.8);
        }

        .leaderboard-panel h2 {
            color: #9bbc0f;
            text-align: center;
            margin-bottom: 20px;
            font-size: 28px;
            font-weight: bold;
        }

        .leaderboard-list {
            list-style: none;
        }

        .leaderboard-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 14px 16px;
            border-bottom: 1px solid #3d3d3d;
            color: #aaa;
            font-size: 15px;
            font-weight: bold;
        }

        .leaderboard-item:first-child {
            color: #ffd700;
            font-weight: bold;
        }

        .leaderboard-item:nth-child(2) {
            color: #c0c0c0;
        }

        .leaderboard-item:nth-child(3) {
            color: #cd7f32;
        }

        .leaderboard-rank {
            min-width: 35px;
            font-weight: bold;
        }

        .leaderboard-name {
            flex: 1;
            text-align: center;
            margin: 0 15px;
            font-weight: bold;
        }

        .leaderboard-score {
            min-width: 90px;
            text-align: right;
            font-weight: bold;
        }

        .leaderboard-empty {
            text-align: center;
            color: #666;
            padding: 30px;
            font-size: 16px;
            font-weight: bold;
        }

        .leaderboard-btn {
            width: 100%;
            padding: 16px;
            background: #4a4a4a;
            border: none;
            border-radius: 8px;
            color: #9bbc0f;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
            margin-top: 20px;
        }

        .name-input-overlay {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: rgba(0,0,0,0.85);
            display: flex;
            align-items: center;
            justify-content: center;
            z-index: 200;
            display: none;
        }

        .name-input-overlay.active {
            display: flex;
        }

        .name-input-panel {
            background: linear-gradient(145deg, #2d2d2d, #1a1a1a);
            border-radius: 16px;
            padding: 30px;
            min-width: 320px;
            text-align: center;
            box-shadow: 0 10px 50px rgba(0,0,0,0.8);
        }

        .name-input-panel h3 {
            color: #9bbc0f;
            margin-bottom: 20px;
            font-size: 24px;
            font-weight: bold;
        }

        .name-input {
            width: 100%;
            padding: 14px;
            background: #1a1a1a;
            border: 2px solid #4a4a4a;
            border-radius: 8px;
            color: #9bbc0f;
            font-size: 18px;
            font-family: 'Courier New', monospace;
            text-align: center;
            margin-bottom: 15px;
            font-weight: bold;
        }

        .name-input:focus {
            outline: none;
            border-color: #9bbc0f;
        }

        .name-input-btn {
            padding: 14px 35px;
            background: #8b0000;
            border: none;
            border-radius: 8px;
            color: #fff;
            cursor: pointer;
            font-size: 16px;
            font-weight: bold;
        }

        .name-input-panel p {
            color: #aaa;
            font-size: 15px;
            font-weight: bold;
            margin-bottom: 15px;
        }

        .name-input {
            width: 100%;
            padding: 12px;
            background: #1a1a1a;
            border: 2px solid #4a4a4a;
            border-radius: 8px;
            color: #9bbc0f;
            font-size: 16px;
            font-family: 'Courier New', monospace;
            text-align: center;
            margin-bottom: 15px;
        }

        .name-input:focus {
            outline: none;
            border-color: #9bbc0f;
        }

        .name-input-btn {
            padding: 12px 30px;
            background: #8b0000;
            border: none;
            border-radius: 8px;
            color: #fff;
            cursor: pointer;
            font-size: 14px;
        }

        .effect-text {
            position: absolute;
            font-size: 20px;
            font-weight: bold;
            color: #0f380f;
            text-shadow: 2px 2px 0 #9bbc0f;
            pointer-events: none;
            z-index: 50;
            animation: effectPop 1s ease-out forwards;
        }

        @keyframes effectPop {
            0% { transform: scale(0.5); opacity: 1; }
            50% { transform: scale(1.2); opacity: 1; }
            100% { transform: scale(1) translateY(-30px); opacity: 0; }
        }
    </style>
</head>
<body>
    <div class="game-wrapper" id="gameWrapper">
        <div class="game-body">
            <button class="settings-btn" id="settingsBtn">&#9881;</button>
            
            <div class="title-section">
                <div class="game-title">俄罗斯方块 - 夜未央魔改版</div>
            </div>
            
            <div class="screen-frame">
                <div class="screen">
                    <div class="game-container">
                        <div class="main-area">
                            <canvas id="game-canvas"></canvas>
                        </div>
                        <div class="side-panel">
                            <div class="info-box">
                                <div class="label">分数</div>
                                <div class="value" id="score">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">等级</div>
                                <div class="value" id="level">1</div>
                                <div class="sub-value" id="speed">速度: 1.0x</div>
                            </div>
                            <div class="info-box">
                                <div class="label">行数</div>
                                <div class="value" id="lines">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">最高</div>
                                <div class="value" id="highscore">0</div>
                            </div>
                            <div class="info-box">
                                <div class="label">下一个</div>
                                <canvas id="next-canvas"></canvas>
                            </div>
                        </div>
                    </div>
                    <div id="start-overlay" class="overlay">
                        <h2>俄罗斯方块</h2>
                        <h2>夜未央魔改版</h2>
                        <p>按 开始 键开始游戏</p>
                        <p class="blink">▼</p>
                    </div>
                    <div id="gameover-overlay" class="overlay" style="display: none;">
                        <h2>游戏结束</h2>
                        <div class="final-score">得分: <span id="final-score">0</span></div>
                        <p>按 开始 键重新开始</p>
                    </div>
                </div>
            </div>

            <div class="controls-section">
                <div class="d-pad">
                    <button class="d-pad-btn d-pad-up" id="key-up">▲</button>
                    <button class="d-pad-btn d-pad-down" id="key-down">▼</button>
                    <button class="d-pad-btn d-pad-left" id="key-left">&#9664;</button>
                    <button class="d-pad-btn d-pad-right" id="key-right">&#9654;</button>
                </div>
                <div class="buttons-section">
                    <button class="btn-a" id="btn-a">A</button>
                    <button class="btn-b" id="btn-b">B</button>
                    <button class="btn-c" id="btn-c">C</button>
                </div>
            </div>

            <div class="start-select">
                <button class="btn-reset" id="btn-reset">重置</button>
                <button class="btn-ss" id="btn-select">暂停</button>
                <button class="btn-ss" id="btn-start">开始</button>
                <button class="btn-rank" id="btn-rank">排行</button>
            </div>

            <div class="speaker" id="speaker">
                <div class="speaker-volume" id="volumeDisplay">100%</div>
                <div class="speaker-grille">
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                    <div class="speaker-hole active"></div>
                </div>
            </div>

            <div class="mobile-controls">
                <div class="mobile-dpad">
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-up">↑</button>
                    </div>
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-left">←</button>
                        <button class="mobile-btn" id="m-down">↓</button>
                        <button class="mobile-btn" id="m-right">→</button>
                    </div>
                </div>
                <div class="mobile-abt-group">
                    <div class="mobile-row">
                        <button class="mobile-btn" id="m-a">A</button>
                        <button class="mobile-btn" id="m-b">B</button>
                        <button class="mobile-btn" id="m-c">C</button>
                    </div>
                    <div class="mobile-row">
                        <button class="mobile-btn mobile-vol" id="m-vol-down">-</button>
                        <button class="mobile-btn mobile-vol" id="m-vol-up">+</button>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <div class="settings-overlay" id="settingsOverlay"></div>
        <div class="settings-panel" id="settingsPanel">
        <h3>按键设置</h3>
        
        <div class="settings-section">
            <div class="settings-section-title">移动控制</div>
            <div class="settings-row">
                <label>向左移动</label>
                <div class="key-display" data-action="left">A</div>
            </div>
            <div class="settings-row">
                <label>向右移动</label>
                <div class="key-display" data-action="right">D</div>
            </div>
            <div class="settings-row">
                <label>向下移动</label>
                <div class="key-display" data-action="down">S</div>
            </div>
            <div class="settings-row">
                <label>旋转</label>
                <div class="key-display" data-action="up">W</div>
            </div>
        </div>
        
        <div class="settings-section">
            <div class="settings-section-title">功能按钮</div>
            <div class="settings-row">
                <label>J(左旋)</label>
                <div class="key-display" data-action="btnA">J</div>
            </div>
            <div class="settings-row">
                <label>K(右旋)</label>
                <div class="key-display" data-action="btnB">K</div>
            </div>
            <div class="settings-row">
                <label>L(硬降)</label>
                <div class="key-display" data-action="btnC">L</div>
            </div>
        </div>
        
        <div class="settings-section">
            <div class="settings-section-title">游戏控制</div>
            <div class="settings-row">
                <label>开始游戏</label>
                <div class="key-display" data-action="start">Enter</div>
            </div>
            <div class="settings-row">
                <label>暂停</label>
                <div class="key-display" data-action="pause">空格</div>
            </div>
            <div class="settings-row">
                <label>重置</label>
                <div class="key-display" data-action="reset">R</div>
            </div>
            <div class="settings-row">
                <label>排行榜</label>
                <div class="key-display" data-action="leaderboard">H</div>
            </div>
            <div class="settings-row">
                <label>设置</label>
                <div class="key-display" data-action="settings">O</div>
            </div>
        </div>

        <div class="settings-section">
            <div class="settings-section-title">音乐设置</div>
            <div class="settings-row">
                <label>选择音乐</label>
                <select id="musicSelect" style="background:#1a1a1a;color:#9bbc0f;padding:8px 12px;border-radius:6px;border:2px solid #4a4a4a;font-size:13px;min-width:120px;cursor:pointer;">
                    <option value="">苍天黄土.mp3</option>
                </select>
            </div>
            <div class="settings-row">
                <label>上传音乐</label>
                <input type="file" id="musicFileInput" accept="audio/*" style="display:none;">
                <button id="uploadMusicBtn" style="background:#1a1a1a;color:#9bbc0f;padding:8px 12px;border-radius:6px;border:2px solid #4a4a4a;font-size:12px;cursor:pointer;">上传</button>
            </div>
            <div class="settings-row">
                <label>音乐音量</label>
                <input type="range" id="musicVolume" min="0" max="100" value="100" style="width:120px;cursor:pointer;">
            </div>
        </div>
        
        <button class="btn-reset-defaults" id="resetDefaultsBtn" style="width:100%;padding:14px;background:#4a4a4a;border:none;border-radius:8px;color:#9bbc0f;cursor:pointer;font-size:15px;font-weight:bold;margin-bottom:10px;">恢复默认值</button>
        <button class="btn-close" id="closeSettings">关闭</button>
    </div>

    <div class="name-input-overlay" id="nameInputOverlay">
        <div class="name-input-panel">
            <h3>游戏结束!</h3>
            <p style="color:#aaa;margin-bottom:15px;">得分为 <span id="nameInputScore" style="color:#9bbc0f;font-weight:bold;">0</span></p>
            <input type="text" class="name-input" id="playerNameInput" placeholder="输入你的名字" maxlength="10">
            <button class="name-input-btn" id="submitName">提交</button>
        </div>
    </div>

    <div class="leaderboard-overlay" id="leaderboardOverlay">
        <div class="leaderboard-panel">
            <h2>排行榜</h2>
            <ul class="leaderboard-list" id="leaderboardList"></ul>
            <button class="leaderboard-btn" id="closeLeaderboard">关闭</button>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('game-canvas');
        const ctx = canvas.getContext('2d');
        const nextCanvas = document.getElementById('next-canvas');
        const nextCtx = nextCanvas.getContext('2d');

        const COLS = 10;
        const ROWS = 18;
        let BLOCK_SIZE = 40;

        const PIECE_TYPES = ['bomb', 'gem', 'shield', 'ingot', 'lightning', 'black'];
        const PIECE_COLORS = {
            bomb: '#ff4444',
            gem: '#44ff44',
            shield: '#4488ff',
            ingot: '#ff8800',
            lightning: '#aa44ff',
            black: '#333333'
        };
        
        const PIECE_TEXT = {
            bomb: '炸',
            gem: '石',
            shield: '时',
            ingot: '变',
            lightning: '&#9889;',
            black: ''
        };

        const SHAPES = {
            I: [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],
            O: [[1,1],[1,1]],
            T: [[0,1,0],[1,1,1],[0,0,0]],
            S: [[0,1,1],[1,1,0],[0,0,0]],
            Z: [[1,1,0],[0,1,1],[0,0,0]],
            J: [[1,0,0],[1,1,1],[0,0,0]],
            L: [[0,0,1],[1,1,1],[0,0,0]]
        };

        const PIECE_NAMES = ['I', 'O', 'T', 'S', 'Z', 'J', 'L'];

        let board = [];
        let currentPiece = null;
        let currentX = 0;
        let currentY = 0;
        let nextPiece = null;
        let score = 0;
        let level = 1;
        let lines = 0;
        let highScore = localStorage.getItem('tetrisHighScore') || 0;
        let gameOver = false;
        let gameStarted = false;
        let dropInterval = 1000;
        let lastDrop = 0;
        let isPaused = false;
        let wasPausedBeforeOverlay = false;
        let speedModifier = 1;
        let speedModifierEnd = 0;
        let isDownHeld = false;
        let isLeftHeld = false;
        let isRightHeld = false;
        let lastMoveTime = 0;
        let moveDelay = 150;
        let moveStartTime = 0;
        let initialMoveDelay = 200;

        const DEFAULT_KEYS = {
            up: 'KeyW',
            down: 'KeyS',
            left: 'KeyA',
            right: 'KeyD',
            rotateLeft: 'KeyJ',
            rotateRight: 'KeyK',
            btnA: 'KeyJ',
            btnB: 'KeyK',
            btnC: 'KeyL',
            start: 'Enter',
            pause: 'Space',
            reset: 'KeyR',
            leaderboard: 'KeyH',
            settings: 'KeyO'
        };

        let keyBindings = JSON.parse(localStorage.getItem('tetrisKeyBindings')) || {...DEFAULT_KEYS};
        let volume = 1.0;
        let volumeDisplayTimeout = null;

        let audioCtx = null;
        let musicPlaying = false;
        let musicInterval = null;
        let musicInitialized = false;
        let musicVolume = 0.5;
        let bgMusic = null;
        let availableMusicFiles = [];
        let musicPausedAt = 0;

        const MUSIC_CANG_TIAN_HUANG_TU = [
            { freq: 196, dur: 0.4 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.2 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 247, dur: 0.4 },
            { freq: 220, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.6 },
            { freq: 262, dur: 0.4 },
            { freq: 247, dur: 0.2 },
            { freq: 220, dur: 0.2 },
            { freq: 196, dur: 0.6 },
            { freq: 220, dur: 0.4 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.2 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 247, dur: 0.4 },
            { freq: 294, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 220, dur: 0.6 },
            { freq: 196, dur: 0.4 },
            { freq: 220, dur: 0.2 },
            { freq: 262, dur: 0.2 },
            { freq: 294, dur: 0.4 },
            { freq: 330, dur: 0.4 },
            { freq: 294, dur: 0.4 },
            { freq: 262, dur: 0.4 },
            { freq: 247, dur: 0.4 },
            { freq: 220, dur: 0.6 }
        ];

        let musicNoteIndex = 0;

        function initAudio() {
            if (!audioCtx) {
                audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            }
            if (audioCtx.state === 'suspended') {
                audioCtx.resume();
            }
        }

        function playTone(freq, duration, type = 'square', vol = 0.3) {
            if (!audioCtx || volume === 0) return;
            
            const oscillator = audioCtx.createOscillator();
            const gainNode = audioCtx.createGain();
            
            oscillator.connect(gainNode);
            gainNode.connect(audioCtx.destination);
            
            oscillator.type = type;
            oscillator.frequency.setValueAtTime(freq, audioCtx.currentTime);
            
            gainNode.gain.setValueAtTime(vol * volume, audioCtx.currentTime);
            gainNode.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
            
            oscillator.start(audioCtx.currentTime);
            oscillator.stop(audioCtx.currentTime + duration);
        }

        function playMoveSound() {
            playTone(200, 0.05, 'square', 0.15);
        }

        function playRotateSound() {
            playTone(350, 0.08, 'square', 0.15);
        }

        function playDropSound() {
            playTone(150, 0.1, 'square', 0.2);
        }

        function playClearSound() {
            playTone(523, 0.1, 'square', 0.2);
            setTimeout(() => playTone(659, 0.1, 'square', 0.2), 80);
            setTimeout(() => playTone(784, 0.15, 'square', 0.2), 160);
        }

        function playGameOverSound() {
            playTone(200, 0.3, 'sawtooth', 0.2);
            setTimeout(() => playTone(150, 0.3, 'sawtooth', 0.2), 200);
            setTimeout(() => playTone(100, 0.5, 'sawtooth', 0.2), 400);
        }

        function playEffectSound(type) {
            switch(type) {
                case 'bomb':
                    playTone(80, 0.4, 'sawtooth', 0.3);
                    break;
                case 'gem':
                    playTone(800, 0.2, 'sine', 0.2);
                    setTimeout(() => playTone(1000, 0.2, 'sine', 0.2), 80);
                    break;
                case 'shield':
                    playTone(300, 0.3, 'triangle', 0.2);
                    break;
                case 'ingot':
                    playTone(500, 0.15, 'sine', 0.15);
                    setTimeout(() => playTone(700, 0.15, 'sine', 0.15), 60);
                    break;
                case 'lightning':
                    playTone(120, 0.5, 'sawtooth', 0.3);
                    break;
            }
        }

        function playMusicNote() {
            if (!musicPlaying || volume === 0) return;
            
            if (bgMusic && bgMusic.paused === false) return;
            
            const note = MUSIC_CANG_TIAN_HUANG_TU[musicNoteIndex];
            playTone(note.freq, note.dur * 0.9, 'square', 0.12 * musicVolume);
            
            musicNoteIndex = (musicNoteIndex + 1) % MUSIC_CANG_TIAN_HUANG_TU.length;
        }

        function playBgMusic() {
            if (!bgMusic || bgMusic.paused) return;
            playMusicNote();
        }

        function startMusic() {
            if (bgMusic && bgMusic.paused === false) return;
            initAudio();
            musicPlaying = true;
            
            const musicFile = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
            
            if (bgMusic) {
                bgMusic.pause();
                bgMusic = null;
            }
            
            bgMusic = new Audio(musicFile);
            bgMusic.loop = true;
            bgMusic.volume = musicVolume;
            
            bgMusic.play().catch(() => {});
            
            musicInterval = setInterval(playBgMusic, 400);
        }

        function stopMusic() {
            musicPlaying = false;
            if (musicInterval) {
                clearInterval(musicInterval);
                musicInterval = null;
            }
            if (bgMusic) {
                musicPausedAt = bgMusic.currentTime;
                bgMusic.pause();
            }
        }

        function updateMusicVolume(val) {
            musicVolume = val;
            if (bgMusic) {
                bgMusic.volume = musicVolume;
            }
            localStorage.setItem('tetrisMusicVolume', musicVolume);
        }

        function loadAvailableMusic() {
            const musicSelect = document.getElementById('musicSelect');
            const savedMusic = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
            
            availableMusicFiles = ['苍天黄土.mp3'];
            
            musicSelect.innerHTML = '';
            availableMusicFiles.forEach(file => {
                const option = document.createElement('option');
                option.value = file;
                option.textContent = file;
                option.selected = file === savedMusic;
                musicSelect.appendChild(option);
            });
        }

        function isMobile() {
            return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) || window.innerWidth <= 700;
        }

        function setupCanvasSize() {
            if (isMobile()) {
                BLOCK_SIZE = 18;
                canvas.width = COLS * BLOCK_SIZE;
                canvas.height = ROWS * BLOCK_SIZE;
                nextCanvas.width = 50;
                nextCanvas.height = 50;
            } else {
                BLOCK_SIZE = 40;
                canvas.width = COLS * BLOCK_SIZE;
                canvas.height = ROWS * BLOCK_SIZE;
                nextCanvas.width = 80;
                nextCanvas.height = 80;
            }
        }

        function setupScale() {
            const wrapper = document.getElementById('gameWrapper');
            const gameBody = document.querySelector('.game-body');
            
            if (!gameBody || !wrapper) return;
            
            const screenWidth = window.innerWidth;
            const screenHeight = window.innerHeight;
            
            if (screenWidth <= 700) {
                wrapper.style.transform = 'scale(1)';
                return;
            }
            
            const gameWidth = gameBody.offsetWidth;
            const gameHeight = gameBody.offsetHeight;
            
            if (gameWidth === 0 || gameHeight === 0) return;
            
            if (screenWidth < gameWidth || screenHeight < gameHeight) {
                const scaleX = (screenWidth * 0.95) / gameWidth;
                const scaleY = (screenHeight * 0.95) / gameHeight;
                const scale = Math.min(scaleX, scaleY, 1);
                wrapper.style.transform = `scale(${scale})`;
            } else {
                wrapper.style.transform = 'scale(1)';
            }
        }

        function getRandomPieceType() {
            const rand = Math.random();
            if (rand < 0.6) return PIECE_TYPES[Math.floor(Math.random() * 5)];
            return 'black';
        }

        document.getElementById('highscore').textContent = highScore;

        function initBoard() {
            board = [];
            for (let r = 0; r < ROWS; r++) {
                board[r] = [];
                for (let c = 0; c < COLS; c++) {
                    board[r][c] = { type: null };
                }
            }
        }

        function createPiece(type) {
            const shape = SHAPES[type].map(row => [...row]);
            const cells = [];
            for (let r = 0; r < shape.length; r++) {
                for (let c = 0; c < shape[r].length; c++) {
                    if (shape[r][c]) {
                        cells.push(getRandomPieceType());
                    }
                }
            }
            return { type, shape, cells };
        }

        function getRandomPiece() {
            const type = PIECE_NAMES[Math.floor(Math.random() * PIECE_NAMES.length)];
            return createPiece(type);
        }

        function drawPieceGraphic(ctx, x, y, type, size) {
            const color = PIECE_COLORS[type];
            const padding = 2;
            const text = PIECE_TEXT[type];
            
            ctx.fillStyle = color;
            ctx.fillRect(x + padding, y + padding, size - padding * 2, size - padding * 2);
            
            ctx.fillStyle = '#000';
            ctx.fillRect(x + padding, y + padding, size - padding * 2, 2);
            ctx.fillRect(x + padding, y + padding, 2, size - padding * 2);
            
            ctx.fillStyle = '#fff';
            ctx.globalAlpha = 0.3;
            ctx.fillRect(x + size - padding - 2, y + padding, 2, size - padding * 2);
            ctx.fillRect(x + padding, y + size - padding - 2, size - padding * 2, 2);
            ctx.globalAlpha = 1;
            
            if (text) {
                ctx.fillStyle = '#fff';
                ctx.font = `bold ${size * 0.45}px Arial`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText(text, x + size/2, y + size/2);
            }
        }

        function drawBlock(ctx, x, y, type, size = BLOCK_SIZE) {
            drawPieceGraphic(ctx, x * size, y * size, type, size);
        }

        function drawBoard() {
            ctx.fillStyle = '#9bbc0f';
            ctx.fillRect(0, 0, canvas.width, canvas.height);

            for (let r = 0; r < ROWS; r++) {
                for (let c = 0; c < COLS; c++) {
                    if (board[r][c].type) {
                        drawBlock(ctx, c, r, board[r][c].type);
                    }
                }
            }

            ctx.strokeStyle = '#306230';
            ctx.lineWidth = 1;
            for (let r = 0; r <= ROWS; r++) {
                ctx.beginPath();
                ctx.moveTo(0, r * BLOCK_SIZE);
                ctx.lineTo(canvas.width, r * BLOCK_SIZE);
                ctx.stroke();
            }
            for (let c = 0; c <= COLS; c++) {
                ctx.beginPath();
                ctx.moveTo(c * BLOCK_SIZE, 0);
                ctx.lineTo(c * BLOCK_SIZE, canvas.height);
                ctx.stroke();
            }
        }

        function drawNextPiece() {
            nextCtx.fillStyle = '#8bac0f';
            nextCtx.fillRect(0, 0, nextCanvas.width, nextCanvas.height);

            if (!nextPiece) return;

            const blockSize = isMobile() ? 12 : 18;
            const offsetX = (nextCanvas.width - nextPiece.shape[0].length * blockSize) / 2;
            const offsetY = (nextCanvas.height - nextPiece.shape.length * blockSize) / 2;

            let cellIndex = 0;
            for (let r = 0; r < nextPiece.shape.length; r++) {
                for (let c = 0; c < nextPiece.shape[r].length; c++) {
                    if (nextPiece.shape[r][c]) {
                        const type = nextPiece.cells[cellIndex++];
                        drawPieceGraphic(nextCtx, offsetX + c * blockSize, offsetY + r * blockSize, type, blockSize);
                    }
                }
            }
        }

        function validMove(piece, x, y) {
            for (let r = 0; r < piece.shape.length; r++) {
                for (let c = 0; c < piece.shape[r].length; c++) {
                    if (piece.shape[r][c]) {
                        const newX = x + c;
                        const newY = y + r;
                        if (newX < 0 || newX >= COLS || newY >= ROWS) {
                            return false;
                        }
                        if (newY >= 0 && board[newY][newX].type) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }

        function rotateMatrixCW(matrix) {
            const n = matrix.length;
            const result = [];
            for (let i = 0; i < n; i++) {
                result[i] = [];
                for (let j = 0; j < n; j++) {
                    result[i][j] = matrix[n - 1 - j][i];
                }
            }
            return result;
        }

        function rotateMatrixCCW(matrix) {
            const n = matrix.length;
            const result = [];
            for (let i = 0; i < n; i++) {
                result[i] = [];
                for (let j = 0; j < n; j++) {
                    result[i][j] = matrix[j][n - 1 - i];
                }
            }
            return result;
        }

        function rotateLeft() {
            if (!currentPiece || gameOver || isPaused) return;
            const rotated = rotateMatrixCCW(currentPiece.shape);
            const original = currentPiece.shape;
            currentPiece.shape = rotated;
            if (!validMove(currentPiece, currentX, currentY)) {
                currentPiece.shape = original;
            } else {
                playRotateSound();
            }
        }

        function rotateRight() {
            if (!currentPiece || gameOver || isPaused) return;
            const rotated = rotateMatrixCW(currentPiece.shape);
            const original = currentPiece.shape;
            currentPiece.shape = rotated;
            if (!validMove(currentPiece, currentX, currentY)) {
                currentPiece.shape = original;
            } else {
                playRotateSound();
            }
        }

        function showEffectText(text, x, y) {
            const effectEl = document.createElement('div');
            effectEl.className = 'effect-text';
            effectEl.textContent = text;
            effectEl.style.left = (x * BLOCK_SIZE + 50) + 'px';
            effectEl.style.top = (y * BLOCK_SIZE + 100) + 'px';
            document.querySelector('.screen').appendChild(effectEl);
            setTimeout(() => effectEl.remove(), 1000);
        }

        function triggerBombEffect(count) {
            const size = count + 2;
            const centerX = Math.floor(Math.random() * (COLS - size));
            const centerY = Math.floor(Math.random() * (ROWS - size));
            
            let cleared = 0;
            for (let r = Math.max(0, centerY - 1); r < Math.min(ROWS, centerY + size); r++) {
                for (let c = Math.max(0, centerX - 1); c < Math.min(COLS, centerX + size); c++) {
                    if (board[r][c].type) {
                        board[r][c] = { type: null };
                        cleared++;
                    }
                }
            }
            
            playEffectSound('bomb');
            showEffectText(`炸弹${size}x${size}!`, centerX, centerY);
            return cleared;
        }

        function triggerGemEffect(count) {
            let added = 0;
            
            for (let i = 0; i < count; i++) {
                const blackCells = [];
                for (let r = 0; r < ROWS; r++) {
                    for (let c = 0; c < COLS; c++) {
                        if (board[r][c].type === 'black') {
                            blackCells.push({r, c});
                        }
                    }
                }
                
                if (blackCells.length > 0) {
                    const cell = blackCells[Math.floor(Math.random() * blackCells.length)];
                    board[cell.r][cell.c] = { type: PIECE_TYPES[Math.floor(Math.random() * 5)] };
                    added++;
                }
            }
            
            playEffectSound('gem');
            showEffectText(`绿宝石+${added}!`, 5, 5);
            return added;
        }

        function triggerShieldEffect(count) {
            const duration = count * 1000;
            speedModifier = 2.0;
            speedModifierEnd = Date.now() + duration;
            
            playEffectSound('shield');
            showEffectText(`减速${count}秒!`, 5, 5);
        }

        function triggerIngotEffect(count) {
            let changed = 0;
            for (let i = 0; i < count; i++) {
                const x = Math.floor(Math.random() * COLS);
                const y = Math.floor(Math.random() * ROWS);
                if (board[y][x].type && board[y][x].type !== 'black') {
                    board[y][x] = { type: PIECE_TYPES[Math.floor(Math.random() * 5)] };
                    changed++;
                }
            }
            
            playEffectSound('ingot');
            showEffectText(`换图${changed}!`, 5, 5);
        }

        function triggerLightningEffect(count) {
            let cleared = 0;
            for (let r = 0; r < ROWS; r++) {
                for (let c = 0; c < COLS; c++) {
                    if (board[r][c].type === 'lightning') {
                        board[r][c] = { type: null };
                        cleared++;
                    }
                }
            }
            
            playEffectSound('lightning');
            showEffectText(`闪电${cleared}!`, 5, 5);
            return cleared;
        }

        function processLineEffects(clearedTypes) {
            let extraScore = 0;
            
            const counts = {};
            for (const type of clearedTypes) {
                if (type !== 'black') {
                    counts[type] = (counts[type] || 0) + 1;
                }
            }
            
            let effectCount = 0;
            for (const [type, count] of Object.entries(counts)) {
                const triggerCount = Math.floor(count / 3);
                for (let i = 0; i < triggerCount; i++) {
                    effectCount++;
                    switch(type) {
                        case 'bomb':
                            extraScore += triggerBombEffect(3);
                            break;
                        case 'gem':
                            extraScore += triggerGemEffect(1);
                            extraScore += 1;
                            break;
                        case 'shield':
                            triggerShieldEffect(1);
                            break;
                        case 'ingot':
                            triggerIngotEffect(1);
                            break;
                        case 'lightning':
                            extraScore += triggerLightningEffect(1);
                            break;
                    }
                }
            }
            
            extraScore += effectCount * 10;
            
            return extraScore;
        }

        function lockPiece() {
            if (!currentPiece) return;
            
            let cellIndex = 0;
            for (let r = 0; r < currentPiece.shape.length; r++) {
                for (let c = 0; c < currentPiece.shape[r].length; c++) {
                    if (currentPiece.shape[r][c]) {
                        if (currentY + r < 0) {
                            endGame();
                            return;
                        }
                        board[currentY + r][currentX + c] = { type: currentPiece.cells[cellIndex] };
                        cellIndex++;
                    }
                }
            }
            currentPiece = null;
            clearLines();
            spawnPiece();
        }

        function clearLines() {
            let cleared = 0;
            let clearedTypes = [];
            
            for (let r = ROWS - 1; r >= 0; r--) {
                if (board[r].every(cell => cell.type !== null)) {
                    const rowTypes = [];
                    for (let c = 0; c < COLS; c++) {
                        rowTypes.push(board[r][c].type);
                    }
                    clearedTypes.push(...rowTypes);
                    board.splice(r, 1);
                    board.unshift(Array(COLS).fill(0).map(() => ({ type: null })));
                    cleared++;
                    r++;
                }
            }

            if (cleared > 0) {
                const points = [0, 10, 25, 40, 60];
                score += points[cleared];
                
                const extraScore = processLineEffects(clearedTypes);
                score += extraScore;
                
                let gemBonus = 0;
                const gemCounts = {};
                for (const type of clearedTypes) {
                    if (type !== 'black') {
                        gemCounts[type] = (gemCounts[type] || 0) + 1;
                    }
                }
                for (const [type, count] of Object.entries(gemCounts)) {
                    if (count >= 4) {
                        gemBonus += count - 3;
                    }
                }
                score += gemBonus;
                
                lines += cleared;
                level = Math.min(100, Math.floor(score / 1000) + 1);
                const speedBonus = 1 + (level - 1) * 0.01;
                dropInterval = Math.max(100, 1000 / speedBonus);
                
                playClearSound();
                updateUI();
            }
        }

        function spawnPiece() {
            currentPiece = nextPiece || getRandomPiece();
            nextPiece = getRandomPiece();
            currentX = Math.floor((COLS - currentPiece.shape[0].length) / 2);
            currentY = 0;

            if (!validMove(currentPiece, currentX, currentY)) {
                endGame();
            }
            drawNextPiece();
        }

        function updateUI() {
            document.getElementById('score').textContent = score;
            document.getElementById('level').textContent = level;
            document.getElementById('lines').textContent = lines;
            
            const currentSpeed = (1 + (level - 1) * 0.01).toFixed(2) + 'x';
            document.getElementById('speed').textContent = '速度: ' + currentSpeed;
            
            if (score > highScore) {
                highScore = score;
                localStorage.setItem('tetrisHighScore', highScore);
                document.getElementById('highscore').textContent = highScore;
            }
        }

        function endGame() {
            gameOver = true;
            gameStarted = false;
            stopMusic();
            if (bgMusic) {
                bgMusic.pause();
            }
            playGameOverSound();
            
            document.getElementById('final-score').textContent = score;
            document.getElementById('gameover-overlay').style.display = 'flex';
            
            setTimeout(() => {
                document.getElementById('nameInputScore').textContent = score;
                const lastName = localStorage.getItem('tetrisLastName') || '';
                document.getElementById('playerNameInput').value = lastName;
                document.getElementById('playerNameInput').focus();
                document.getElementById('playerNameInput').select();
                document.getElementById('nameInputOverlay').classList.add('active');
            }, 500);
        }

        function saveToLeaderboard(name, gameScore) {
            localStorage.setItem('tetrisLastName', name);
            let leaderboard = JSON.parse(localStorage.getItem('tetrisLeaderboard')) || [];
            leaderboard.push({ name, score: gameScore, date: new Date().toISOString() });
            leaderboard.sort((a, b) => b.score - a.score);
            leaderboard = leaderboard.slice(0, 10);
            localStorage.setItem('tetrisLeaderboard', JSON.stringify(leaderboard));
        }

        function showLeaderboard() {
            const leaderboard = JSON.parse(localStorage.getItem('tetrisLeaderboard')) || [];
            const listEl = document.getElementById('leaderboardList');
            
            if (leaderboard.length === 0) {
                listEl.innerHTML = '<li class="leaderboard-empty">暂无记录</li>';
            } else {
                listEl.innerHTML = leaderboard.map((entry, i) => `
                    <li class="leaderboard-item">
                        <span class="leaderboard-rank">#${i + 1}</span>
                        <span class="leaderboard-name">${entry.name}</span>
                        <span class="leaderboard-score">${entry.score}</span>
                    </li>
                `).join('');
            }
            
            document.getElementById('leaderboardOverlay').classList.add('active');
        }

        function hideLeaderboard() {
            document.getElementById('leaderboardOverlay').classList.remove('active');
        }

        function startGame() {
            initBoard();
            score = 0;
            level = 1;
            lines = 0;
            dropInterval = 1000;
            gameOver = false;
            gameStarted = true;
            isPaused = false;
            speedModifier = 1;
            speedModifierEnd = 0;
            
            document.getElementById('start-overlay').style.display = 'none';
            document.getElementById('gameover-overlay').style.display = 'none';
            
            nextPiece = getRandomPiece();
            spawnPiece();
            updateUI();
            lastDrop = performance.now();
            
            if (bgMusic) {
                bgMusic.currentTime = musicPausedAt;
                bgMusic.play().catch(() => {});
            }
            musicPlaying = true;
            musicInterval = setInterval(playBgMusic, 400);
        }

        function resetGame() {
            stopMusic();
            if (bgMusic) {
                bgMusic.pause();
                musicPausedAt = 0;
                const musicFile = localStorage.getItem('tetrisMusicFile') || '苍天黄土.mp3';
                bgMusic = new Audio(musicFile);
                bgMusic.loop = true;
                bgMusic.volume = musicVolume;
            }
            initBoard();
            score = 0;
            level = 1;
            lines = 0;
            dropInterval = 1000;
            gameOver = false;
            gameStarted = false;
            isPaused = false;
            speedModifier = 1;
            speedModifierEnd = 0;
            currentPiece = null;
            nextPiece = null;
            
            document.getElementById('start-overlay').style.display = 'none';
            document.getElementById('gameover-overlay').style.display = 'none';
            document.getElementById('score').textContent = '0';
            document.getElementById('level').textContent = '1';
            document.getElementById('lines').textContent = '0';
            document.getElementById('speed').textContent = '速度: 1.00x';
            
            startGame();
            if (bgMusic) {
                bgMusic.play().catch(() => {});
            }
        }

        function togglePause() {
            if (!gameStarted || gameOver) return;
            isPaused = !isPaused;
            if (isPaused) {
                stopMusic();
            } else {
                if (bgMusic) {
                    bgMusic.currentTime = musicPausedAt;
                    bgMusic.play().catch(() => {});
                }
                musicPlaying = true;
                musicInterval = setInterval(playBgMusic, 400);
            }
        }

        function moveDown() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX, currentY + 1)) {
                currentY++;
            } else {
                lockPiece();
            }
        }

        function moveLeft() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX - 1, currentY)) {
                currentX--;
                playMoveSound();
            }
        }

        function moveRight() {
            if (gameOver || isPaused || !currentPiece) return;
            if (validMove(currentPiece, currentX + 1, currentY)) {
                currentX++;
                playMoveSound();
            }
        }

        function hardDrop() {
            if (gameOver || isPaused || !currentPiece) return;
            while (validMove(currentPiece, currentX, currentY + 1)) {
                currentY++;
            }
            playDropSound();
            lockPiece();
            updateUI();
        }

        function gameLoop(timestamp) {
            if (Date.now() > speedModifierEnd) {
                speedModifier = 1;
            }
            
            if (gameStarted && !gameOver && !isPaused) {
                let effectiveInterval = dropInterval / speedModifier;
                if (isDownHeld) {
                    effectiveInterval = 50;
                }
                if (timestamp - lastDrop > effectiveInterval) {
                    moveDown();
                    lastDrop = timestamp;
                }
                
                if (timestamp - lastMoveTime > moveDelay) {
                    if (isLeftHeld) {
                        if (timestamp - moveStartTime > initialMoveDelay) {
                            moveLeft();
                            lastMoveTime = timestamp;
                        }
                    } else if (isRightHeld) {
                        if (timestamp - moveStartTime > initialMoveDelay) {
                            moveRight();
                            lastMoveTime = timestamp;
                        }
                    }
                }
            }

            drawBoard();

            if (currentPiece && !gameOver) {
                let cellIndex = 0;
                for (let r = 0; r < currentPiece.shape.length; r++) {
                    for (let c = 0; c < currentPiece.shape[r].length; c++) {
                        if (currentPiece.shape[r][c]) {
                            const drawX = currentX + c;
                            const drawY = currentY + r;
                            if (drawY >= 0) {
                                drawBlock(ctx, drawX, drawY, currentPiece.cells[cellIndex]);
                            }
                            cellIndex++;
                        }
                    }
                }
            }

            requestAnimationFrame(gameLoop);
        }

        function getKeyDisplayName(key) {
            const keyMap = {
                'ArrowLeft': '←',
                'ArrowRight': '→',
                'ArrowUp': '↑',
                'ArrowDown': '↓',
                'Space': '空格',
                'Enter': 'Enter',
                'Escape': 'Esc',
                'Backspace': '退格',
                'KeyR': 'R',
                'KeyL': 'L',
                'KeyH': 'H',
                'KeyO': 'O',
                'KeyZ': 'Z',
                'KeyX': 'X',
                'KeyW': 'W',
                'KeyS': 'S',
                'KeyA': 'A',
                'KeyD': 'D',
                'KeyJ': 'J',
                'KeyK': 'K'
            };
            if (keyMap[key]) return keyMap[key];
            if (key.startsWith('Key')) return key.substring(3);
            if (key.startsWith('Digit')) return key.substring(5);
            return key.toUpperCase();
        }

        function updateKeyDisplay() {
            document.querySelectorAll('.key-display').forEach(el => {
                const action = el.dataset.action;
                if (keyBindings[action]) {
                    el.textContent = getKeyDisplayName(keyBindings[action]);
                }
            });
        }

        let listeningFor = null;

        document.querySelectorAll('.key-display').forEach(el => {
            el.addEventListener('click', () => {
                if (listeningFor) {
                    document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                }
                listeningFor = el.dataset.action;
                el.classList.add('listening');
                el.textContent = '按下按键...';
            });
        });

        document.addEventListener('keydown', (e) => {
            if (listeningFor) {
                e.preventDefault();
                keyBindings[listeningFor] = e.code;
                localStorage.setItem('tetrisKeyBindings', JSON.stringify(keyBindings));
                
                const el = document.querySelector(`[data-action="${listeningFor}"]`);
                el.textContent = getKeyDisplayName(e.code);
                el.classList.remove('listening');
                listeningFor = null;
                return;
            }

            const nameInputOverlay = document.getElementById('nameInputOverlay');
            if (nameInputOverlay.classList.contains('active')) {
                if (e.code === 'Enter') {
                    e.preventDefault();
                    document.getElementById('submitName').click();
                    return;
                }
            }

            const leaderboardOverlay = document.getElementById('leaderboardOverlay');
            if (leaderboardOverlay.classList.contains('active')) {
                if (e.code === 'Enter' || e.code === 'Escape') {
                    e.preventDefault();
                    hideLeaderboard();
                    return;
                }
            }

            const settingsPanel = document.getElementById('settingsPanel');
            if (settingsPanel.classList.contains('active')) {
                if (e.code === 'Escape') {
                    e.preventDefault();
                    document.getElementById('closeSettings').click();
                    return;
                }
            }

            const action = Object.keys(keyBindings).find(k => keyBindings[k] === e.code);
            if (action) {
                e.preventDefault();
                switch(action) {
                    case 'down': 
                        isDownHeld = true;
                        moveDown(); 
                        break;
                    case 'left': 
                        isLeftHeld = true;
                        moveStartTime = performance.now();
                        moveLeft(); 
                        highlightKey('key-left'); 
                        break;
                    case 'right': 
                        isRightHeld = true;
                        moveStartTime = performance.now();
                        moveRight(); 
                        highlightKey('key-right'); 
                        break;
                    case 'up': rotateLeft(); highlightKey('key-up'); break;
                    case 'rotateLeft': rotateLeft(); break;
                    case 'rotateRight': rotateRight(); break;
                    case 'btnA': rotateLeft(); highlightKey('btn-a'); break;
                    case 'btnB': rotateRight(); highlightKey('btn-b'); break;
                    case 'btnC': hardDrop(); highlightKey('btn-c'); break;
                    case 'start': 
                        if (!gameStarted || gameOver) {
                            startGame();
                        }
                        break;
                    case 'pause': togglePause(); break;
                    case 'reset': resetGame(); break;
                    case 'leaderboard': 
                        if (document.getElementById('leaderboardOverlay').classList.contains('active')) {
                            hideLeaderboard();
                            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                                togglePause();
                            }
                            wasPausedBeforeOverlay = false;
                        } else {
                            wasPausedBeforeOverlay = isPaused;
                            if (gameStarted && !gameOver && !isPaused) {
                                togglePause();
                            }
                            showLeaderboard();
                        }
                        break;
                    case 'settings': 
                        if (document.getElementById('settingsPanel').classList.contains('active')) {
                            document.getElementById('settingsPanel').classList.remove('active');
                            document.getElementById('settingsOverlay').classList.remove('active');
                            if (listeningFor) {
                                document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                                listeningFor = null;
                                updateKeyDisplay();
                            }
                            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                                togglePause();
                            }
                            wasPausedBeforeOverlay = false;
                        } else {
                            wasPausedBeforeOverlay = isPaused;
                            document.getElementById('settingsPanel').classList.add('active');
                            document.getElementById('settingsOverlay').classList.add('active');
                            updateKeyDisplay();
                            loadAvailableMusic();
                            document.getElementById('musicVolume').value = Math.round(musicVolume * 100);
                            if (gameStarted && !gameOver && !isPaused) {
                                togglePause();
                            }
                        }
                        break;
                }
            }
        });

        function highlightKey(id) {
            const el = document.getElementById(id);
            if (el) {
                el.style.filter = 'brightness(2)';
                setTimeout(() => el.style.filter = '', 150);
            }
        }

        document.getElementById('settingsBtn').addEventListener('click', () => {
            if (document.getElementById('settingsPanel').classList.contains('active')) {
                document.getElementById('settingsPanel').classList.remove('active');
                document.getElementById('settingsOverlay').classList.remove('active');
                if (listeningFor) {
                    document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                    listeningFor = null;
                    updateKeyDisplay();
                }
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            } else {
                wasPausedBeforeOverlay = isPaused;
                document.getElementById('settingsPanel').classList.add('active');
                document.getElementById('settingsOverlay').classList.add('active');
                updateKeyDisplay();
                loadAvailableMusic();
                document.getElementById('musicVolume').value = Math.round(musicVolume * 100);
                if (gameStarted && !gameOver && !isPaused) {
                    togglePause();
                }
            }
        });

        document.getElementById('resetDefaultsBtn').addEventListener('click', () => {
            keyBindings = {...DEFAULT_KEYS};
            localStorage.setItem('tetrisKeyBindings', JSON.stringify(keyBindings));
            updateKeyDisplay();
        });

        document.getElementById('closeSettings').addEventListener('click', () => {
            document.getElementById('settingsPanel').classList.remove('active');
            document.getElementById('settingsOverlay').classList.remove('active');
            if (listeningFor) {
                document.querySelector(`[data-action="${listeningFor}"]`).classList.remove('listening');
                listeningFor = null;
                updateKeyDisplay();
            }
            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                togglePause();
            }
            wasPausedBeforeOverlay = false;
        });

        document.getElementById('settingsOverlay').addEventListener('click', () => {
            document.getElementById('closeSettings').click();
        });

        document.getElementById('btn-start').addEventListener('click', () => {
            initAudio();
            if (!gameStarted || gameOver) {
                startGame();
            }
        });

        document.getElementById('btn-select').addEventListener('click', togglePause);
        document.getElementById('btn-reset').addEventListener('click', resetGame);
        document.getElementById('btn-rank').addEventListener('click', () => {
            if (document.getElementById('leaderboardOverlay').classList.contains('active')) {
                hideLeaderboard();
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            } else {
                wasPausedBeforeOverlay = isPaused;
                if (gameStarted && !gameOver && !isPaused) {
                    togglePause();
                }
                showLeaderboard();
            }
        });

        document.getElementById('btn-a').addEventListener('click', rotateLeft);
        document.getElementById('btn-b').addEventListener('click', rotateRight);
        document.getElementById('btn-c').addEventListener('click', hardDrop);

        document.getElementById('key-left').addEventListener('click', moveLeft);
        document.getElementById('key-right').addEventListener('click', moveRight);
        document.getElementById('key-down').addEventListener('click', moveDown);
        document.getElementById('key-up').addEventListener('click', rotateLeft);

        document.getElementById('m-left').addEventListener('touchstart', (e) => { e.preventDefault(); isLeftHeld = true; moveLeft(); });
        document.getElementById('m-left').addEventListener('touchend', (e) => { e.preventDefault(); isLeftHeld = false; });
        document.getElementById('m-right').addEventListener('touchstart', (e) => { e.preventDefault(); isRightHeld = true; moveRight(); });
        document.getElementById('m-right').addEventListener('touchend', (e) => { e.preventDefault(); isRightHeld = false; });
        document.getElementById('m-down').addEventListener('touchstart', (e) => { e.preventDefault(); isDownHeld = true; moveDown(); });
        document.getElementById('m-down').addEventListener('touchend', (e) => { e.preventDefault(); isDownHeld = false; });
        document.getElementById('m-up').addEventListener('touchstart', (e) => { e.preventDefault(); rotateLeft(); });
        document.getElementById('m-a').addEventListener('touchstart', (e) => { e.preventDefault(); rotateLeft(); });
        document.getElementById('m-b').addEventListener('touchstart', (e) => { e.preventDefault(); rotateRight(); });
        document.getElementById('m-c').addEventListener('touchstart', (e) => { e.preventDefault(); hardDrop(); });
        document.getElementById('m-vol-up').addEventListener('touchstart', (e) => { e.preventDefault(); initAudio(); volume = Math.min(1, volume + 0.1); localStorage.setItem('tetrisVolume', volume); updateVolumeDisplay(); showVolume(); playTone(440, 0.1, 'square', 0.2); });
        document.getElementById('m-vol-down').addEventListener('touchstart', (e) => { e.preventDefault(); initAudio(); volume = Math.max(0, volume - 0.1); localStorage.setItem('tetrisVolume', volume); updateVolumeDisplay(); showVolume(); playTone(440, 0.1, 'square', 0.2); });

        function showVolume() {
            const display = document.getElementById('volumeDisplay');
            display.classList.add('show');
            if (volumeDisplayTimeout) {
                clearTimeout(volumeDisplayTimeout);
            }
            volumeDisplayTimeout = setTimeout(() => {
                display.classList.remove('show');
            }, 3000);
        }

        function updateVolumeDisplay() {
            const percent = Math.round(volume * 100);
            document.getElementById('volumeDisplay').textContent = percent + '%';
            
            const holes = document.querySelectorAll('.speaker-hole');
            const activeCount = Math.ceil(percent / 12.5);
            holes.forEach((hole, i) => {
                hole.classList.toggle('active', i < activeCount);
            });
        }

        document.getElementById('speaker').addEventListener('click', () => {
            initAudio();
            volume = volume - 0.1;
            if (volume < 0) {
                volume = 1.0;
            }
            localStorage.setItem('tetrisVolume', volume);
            updateVolumeDisplay();
            showVolume();
            playTone(440, 0.1, 'square', 0.2);
        });

        document.getElementById('speaker').addEventListener('contextmenu', (e) => {
            e.preventDefault();
            volume = volume + 0.1;
            if (volume > 1.0) {
                volume = 0;
            }
            localStorage.setItem('tetrisVolume', volume);
            updateVolumeDisplay();
            showVolume();
            playTone(440, 0.1, 'square', 0.2);
        });

        document.getElementById('submitName').addEventListener('click', () => {
            const name = document.getElementById('playerNameInput').value.trim() || '匿名';
            saveToLeaderboard(name, score);
            document.getElementById('nameInputOverlay').classList.remove('active');
            showLeaderboard();
        });

        document.getElementById('playerNameInput').addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                document.getElementById('submitName').click();
            }
        });

        document.getElementById('closeLeaderboard').addEventListener('click', () => {
            hideLeaderboard();
            if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                togglePause();
            }
            wasPausedBeforeOverlay = false;
        });
        document.getElementById('leaderboardOverlay').addEventListener('click', (e) => {
            if (e.target === document.getElementById('leaderboardOverlay')) {
                hideLeaderboard();
                if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                    togglePause();
                }
                wasPausedBeforeOverlay = false;
            }
        });
        document.addEventListener('keydown', (e) => {
            if (e.code === 'Enter' || e.code === 'Escape') {
                const leaderboardOverlay = document.getElementById('leaderboardOverlay');
                if (leaderboardOverlay.classList.contains('active')) {
                    hideLeaderboard();
                    if (gameStarted && !gameOver && wasPausedBeforeOverlay) {
                        togglePause();
                    }
                    wasPausedBeforeOverlay = false;
                }
            }
        });

        document.addEventListener('keyup', (e) => {
            const action = Object.keys(keyBindings).find(k => keyBindings[k] === e.code);
            if (action === 'down') {
                isDownHeld = false;
            }
            if (action === 'left') {
                isLeftHeld = false;
            }
            if (action === 'right') {
                isRightHeld = false;
            }
        });

        let resizeTimeout;
        window.addEventListener('resize', () => {
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(setupScale, 100);
        });

        volume = parseFloat(localStorage.getItem('tetrisVolume')) || 1.0;
        musicVolume = parseFloat(localStorage.getItem('tetrisMusicVolume')) || 0.5;
        updateVolumeDisplay();
        loadAvailableMusic();

        const savedMusicFile = localStorage.getItem('tetrisMusicFile');
        if (savedMusicFile) {
            bgMusic = new Audio(savedMusicFile);
            bgMusic.volume = musicVolume;
        }

        setupCanvasSize();
        initBoard();
        drawBoard();
        drawNextPiece();
        startMusic();
        requestAnimationFrame(gameLoop);
        
        setTimeout(setupScale, 100);

        document.getElementById('musicSelect').addEventListener('change', (e) => {
            const file = e.target.value;
            localStorage.setItem('tetrisMusicFile', file);
            
            if (bgMusic) {
                bgMusic.pause();
            }
            
            bgMusic = new Audio(file);
            bgMusic.loop = true;
            bgMusic.volume = musicVolume;
            
            if (gameStarted && !isPaused && !gameOver) {
                bgMusic.play().catch(() => {});
            }
        });

        document.getElementById('musicVolume').addEventListener('input', (e) => {
            updateMusicVolume(e.target.value / 100);
        });

        document.getElementById('musicVolume').value = Math.round(musicVolume * 100);

        document.getElementById('uploadMusicBtn').addEventListener('click', () => {
            document.getElementById('musicFileInput').click();
        });

        document.getElementById('musicFileInput').addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                const url = URL.createObjectURL(file);
                localStorage.setItem('tetrisMusicFile', url);
                localStorage.setItem('tetrisMusicName', file.name);
                
                if (bgMusic) {
                    bgMusic.pause();
                }
                
                bgMusic = new Audio(url);
                bgMusic.loop = true;
                bgMusic.volume = musicVolume;
                
                const musicSelect = document.getElementById('musicSelect');
                const option = document.createElement('option');
                option.value = url;
                option.textContent = file.name;
                option.selected = true;
                
                const existingOption = Array.from(musicSelect.options).find(opt => opt.textContent === file.name);
                if (!existingOption) {
                    musicSelect.appendChild(option);
                } else {
                    existingOption.selected = true;
                }
                
                if (gameStarted && !isPaused && !gameOver) {
                    bgMusic.play().catch(() => {});
                }
            }
        });
    </script>
</body>
</html>










后续更新也在此楼.
4#
Jaxxhh886 发表于 2026-3-28 22:47
不错不错,这么好的东西,需要这么多的代码去支撑
5#
zyrwl 发表于 2026-3-28 22:48
好东西值得分享!!!!谢谢!!
6#
Bart219 发表于 2026-3-28 23:55
没想到一个俄罗斯方块背后需要这么多代码支持,不错不错,支持一下楼主!
7#
classSTU00 发表于 2026-3-29 00:21
这个可以,学一下楼主的代码
8#
aalex13 发表于 2026-3-29 01:45
确实是童年回忆,但是缺不想再玩了(或者说没动力玩了)
9#
jrjmusic 发表于 2026-3-29 02:56
满满的回忆杀啊……唉……岁月无声。
10#
 楼主| 午夜飘雪 发表于 2026-3-29 03:21 |楼主
Bart219 发表于 2026-3-28 23:55
没想到一个俄罗斯方块背后需要这么多代码支持,不错不错,支持一下楼主!

原版没多少代码  , 增加了些其他机制
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-3-29 08:38

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表