🎮ArcadeLab

垃圾小卫士 · 双垃圾挑战

by MysticLion44
2387 lines90.9 KB
▶ Play
<!DOCTYPE html>
<html lang="zh">
<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>
        * {
            box-sizing: border-box;
            user-select: none;
            -webkit-tap-highlight-color: transparent;
        }
        body {
            margin: 0;
            min-height: 100vh;
            background: #2c3e50;
            font-family: 'Comic Sans MS', 'Chalkboard SE', cursive, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            touch-action: none;
            padding: 4px;
        }
        #game-container {
            width: 100%;
            max-width: 500px;
            height: 100vh;
            max-height: 820px;
            background: #ecf0f1;
            border-radius: 32px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            display: flex;
            flex-direction: column;
            overflow: hidden;
            position: relative;
            padding: 8px;
        }

        /* ---------- 选择界面 ---------- */
        #select-screen {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
            gap: 14px;
            text-align: center;
            background: #ecf0f1;
            border-radius: 28px;
            padding: 20px 16px;
        }
        #select-screen h1 {
            font-size: 2rem;
            color: #2c3e50;
            margin: 0;
            line-height: 1.2;
        }
        #select-screen .subtitle {
            font-size: 1.1rem;
            color: #7f8c8d;
            margin: -4px 0 4px 0;
        }
        #select-screen p {
            font-size: 1.1rem;
            color: #34495e;
            margin: 0;
        }
        .select-bins {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 12px;
            width: 100%;
            max-width: 280px;
        }
        .select-bin {
            background: white;
            border-radius: 30px;
            padding: 18px 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            box-shadow: 0 4px 0 #bdc3c7;
            border: 3px solid #95a5a6;
            transition: transform 0.1s;
            touch-action: manipulation;
            cursor: pointer;
            min-height: 88px;
            justify-content: center;
        }
        .select-bin:active {
            transform: scale(0.92);
        }
        .select-bin .bin-emoji {
            font-size: 3rem;
            line-height: 1;
        }
        .select-bin .bin-label {
            font-size: 1.1rem;
            font-weight: bold;
            margin-top: 4px;
            color: #2c3e50;
        }
        .select-bin.bin-recyclable {
            border-color: #3498db;
            background: #d6eaf8;
        }
        .select-bin.bin-hazardous {
            border-color: #e74c3c;
            background: #fadbd8;
        }
        .select-bin.bin-kitchen {
            border-color: #27ae60;
            background: #d5f5e3;
        }
        .select-bin.bin-other {
            border-color: #95a5a6;
            background: #ebedef;
        }
        .select-hint {
            font-size: 0.9rem;
            color: #7f8c8d;
            background: rgba(255, 255, 255, 0.6);
            padding: 6px 18px;
            border-radius: 20px;
        }
        .high-score-display {
            font-size: 1rem;
            color: #f39c12;
            font-weight: bold;
            background: rgba(255, 255, 255, 0.7);
            padding: 6px 20px;
            border-radius: 22px;
            border: 2px solid #f39c12;
        }

        /* ---------- 游戏界面 ---------- */
        #game-screen {
            display: none;
            flex-direction: column;
            height: 100%;
            background: #ecf0f1;
            border-radius: 28px;
            overflow: hidden;
            gap: 4px;
        }

        /* 顶部状态栏 */
        #top-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 6px 12px;
            background: rgba(189, 195, 199, 0.6);
            border-radius: 28px;
            flex-shrink: 0;
            height: 52px;
            backdrop-filter: blur(4px);
            z-index: 5;
            gap: 6px;
            flex-wrap: nowrap;
        }
        #timer-display {
            font-size: 1.4rem;
            font-weight: bold;
            color: #2c3e50;
            background: white;
            padding: 4px 16px;
            border-radius: 24px;
            border: 2px solid #e74c3c;
            white-space: nowrap;
            min-width: 60px;
            text-align: center;
        }
        #timer-display.warning {
            color: #e74c3c;
            animation: pulse 0.6s ease-in-out infinite alternate;
        }
        @keyframes pulse {
            from {
                transform: scale(1);
            }
            to {
                transform: scale(1.08);
            }
        }
        #score-display {
            font-size: 1.5rem;
            background: white;
            padding: 4px 20px;
            border-radius: 24px;
            font-weight: bold;
            border: 2px solid #2c3e50;
            white-space: nowrap;
            min-width: 70px;
            text-align: center;
        }
        #score-display.negative {
            border-color: #e74c3c;
            color: #e74c3c;
        }
        #category-display {
            font-size: 0.9rem;
            background: #2c3e50;
            color: white;
            padding: 4px 14px;
            border-radius: 20px;
            white-space: nowrap;
        }

        /* ---------- 地图区域 ---------- */
        #map-container {
            flex: 1;
            position: relative;
            border-radius: 20px;
            overflow: hidden;
            border: 4px solid #7f8c8d;
            background: #7ec87e;
            min-height: 160px;
            background-image: linear-gradient(rgba(255, 255, 255, 0.12) 1px, transparent 1px),
                linear-gradient(90deg, rgba(255, 255, 255, 0.12) 1px, transparent 1px);
            background-size: 28px 28px;
        }
        .road-h {
            position: absolute;
            height: 16px;
            background: #bdc3c7;
            border-top: 2px solid #95a5a6;
            border-bottom: 2px solid #95a5a6;
            z-index: 1;
            pointer-events: none;
        }
        .road-v {
            position: absolute;
            width: 16px;
            background: #bdc3c7;
            border-left: 2px solid #95a5a6;
            border-right: 2px solid #95a5a6;
            z-index: 1;
            pointer-events: none;
        }
        .tree {
            position: absolute;
            font-size: 1.8rem;
            z-index: 2;
            pointer-events: none;
            text-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
        }
        .flower {
            position: absolute;
            font-size: 1.2rem;
            z-index: 1;
            pointer-events: none;
        }
        .house {
            position: absolute;
            font-size: 2.2rem;
            z-index: 2;
            pointer-events: none;
            text-shadow: 0 3px 6px rgba(0, 0, 0, 0.15);
        }

        /* ---------- 垃圾桶 ---------- */
        #player-bin {
            position: absolute;
            width: 60px;
            height: 68px;
            z-index: 10;
            pointer-events: none;
            transition: left 0.08s linear, top 0.08s linear;
            filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.25));
        }
        #player-bin .bin-body {
            position: relative;
            width: 100%;
            height: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: flex-end;
            transition: transform 0.15s;
        }
        #player-bin .bin-body .bucket {
            width: 52px;
            height: 44px;
            background: #4a90e2;
            border-radius: 6px 6px 3px 3px;
            border: 3px solid #2c3e50;
            border-top: none;
            position: relative;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 1.4rem;
            font-weight: bold;
            color: white;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            transition: background 0.3s;
        }
        #player-bin .bin-body .lid {
            width: 46px;
            height: 10px;
            background: #7f8c8d;
            border-radius: 10px 10px 0 0;
            border: 3px solid #2c3e50;
            border-bottom: none;
            margin-bottom: -2px;
            position: relative;
            z-index: 2;
            transition: transform 0.2s;
        }
        #player-bin .bin-body .wheel {
            position: absolute;
            bottom: -5px;
            width: 13px;
            height: 13px;
            background: #2c3e50;
            border-radius: 50%;
            border: 2px solid #1a252f;
        }
        #player-bin .bin-body .wheel.left {
            left: 2px;
        }
        #player-bin .bin-body .wheel.right {
            right: 2px;
        }
        #player-bin .bin-body .face {
            position: absolute;
            font-size: 1.1rem;
            top: 4px;
            left: 50%;
            transform: translateX(-50%);
            line-height: 1;
            text-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
            z-index: 3;
            pointer-events: none;
            transition: all 0.2s;
        }
        #player-bin .bin-body .cat-icon {
            position: absolute;
            font-size: 0.9rem;
            bottom: 3px;
            right: 3px;
            z-index: 3;
            pointer-events: none;
            text-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
        }
        #player-bin .mouth-icon {
            position: absolute;
            font-size: 2.4rem;
            top: -12px;
            left: 50%;
            transform: translateX(-50%) scale(0);
            opacity: 0;
            transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
            z-index: 12;
            pointer-events: none;
        }
        #player-bin .mouth-icon.show {
            opacity: 1;
            transform: translateX(-50%) scale(1.4);
        }
        #player-bin .stars {
            position: absolute;
            font-size: 1.8rem;
            top: -20px;
            right: -8px;
            opacity: 0;
            pointer-events: none;
            z-index: 13;
            transition: all 0.5s ease;
        }
        #player-bin .stars.show {
            opacity: 1;
            transform: translateY(-25px) scale(1.6);
        }

        #player-bin.walking .bin-body {
            animation: walkSway 0.4s ease-in-out infinite alternate;
        }
        @keyframes walkSway {
            0% {
                transform: rotate(-5deg) scale(1);
            }
            100% {
                transform: rotate(5deg) scale(1.02);
            }
        }
        #player-bin.eating .bin-body {
            animation: eatOpen 0.5s ease;
        }
        @keyframes eatOpen {
            0% {
                transform: scale(1) rotate(0);
            }
            30% {
                transform: scale(1.2) rotate(-8deg);
            }
            60% {
                transform: scale(0.95) rotate(8deg);
            }
            100% {
                transform: scale(1) rotate(0);
            }
        }
        #player-bin.kicking .bin-body {
            animation: kickAction 0.6s ease;
        }
        @keyframes kickAction {
            0% {
                transform: scale(1) rotate(0);
            }
            20% {
                transform: scale(1.1) rotate(-18deg) translateX(-8px);
            }
            40% {
                transform: scale(0.9) rotate(14deg) translateX(12px);
            }
            60% {
                transform: scale(1.05) rotate(-6deg) translateX(-4px);
            }
            100% {
                transform: scale(1) rotate(0) translateX(0);
            }
        }

        /* ---------- 垃圾物品 ---------- */
        .garbage-item {
            position: absolute;
            font-size: 2.6rem;
            line-height: 1;
            width: 54px;
            height: 54px;
            display: flex;
            justify-content: center;
            align-items: center;
            background: rgba(255, 255, 255, 0.85);
            border-radius: 50%;
            border: 4px solid #2c3e50;
            box-shadow: 0 4px 0 #7f8c8d;
            z-index: 5;
            pointer-events: none;
            transition: transform 0.2s, opacity 0.3s;
        }
        /* 特殊垃圾标记 */
        .garbage-item.special {
            border-color: #8e44ad;
            background: #f4ecf7;
            box-shadow: 0 4px 0 #6c3483, 0 0 20px rgba(142, 68, 173, 0.3);
            animation: specialGlow 1.2s ease-in-out infinite alternate;
        }
        @keyframes specialGlow {
            0% {
                box-shadow: 0 4px 0 #6c3483, 0 0 15px rgba(142, 68, 173, 0.2);
            }
            100% {
                box-shadow: 0 4px 0 #6c3483, 0 0 35px rgba(142, 68, 173, 0.5);
            }
        }
        .garbage-item .special-tag {
            position: absolute;
            bottom: -22px;
            font-size: 0.55rem;
            background: #8e44ad;
            color: white;
            padding: 1px 8px;
            border-radius: 10px;
            white-space: nowrap;
            font-weight: bold;
        }
        /* 吸入动画 */
        .garbage-item.sucked {
            animation: suckIn 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
            pointer-events: none;
            z-index: 20;
        }
        @keyframes suckIn {
            0% {
                transform: scale(1) rotate(0deg);
                opacity: 1;
            }
            60% {
                transform: scale(0.5) rotate(200deg) translateY(-30px);
                opacity: 0.9;
            }
            100% {
                transform: scale(0) rotate(360deg) translateY(-60px);
                opacity: 0;
            }
        }
        /* 踢飞动画 */
        .garbage-item.flying {
            animation: flyAway 0.8s ease forwards;
            pointer-events: none;
        }
        @keyframes flyAway {
            0% {
                transform: translate(0, 0) scale(1) rotate(0);
                opacity: 1;
            }
            30% {
                transform: translate(60px, -80px) scale(1.3) rotate(40deg);
                opacity: 1;
            }
            100% {
                transform: translate(120px, -160px) scale(0.3) rotate(120deg);
                opacity: 0;
            }
        }

        /* 交互提示 */
        #interact-hint {
            position: absolute;
            bottom: 10px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(0, 0, 0, 0.8);
            color: #f1c40f;
            padding: 6px 18px;
            border-radius: 28px;
            font-size: 0.9rem;
            z-index: 20;
            opacity: 0;
            transition: opacity 0.3s;
            pointer-events: none;
            white-space: nowrap;
            border: 2px solid #f1c40f;
            font-weight: bold;
        }
        #interact-hint.show {
            opacity: 1;
        }
        #interact-hint.special-hint {
            border-color: #8e44ad;
            color: #d7bde2;
            background: rgba(46, 0, 70, 0.85);
        }

        /* 反馈气泡 */
        #feedback {
            position: absolute;
            top: 18%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 1.8rem;
            font-weight: bold;
            text-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
            pointer-events: none;
            z-index: 20;
            opacity: 0;
            transition: all 0.25s ease;
            background: rgba(0, 0, 0, 0.75);
            padding: 8px 22px;
            border-radius: 40px;
            color: white;
            white-space: nowrap;
        }
        #feedback.show {
            opacity: 1;
            transform: translate(-50%, -70%) scale(1.1);
        }

        /* ---------- 控制面板 ---------- */
        #control-panel {
            flex-shrink: 0;
            display: flex;
            gap: 8px;
            padding: 6px 6px 4px 6px;
            background: rgba(189, 195, 199, 0.4);
            border-radius: 28px;
            backdrop-filter: blur(4px);
            z-index: 5;
            height: 140px;
            min-height: 120px;
        }
        #joystick-area {
            flex: 1;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 4px;
            min-width: 0;
        }
        #joystick-base {
            position: relative;
            width: 100%;
            max-width: 130px;
            aspect-ratio: 1/1;
            background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.3), rgba(0, 0, 0, 0.08));
            border-radius: 50%;
            border: 5px solid rgba(44, 62, 80, 0.3);
            box-shadow: inset 0 -4px 8px rgba(0, 0, 0, 0.15), 0 4px 12px rgba(0, 0, 0, 0.1);
            touch-action: none;
            cursor: grab;
            transition: border-color 0.2s;
        }
        #joystick-base.active {
            border-color: rgba(52, 152, 219, 0.6);
            box-shadow: 0 0 30px rgba(52, 152, 219, 0.25);
        }
        #joystick-thumb {
            position: absolute;
            width: 44%;
            aspect-ratio: 1/1;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: radial-gradient(circle at 35% 35%, #ecf0f1, #bdc3c7);
            border-radius: 50%;
            border: 4px solid #2c3e50;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25), inset 0 -3px 6px rgba(0, 0, 0, 0.1);
            pointer-events: none;
            transition: box-shadow 0.2s;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 1.2rem;
            color: #2c3e50;
            font-weight: bold;
        }
        #joystick-thumb::after {
            content: '⬤';
            font-size: 0.8rem;
            color: rgba(44, 62, 80, 0.3);
        }
        #joystick-base.active #joystick-thumb {
            box-shadow: 0 4px 20px rgba(52, 152, 219, 0.4);
        }
        .joystick-arrow {
            position: absolute;
            font-size: 0.9rem;
            color: rgba(44, 62, 80, 0.25);
            pointer-events: none;
        }
        .joystick-arrow.up {
            top: 6px;
            left: 50%;
            transform: translateX(-50%);
        }
        .joystick-arrow.down {
            bottom: 6px;
            left: 50%;
            transform: translateX(-50%);
        }
        .joystick-arrow.left {
            left: 6px;
            top: 50%;
            transform: translateY(-50%);
        }
        .joystick-arrow.right {
            right: 6px;
            top: 50%;
            transform: translateY(-50%);
        }

        #knob-area {
            flex: 1;
            display: flex;
            flex-direction: column;
            gap: 6px;
            padding: 4px 6px 4px 0;
            min-width: 0;
        }
        .knob-btn {
            flex: 1;
            border-radius: 40px;
            border: none;
            font-family: inherit;
            font-weight: bold;
            font-size: 1.4rem;
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 10px;
            box-shadow: 0 6px 0 rgba(0, 0, 0, 0.2), inset 0 -2px 8px rgba(0, 0, 0, 0.05);
            cursor: pointer;
            touch-action: manipulation;
            transition: all 0.08s;
            padding: 0 8px;
            position: relative;
            min-height: 54px;
        }
        .knob-btn:active {
            transform: translateY(4px);
            box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
        }
        .knob-btn .knob-icon {
            font-size: 2.2rem;
            line-height: 1;
        }
        .knob-btn .knob-label {
            font-size: 1.4rem;
            letter-spacing: 2px;
            font-weight: 900;
        }
        .knob-btn::before {
            content: '';
            position: absolute;
            inset: 4px;
            border-radius: 34px;
            border: 2px solid rgba(255, 255, 255, 0.25);
            pointer-events: none;
        }
        .knob-btn.eat-btn {
            background: linear-gradient(145deg, #2ecc71, #27ae60);
            border: 3px solid #1e8449;
        }
        .knob-btn.eat-btn:active {
            background: linear-gradient(145deg, #27ae60, #1e8449);
        }
        .knob-btn.spit-btn {
            background: linear-gradient(145deg, #e67e22, #d35400);
            border: 3px solid #a04000;
        }
        .knob-btn.spit-btn:active {
            background: linear-gradient(145deg, #d35400, #a04000);
        }
        .knob-btn .knob-dots {
            position: absolute;
            width: 100%;
            height: 100%;
            pointer-events: none;
            border-radius: 40px;
            overflow: hidden;
        }
        .knob-btn .knob-dots span {
            position: absolute;
            width: 4px;
            height: 8px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 3px;
        }

        #bottom-extra {
            display: flex;
            gap: 8px;
            justify-content: center;
            padding: 4px 0 2px 0;
            flex-shrink: 0;
        }
        .extra-btn {
            background: rgba(255, 255, 255, 0.7);
            border: 2px solid #7f8c8d;
            border-radius: 24px;
            padding: 6px 20px;
            font-size: 0.95rem;
            font-weight: bold;
            color: #2c3e50;
            cursor: pointer;
            touch-action: manipulation;
            font-family: inherit;
            box-shadow: 0 3px 0 #bdc3c7;
            transition: all 0.08s;
            min-height: 40px;
        }
        .extra-btn:active {
            transform: translateY(3px);
            box-shadow: 0 0px 0 #bdc3c7;
        }
        .extra-btn.reset {
            background: #f39c12;
            color: white;
            border-color: #d68910;
            box-shadow: 0 3px 0 #d68910;
        }
        .extra-btn.back {
            background: #3498db;
            color: white;
            border-color: #2471a3;
            box-shadow: 0 3px 0 #2471a3;
        }

        /* ---------- 游戏结束覆盖层 ---------- */
        #game-over-overlay {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.75);
            backdrop-filter: blur(4px);
            border-radius: 28px;
            z-index: 200;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            color: white;
            text-align: center;
            padding: 20px;
        }
        #game-over-overlay h1 {
            font-size: 2.4rem;
            margin: 0 0 4px 0;
        }
        #game-over-overlay .final-score {
            font-size: 3rem;
            font-weight: bold;
            color: #f1c40f;
            margin: 4px 0;
        }
        #game-over-overlay .high-score {
            font-size: 1.3rem;
            color: #ecf0f1;
            margin: 2px 0 14px 0;
        }
        #game-over-overlay .high-score span {
            color: #f39c12;
            font-weight: bold;
        }
        #game-over-overlay .btn-group {
            display: flex;
            gap: 14px;
            margin-top: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }
        #game-over-overlay .btn-group button {
            background: #f39c12;
            border: none;
            border-radius: 36px;
            padding: 14px 32px;
            font-size: 1.2rem;
            font-weight: bold;
            color: white;
            box-shadow: 0 5px 0 #d68910;
            cursor: pointer;
            font-family: inherit;
            min-height: 52px;
            min-width: 130px;
            touch-action: manipulation;
        }
        #game-over-overlay .btn-group button:active {
            transform: translateY(4px);
            box-shadow: 0 1px 0 #d68910;
        }
        #game-over-overlay .btn-group button.back-btn {
            background: #3498db;
            box-shadow: 0 5px 0 #2471a3;
        }
        #game-over-overlay .btn-group button.back-btn:active {
            box-shadow: 0 1px 0 #2471a3;
        }

        /* ---------- 答题覆盖层 ---------- */
        #quiz-overlay {
            display: none;
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.6);
            backdrop-filter: blur(4px);
            border-radius: 28px;
            z-index: 100;
            justify-content: center;
            align-items: center;
            padding: 16px;
        }
        #quiz-box {
            background: #ecf0f1;
            border-radius: 36px;
            padding: 24px 20px;
            max-width: 360px;
            width: 100%;
            text-align: center;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            border: 4px solid #2c3e50;
        }
        #quiz-question {
            font-size: 1.3rem;
            font-weight: bold;
            color: #2c3e50;
            margin: 0 0 10px 0;
            line-height: 1.4;
        }
        #quiz-options {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
        }
        .quiz-option {
            background: white;
            border: 3px solid #7f8c8d;
            border-radius: 28px;
            padding: 12px 5px;
            font-size: 1rem;
            font-weight: bold;
            color: #2c3e50;
            cursor: pointer;
            touch-action: manipulation;
            box-shadow: 0 4px 0 #95a5a6;
            font-family: inherit;
            min-height: 50px;
        }
        .quiz-option:active {
            transform: scale(0.94);
            box-shadow: 0 1px 0 #95a5a6;
        }
        .quiz-option.correct {
            background: #2ecc71;
            border-color: #27ae60;
            color: white;
            box-shadow: 0 4px 0 #1e8449;
        }
        .quiz-option.wrong {
            background: #e74c3c;
            border-color: #c0392b;
            color: white;
            box-shadow: 0 4px 0 #922b21;
        }
        .quiz-option.disabled {
            pointer-events: none;
            opacity: 0.6;
        }
        #quiz-result {
            font-size: 1rem;
            font-weight: bold;
            min-height: 2.5em;
            color: #2c3e50;
            background: white;
            border-radius: 20px;
            padding: 8px 12px;
            border: 2px solid #7f8c8d;
            margin: 8px 0;
        }
        #quiz-continue {
            background: #3498db;
            border: none;
            border-radius: 28px;
            padding: 12px 0;
            font-size: 1.2rem;
            font-weight: bold;
            color: white;
            box-shadow: 0 4px 0 #2471a3;
            cursor: pointer;
            font-family: inherit;
            min-height: 50px;
        }
        #quiz-continue:active {
            transform: translateY(4px);
            box-shadow: 0 1px 0 #2471a3;
        }
        #quiz-continue.hidden {
            display: none;
        }

        .hidden {
            display: none !important;
        }
        .fade-in {
            animation: fadeIn 0.3s ease;
        }
        @keyframes fadeIn {
            from {
                transform: scale(0.9);
                opacity: 0;
            }
            to {
                transform: scale(1);
                opacity: 1;
            }
        }

        /* 响应式 */
        @media (max-width: 400px) {
            #game-container {
                padding: 4px;
                border-radius: 24px;
            }
            #player-bin {
                width: 50px;
                height: 58px;
            }
            #player-bin .bin-body .bucket {
                width: 44px;
                height: 36px;
                font-size: 1.2rem;
            }
            #player-bin .bin-body .lid {
                width: 38px;
                height: 8px;
            }
            #player-bin .bin-body .face {
                font-size: 0.9rem;
            }
            .garbage-item {
                font-size: 2.2rem;
                width: 46px;
                height: 46px;
            }
            #control-panel {
                height: 120px;
                min-height: 100px;
                gap: 6px;
            }
            #joystick-base {
                max-width: 110px;
            }
            .knob-btn {
                font-size: 1.2rem;
                min-height: 46px;
            }
            .knob-btn .knob-icon {
                font-size: 1.8rem;
            }
            .knob-btn .knob-label {
                font-size: 1.1rem;
            }
            #top-bar {
                height: 46px;
                padding: 4px 10px;
            }
            #timer-display {
                font-size: 1.2rem;
                padding: 2px 12px;
                min-width: 50px;
            }
            #score-display {
                font-size: 1.3rem;
                padding: 2px 14px;
                min-width: 60px;
            }
            #category-display {
                font-size: 0.8rem;
                padding: 2px 10px;
            }
            .extra-btn {
                font-size: 0.85rem;
                padding: 4px 14px;
                min-height: 34px;
            }
            #quiz-question {
                font-size: 1.1rem;
            }
            #game-over-overlay h1 {
                font-size: 2rem;
            }
            #game-over-overlay .final-score {
                font-size: 2.4rem;
            }
            #game-over-overlay .btn-group button {
                padding: 10px 22px;
                font-size: 1rem;
                min-width: 100px;
                min-height: 44px;
            }
        }
        @media (max-height: 640px) {
            #control-panel {
                height: 100px;
                min-height: 80px;
            }
            #joystick-base {
                max-width: 100px;
            }
            .knob-btn {
                min-height: 40px;
                font-size: 1rem;
            }
            .knob-btn .knob-icon {
                font-size: 1.6rem;
            }
            .knob-btn .knob-label {
                font-size: 1rem;
            }
            #map-container {
                min-height: 120px;
            }
            #top-bar {
                height: 40px;
            }
            #timer-display {
                font-size: 1rem;
                padding: 2px 10px;
            }
            #score-display {
                font-size: 1.1rem;
                padding: 2px 12px;
            }
        }
    </style>
</head>
<body>

    <div id="game-container">
        <!-- 选择界面 -->
        <div id="select-screen">
            <h1>🗑️ 垃圾小卫士</h1>
            <div class="subtitle">⏱️ 60秒 · 双垃圾挑战</div>
            <p>👋 选择你的垃圾桶类别</p>
            <div class="select-bins">
                <div class="select-bin bin-recyclable" data-cat="recyclable" onclick="window.startGame('recyclable')">
                    <span class="bin-emoji">♻️</span>
                    <span class="bin-label">可回收</span>
                </div>
                <div class="select-bin bin-hazardous" data-cat="hazardous" onclick="window.startGame('hazardous')">
                    <span class="bin-emoji">☣️</span>
                    <span class="bin-label">有害</span>
                </div>
                <div class="select-bin bin-kitchen" data-cat="kitchen" onclick="window.startGame('kitchen')">
                    <span class="bin-emoji">🍂</span>
                    <span class="bin-label">厨余</span>
                </div>
                <div class="select-bin bin-other" data-cat="other" onclick="window.startGame('other')">
                    <span class="bin-emoji">🗑️</span>
                    <span class="bin-label">其他</span>
                </div>
            </div>
            <div class="high-score-display" id="select-high-score">🏆 最高分:0</div>
            <div class="select-hint">🎮 左摇杆漫步 &nbsp;|&nbsp; 🍽️ 吃 / 💨 吐</div>
        </div>

        <!-- 游戏界面 -->
        <div id="game-screen">
            <!-- 顶部状态 -->
            <div id="top-bar">
                <div id="timer-display">⏱️ 60</div>
                <div id="score-display">🏆 <span id="score-num">0</span></div>
                <div id="category-display">♻️ 可回收</div>
            </div>

            <!-- 地图 -->
            <div id="map-container">
                <div id="player-bin">
                    <div class="bin-body">
                        <div class="lid"></div>
                        <div class="bucket">
                            <span class="face" id="bin-face">😊</span>
                            <span class="cat-icon" id="cat-icon">♻️</span>
                            <div class="wheel left"></div>
                            <div class="wheel right"></div>
                        </div>
                    </div>
                    <div class="mouth-icon" id="bin-mouth">😮</div>
                    <div class="stars" id="bin-stars">⭐✨</div>
                </div>
                <div id="interact-hint">💡 靠近垃圾,按 🍽️吃 或 💨吐</div>
                <div id="feedback">👍 干得好!</div>
            </div>

            <!-- 控制面板 -->
            <div id="control-panel">
                <div id="joystick-area">
                    <div id="joystick-base">
                        <span class="joystick-arrow up">▲</span>
                        <span class="joystick-arrow down">▼</span>
                        <span class="joystick-arrow left">◀</span>
                        <span class="joystick-arrow right">▶</span>
                        <div id="joystick-thumb"></div>
                    </div>
                </div>
                <div id="knob-area">
                    <button class="knob-btn eat-btn" id="btn-eat">
                        <span class="knob-dots">
                            <span style="top:5px;left:20%;"></span>
                            <span style="top:5px;right:20%;"></span>
                            <span style="bottom:5px;left:20%;"></span>
                            <span style="bottom:5px;right:20%;"></span>
                        </span>
                        <span class="knob-icon">🍽️</span>
                        <span class="knob-label">吃</span>
                    </button>
                    <button class="knob-btn spit-btn" id="btn-spit">
                        <span class="knob-dots">
                            <span style="top:5px;left:20%;"></span>
                            <span style="top:5px;right:20%;"></span>
                            <span style="bottom:5px;left:20%;"></span>
                            <span style="bottom:5px;right:20%;"></span>
                        </span>
                        <span class="knob-icon">💨</span>
                        <span class="knob-label">吐</span>
                    </button>
                </div>
            </div>

            <div id="bottom-extra">
                <button class="extra-btn back" id="back-btn">🏠 返回</button>
                <button class="extra-btn" id="pause-btn">⏸️ 暂停</button>
                <button class="extra-btn reset" id="reset-btn">🔄 重来</button>
            </div>
        </div>

        <!-- 游戏结束覆盖层 -->
        <div id="game-over-overlay">
            <h1>⏰ 时间到!</h1>
            <div class="final-score" id="final-score-text">0 分</div>
            <div class="high-score">🏆 历史最高分:<span id="final-high-score">0</span></div>
            <div class="btn-group">
                <button id="restart-same">🔄 再来一局</button>
                <button class="back-btn" id="gameover-back">🏠 返回</button>
            </div>
        </div>

        <!-- 答题覆盖层 -->
        <div id="quiz-overlay">
            <div id="quiz-box" class="fade-in">
                <div id="quiz-question">❓ 吃剩的苹果核属于什么垃圾?</div>
                <div id="quiz-options">
                    <button class="quiz-option" data-index="0">可回收</button>
                    <button class="quiz-option" data-index="1">有害</button>
                    <button class="quiz-option" data-index="2">厨余</button>
                    <button class="quiz-option" data-index="3">其他</button>
                </div>
                <div id="quiz-result">💡 选一个答案吧!</div>
                <button id="quiz-continue" class="hidden">👉 继续</button>
            </div>
        </div>
    </div>

    <script>
        (function() {
            'use strict';

            // ============================================================
            // 1. 普通垃圾数据(30种)
            // ============================================================
            const GARBAGE = [
                { emoji: '🥤', name: '塑料瓶', cat: 'recyclable' },
                { emoji: '📰', name: '旧报纸', cat: 'recyclable' },
                { emoji: '🧃', name: '牛奶盒', cat: 'recyclable' },
                { emoji: '🧸', name: '旧布偶', cat: 'recyclable' },
                { emoji: '📦', name: '纸箱', cat: 'recyclable' },
                { emoji: '🥫', name: '易拉罐', cat: 'recyclable' },
                { emoji: '👕', name: '旧衣服', cat: 'recyclable' },
                { emoji: '🔩', name: '螺丝钉', cat: 'recyclable' },
                { emoji: '🔋', name: '电池', cat: 'hazardous' },
                { emoji: '💊', name: '过期药', cat: 'hazardous' },
                { emoji: '💡', name: '节能灯', cat: 'hazardous' },
                { emoji: '💄', name: '过期口红', cat: 'hazardous' },
                { emoji: '🖍️', name: '旧油画棒', cat: 'hazardous' },
                { emoji: '🪣', name: '油漆桶', cat: 'hazardous' },
                { emoji: '🌡️', name: '温度计', cat: 'hazardous' },
                { emoji: '🍎', name: '苹果核', cat: 'kitchen' },
                { emoji: '🌿', name: '枯树叶', cat: 'kitchen' },
                { emoji: '🍵', name: '茶叶渣', cat: 'kitchen' },
                { emoji: '🥕', name: '萝卜皮', cat: 'kitchen' },
                { emoji: '🍌', name: '香蕉皮', cat: 'kitchen' },
                { emoji: '🦐', name: '虾壳', cat: 'kitchen' },
                { emoji: '🥚', name: '蛋壳', cat: 'kitchen' },
                { emoji: '🌽', name: '玉米芯', cat: 'kitchen' },
                { emoji: '🦴', name: '大棒骨', cat: 'other' },
                { emoji: '🧻', name: '脏纸巾', cat: 'other' },
                { emoji: '🥡', name: '脏外卖盒', cat: 'other' },
                { emoji: '🧂', name: '碎陶瓷', cat: 'other' },
                { emoji: '💈', name: '头发', cat: 'other' },
                { emoji: '🪥', name: '旧牙刷', cat: 'other' },
                { emoji: '🧦', name: '破袜子', cat: 'other' },
                { emoji: '🩹', name: '创可贴', cat: 'other' },
            ];

            // ============================================================
            // 2. 高级题库(垃圾分类知识 + 延伸问题,难度高)
            // ============================================================
            const ADVANCED_QUIZ = [{
                question: '用过的纸杯(内壁有塑料膜)属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 3,
                explain: '纸杯内壁的塑料膜难以分离,属于其他垃圾。'
            }, {
                question: '破碎的陶瓷碗属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 3,
                explain: '陶瓷无法回收再利用,属于其他垃圾。'
            }, {
                question: '过期化妆品属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 1,
                explain: '化妆品含有化学物质,属于有害垃圾。'
            }, {
                question: '旧手机属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 0,
                explain: '旧手机含有贵金属,可以回收再利用。'
            }, {
                question: '大棒骨为什么属于其他垃圾而不是厨余垃圾?',
                options: ['太硬不易腐烂', '太大占地方', '没有营养', '容易污染环境'],
                answer: 0,
                explain: '大棒骨质地坚硬,不易腐烂分解,属于其他垃圾。'
            }, {
                question: '用过的创可贴属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 3,
                explain: '创可贴被污染后无法回收,属于其他垃圾。'
            }, {
                question: '节能灯管属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 1,
                explain: '节能灯管含有汞等有害物质,属于有害垃圾。'
            }, {
                question: '一次性塑料餐具(使用后)属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 3,
                explain: '使用后的一次性餐具被污染,属于其他垃圾。'
            }, {
                question: '废玻璃瓶属于什么垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 0,
                explain: '玻璃可以回收再利用,属于可回收垃圾。'
            }, {
                question: '下列哪种行为最有利于垃圾减量?',
                options: ['减少使用一次性物品', '多买包装精美的商品', '每天多扔垃圾', '把垃圾堆在一起'],
                answer: 0,
                explain: '减少使用一次性物品是垃圾减量的最有效方式。'
            }, {
                question: '回收1吨废纸大约可以拯救多少棵树?',
                options: ['5棵', '17棵', '50棵', '100棵'],
                answer: 1,
                explain: '回收1吨废纸约可拯救17棵大树,节约资源。'
            }, {
                question: '塑料瓶在自然环境中大约需要多少年才能分解?',
                options: ['50年', '200年', '500年', '1000年'],
                answer: 2,
                explain: '塑料瓶在自然环境中需要约500年才能分解。'
            }, {
                question: '我国城市生活垃圾中占比最大的是哪类垃圾?',
                options: ['可回收', '有害', '厨余', '其他'],
                answer: 2,
                explain: '厨余垃圾在我国城市生活垃圾中占比最大,约50%-60%。'
            }, {
                question: '过期药品应该如何处理?',
                options: ['扔进其他垃圾桶', '扔进有害垃圾桶', '冲进下水道', '送人使用'],
                answer: 1,
                explain: '过期药品属于有害垃圾,应投入有害垃圾桶。'
            }, {
                question: '以下哪种垃圾可以转化为有机肥料?',
                options: ['废旧电池', '厨余垃圾', '旧衣服', '碎玻璃'],
                answer: 1,
                explain: '厨余垃圾可以通过堆肥转化为有机肥料。'
            }];

            // ---------- DOM ----------
            const selectScreen = document.getElementById('select-screen');
            const gameScreen = document.getElementById('game-screen');
            const mapContainer = document.getElementById('map-container');
            const feedback = document.getElementById('feedback');
            const scoreNum = document.getElementById('score-num');
            const timerDisplay = document.getElementById('timer-display');
            const scoreDisplay = document.getElementById('score-display');
            const categoryDisplay = document.getElementById('category-display');
            const playerBin = document.getElementById('player-bin');
            const binFace = document.getElementById('bin-face');
            const catIcon = document.getElementById('cat-icon');
            const binMouth = document.getElementById('bin-mouth');
            const binStars = document.getElementById('bin-stars');
            const interactHint = document.getElementById('interact-hint');
            const finalScoreText = document.getElementById('final-score-text');
            const finalHighScore = document.getElementById('final-high-score');
            const selectHighScore = document.getElementById('select-high-score');

            const quizOverlay = document.getElementById('quiz-overlay');
            const quizQuestion = document.getElementById('quiz-question');
            const quizOptions = document.getElementById('quiz-options');
            const quizResult = document.getElementById('quiz-result');
            const quizContinue = document.getElementById('quiz-continue');

            const gameOverOverlay = document.getElementById('game-over-overlay');
            const restartSame = document.getElementById('restart-same');
            const gameoverBack = document.getElementById('gameover-back');
            const backBtn = document.getElementById('back-btn');
            const pauseBtn = document.getElementById('pause-btn');
            const resetBtn = document.getElementById('reset-btn');
            const btnEat = document.getElementById('btn-eat');
            const btnSpit = document.getElementById('btn-spit');

            const joystickBase = document.getElementById('joystick-base');
            const joystickThumb = document.getElementById('joystick-thumb');

            // ---------- 状态 ----------
            let selectedCat = '';
            let score = 0;
            let timeLeft = 60;
            const maxTime = 60;
            let isPaused = false;
            let isGameOver = false;
            let isQuizActive = false;
            let isProcessing = false;
            let garbageList = [];
            let animationFrame = null;
            let timerInterval = null;
            let playerX = 30;
            let playerY = 30;
            const moveSpeed = 0.8;
            let joystickActive = false;
            let joystickDx = 0;
            let joystickDy = 0;
            let highScore = parseInt(localStorage.getItem('garbageEatHighScore')) || 0;
            let currentCategory = '';

            let recentAdvancedIndices = [];
            const MAX_RECENT = 3;

            // ---------- 音效(Web Audio,简单短促) ----------
            let audioCtx = null;

            function ensureAudio() {
                if (!audioCtx) {
                    audioCtx = new(window.AudioContext || window.webkitAudioContext)();
                }
                // 如果处于 suspended 状态,尝试恢复
                if (audioCtx.state === 'suspended') {
                    audioCtx.resume();
                }
                return audioCtx;
            }

            // 播放正确音效(短促上升音,清脆)
            function playCorrectSound() {
                try {
                    const ctx = ensureAudio();
                    const osc = ctx.createOscillator();
                    const gain = ctx.createGain();
                    osc.connect(gain);
                    gain.connect(ctx.destination);
                    osc.type = 'sine';
                    osc.frequency.setValueAtTime(880, ctx.currentTime);
                    osc.frequency.exponentialRampToValueAtTime(1320, ctx.currentTime + 0.08);
                    gain.gain.setValueAtTime(0.3, ctx.currentTime);
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.12);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.12);
                    // 加一点泛音
                    const osc2 = ctx.createOscillator();
                    const gain2 = ctx.createGain();
                    osc2.connect(gain2);
                    gain2.connect(ctx.destination);
                    osc2.type = 'sine';
                    osc2.frequency.setValueAtTime(1320, ctx.currentTime);
                    osc2.frequency.exponentialRampToValueAtTime(1760, ctx.currentTime + 0.06);
                    gain2.gain.setValueAtTime(0.15, ctx.currentTime);
                    gain2.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.1);
                    osc2.start(ctx.currentTime + 0.02);
                    osc2.stop(ctx.currentTime + 0.1);
                } catch (e) { /* 静默降级 */ }
            }

            // 播放错误音效(短促下降音,低沉)
            function playWrongSound() {
                try {
                    const ctx = ensureAudio();
                    const osc = ctx.createOscillator();
                    const gain = ctx.createGain();
                    osc.connect(gain);
                    gain.connect(ctx.destination);
                    osc.type = 'sawtooth';
                    osc.frequency.setValueAtTime(300, ctx.currentTime);
                    osc.frequency.exponentialRampToValueAtTime(120, ctx.currentTime + 0.12);
                    gain.gain.setValueAtTime(0.2, ctx.currentTime);
                    gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.14);
                    osc.start(ctx.currentTime);
                    osc.stop(ctx.currentTime + 0.14);
                    // 加一个低音短促
                    const osc2 = ctx.createOscillator();
                    const gain2 = ctx.createGain();
                    osc2.connect(gain2);
                    gain2.connect(ctx.destination);
                    osc2.type = 'square';
                    osc2.frequency.setValueAtTime(150, ctx.currentTime);
                    osc2.frequency.exponentialRampToValueAtTime(80, ctx.currentTime + 0.1);
                    gain2.gain.setValueAtTime(0.1, ctx.currentTime);
                    gain2.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.1);
                    osc2.start(ctx.currentTime + 0.02);
                    osc2.stop(ctx.currentTime + 0.1);
                } catch (e) { /* 静默降级 */ }
            }

            // ---------- 工具 ----------
            function getCategoryName(cat) {
                const map = { 'recyclable': '可回收', 'hazardous': '有害', 'kitchen': '厨余', 'other': '其他' };
                return map[cat] || '未知';
            }

            function getCategoryEmoji(cat) {
                const map = { 'recyclable': '♻️', 'hazardous': '☣️', 'kitchen': '🍂', 'other': '🗑️' };
                return map[cat] || '🗑️';
            }

            function getRandomGarbage() { return GARBAGE[Math.floor(Math.random() * GARBAGE.length)]; }

            function updateScoreUI() {
                scoreNum.textContent = score;
                if (score < 0) {
                    scoreDisplay.classList.add('negative');
                } else {
                    scoreDisplay.classList.remove('negative');
                }
            }

            function updateTimerUI() {
                timerDisplay.textContent = '⏱️ ' + Math.ceil(timeLeft);
                if (timeLeft <= 10) {
                    timerDisplay.classList.add('warning');
                } else {
                    timerDisplay.classList.remove('warning');
                }
            }

            function updateHighScoreDisplay() {
                selectHighScore.textContent = '🏆 最高分:' + highScore;
            }

            function showFeedback(emoji, text, isCorrect) {
                feedback.textContent = `${emoji} ${text}`;
                feedback.className = 'show';
                feedback.style.background = isCorrect ? 'rgba(46, 204, 113, 0.85)' : 'rgba(231, 76, 60, 0.85)';
                clearTimeout(feedback._hideTimer);
                feedback._hideTimer = setTimeout(() => { feedback.className = ''; }, 800);
                // 播放音效
                if (isCorrect) {
                    playCorrectSound();
                } else {
                    playWrongSound();
                }
            }

            function setFace(emoji) { binFace.textContent = emoji; }

            function setHappy() { setFace('😊'); }

            function setEating() { setFace('😮'); }

            function setKicking() { setFace('😤'); }

            // ---------- 判断是否为特殊垃圾 ----------
            function isSpecialGarbage(el) {
                return el && el.dataset && el.dataset.special === 'true';
            }

            // ---------- 生成垃圾 ----------
            function ensureTwoGarbage() {
                if (isPaused || isGameOver || isQuizActive) return;
                let normalCount = 0,
                    specialCount = 0;
                garbageList.forEach(el => {
                    if (isSpecialGarbage(el)) specialCount++;
                    else normalCount++;
                });
                if (normalCount < 1) {
                    spawnSingleGarbage(false);
                }
                if (specialCount < 1) {
                    spawnSingleGarbage(true);
                }
            }

            function spawnSingleGarbage(isSpecial) {
                if (isPaused || isGameOver || isQuizActive) return;

                let el;
                if (isSpecial) {
                    el = document.createElement('div');
                    el.className = 'garbage-item special';
                    el.textContent = '❓';
                    el.dataset.cat = 'special';
                    el.dataset.name = '未知垃圾';
                    el.dataset.special = 'true';
                    const tag = document.createElement('span');
                    tag.className = 'special-tag';
                    tag.textContent = '❓ 特殊';
                    el.appendChild(tag);
                } else {
                    const garbage = getRandomGarbage();
                    el = document.createElement('div');
                    el.className = 'garbage-item';
                    el.textContent = garbage.emoji;
                    el.dataset.cat = garbage.cat;
                    el.dataset.name = garbage.name;
                    el.dataset.special = 'false';
                }

                let x, y, attempts = 0;
                let overlap = true;
                const margin = 8;
                while (overlap && attempts < 30) {
                    x = margin + Math.random() * (100 - 2 * margin);
                    y = margin + Math.random() * (100 - 2 * margin);
                    overlap = false;
                    for (let g of garbageList) {
                        const gx = parseFloat(g.style.left);
                        const gy = parseFloat(g.style.top);
                        if (Math.hypot(x - gx, y - gy) < 15) {
                            overlap = true;
                            break;
                        }
                    }
                    if (Math.hypot(x - playerX, y - playerY) < 20) overlap = true;
                    attempts++;
                }
                el.style.left = x + '%';
                el.style.top = y + '%';

                mapContainer.appendChild(el);
                garbageList.push(el);
                updateInteractHint();
            }

            // ---------- 更新交互提示 ----------
            function updateInteractHint() {
                if (garbageList.length === 0) {
                    interactHint.classList.remove('show', 'special-hint');
                    return;
                }
                let nearest = null;
                let minDist = Infinity;
                const binRect = playerBin.getBoundingClientRect();
                const binCX = binRect.left + binRect.width / 2;
                const binCY = binRect.top + binRect.height / 2;
                for (let el of garbageList) {
                    const elRect = el.getBoundingClientRect();
                    const cx = elRect.left + elRect.width / 2;
                    const cy = elRect.top + elRect.height / 2;
                    const dist = Math.hypot(cx - binCX, cy - binCY);
                    if (dist < minDist) {
                        minDist = dist;
                        nearest = el;
                    }
                }
                const mapRect = mapContainer.getBoundingClientRect();
                const threshold = Math.min(mapRect.width, mapRect.height) * 0.13;
                if (nearest && minDist < threshold && !isProcessing) {
                    const isSpecial = isSpecialGarbage(nearest);
                    if (isSpecial) {
                        interactHint.textContent = '❓ 特殊垃圾,按"吃"挑战答题!(+30/-20)';
                        interactHint.classList.add('show', 'special-hint');
                    } else {
                        const catName = getCategoryName(nearest.dataset.cat);
                        interactHint.textContent = `💡 ${nearest.dataset.name}(${catName}),按 🍽️吃 或 💨吐`;
                        interactHint.classList.add('show');
                        interactHint.classList.remove('special-hint');
                    }
                } else {
                    interactHint.classList.remove('show', 'special-hint');
                }
            }

            // ---------- 吸入效果 ----------
            function suckGarbageToBin(el) {
                const binRect = playerBin.getBoundingClientRect();
                const elRect = el.getBoundingClientRect();
                const mapRect = mapContainer.getBoundingClientRect();

                const startX = (elRect.left + elRect.width / 2 - mapRect.left) / mapRect.width * 100;
                const startY = (elRect.top + elRect.height / 2 - mapRect.top) / mapRect.height * 100;
                const targetX = (binRect.left + binRect.width / 2 - mapRect.left) / mapRect.width * 100;
                const targetY = (binRect.top + binRect.height / 2 - mapRect.top) / mapRect.height * 100;

                el.style.transition =
                    'left 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), top 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.4s ease, opacity 0.3s ease';
                el.style.left = targetX + '%';
                el.style.top = targetY + '%';
                el.style.transform = 'scale(0.2) rotate(720deg)';
                el.style.opacity = '0.8';
                el.classList.add('sucked');

                const onFinish = () => {
                    el.removeEventListener('transitionend', onFinish);
                    if (el.parentNode) el.parentNode.removeChild(el);
                    const idx = garbageList.indexOf(el);
                    if (idx !== -1) garbageList.splice(idx, 1);
                    updateInteractHint();
                    ensureTwoGarbage();
                };
                el.addEventListener('transitionend', onFinish);
                setTimeout(() => {
                    if (el.parentNode) {
                        el.removeEventListener('transitionend', onFinish);
                        el.parentNode.removeChild(el);
                        const idx = garbageList.indexOf(el);
                        if (idx !== -1) garbageList.splice(idx, 1);
                        updateInteractHint();
                        ensureTwoGarbage();
                    }
                }, 600);
            }

            // ---------- 处理分类 ----------
            function handleClassification(action) {
                if (isPaused || isGameOver || isQuizActive || isProcessing) return;
                if (garbageList.length === 0) {
                    showFeedback('🤔', '没有垃圾呀', false);
                    return;
                }

                let nearest = null;
                let minDist = Infinity;
                const binRect = playerBin.getBoundingClientRect();
                const binCX = binRect.left + binRect.width / 2;
                const binCY = binRect.top + binRect.height / 2;
                for (let el of garbageList) {
                    const elRect = el.getBoundingClientRect();
                    const cx = elRect.left + elRect.width / 2;
                    const cy = elRect.top + elRect.height / 2;
                    const dist = Math.hypot(cx - binCX, cy - binCY);
                    if (dist < minDist) {
                        minDist = dist;
                        nearest = el;
                    }
                }

                if (!nearest) {
                    showFeedback('🤔', '没有垃圾呀', false);
                    return;
                }

                const mapRect = mapContainer.getBoundingClientRect();
                const threshold = Math.min(mapRect.width, mapRect.height) * 0.13;
                if (minDist > threshold) {
                    showFeedback('🚶', '靠近一点再操作', false);
                    return;
                }

                isProcessing = true;
                const garbageEl = nearest;
                const isSpecial = isSpecialGarbage(garbageEl);

                if (isSpecial) {
                    if (action === true) {
                        setEating();
                        binMouth.classList.add('show');
                        binStars.classList.add('show');
                        playerBin.classList.add('eating');
                        suckGarbageToBin(garbageEl);
                        showFeedback('❓', '挑战高级答题!', true);
                        setTimeout(() => {
                            binMouth.classList.remove('show');
                            binStars.classList.remove('show');
                            playerBin.classList.remove('eating');
                            setHappy();
                        }, 600);
                        setTimeout(() => {
                            isProcessing = false;
                            triggerAdvancedQuiz();
                        }, 700);
                    } else {
                        setKicking();
                        playerBin.classList.add('kicking');
                        garbageEl.classList.add('flying');
                        setTimeout(() => {
                            if (garbageEl.parentNode) garbageEl.parentNode.removeChild(garbageEl);
                            const idx = garbageList.indexOf(garbageEl);
                            if (idx !== -1) garbageList.splice(idx, 1);
                            playerBin.classList.remove('kicking');
                            setHappy();
                            isProcessing = false;
                            updateInteractHint();
                            ensureTwoGarbage();
                        }, 800);
                        showFeedback('💨', '吐掉了,继续吧', false);
                    }
                    return;
                }

                // 普通垃圾
                const cat = garbageEl.dataset.cat;
                const name = garbageEl.dataset.name;
                const actualIsCorrect = (cat === selectedCat);
                const playerCorrect = (action === actualIsCorrect);

                if (playerCorrect) {
                    if (actualIsCorrect) {
                        setEating();
                        binMouth.classList.add('show');
                        binStars.classList.add('show');
                        playerBin.classList.add('eating');
                        suckGarbageToBin(garbageEl);
                        score += 10;
                        updateScoreUI();
                        showFeedback('🎉', `正确!+10分`, true);
                        setTimeout(() => {
                            binMouth.classList.remove('show');
                            binStars.classList.remove('show');
                            playerBin.classList.remove('eating');
                            setHappy();
                        }, 600);
                    } else {
                        setKicking();
                        playerBin.classList.add('kicking');
                        garbageEl.classList.add('flying');
                        setTimeout(() => {
                            if (garbageEl.parentNode) garbageEl.parentNode.removeChild(garbageEl);
                            const idx = garbageList.indexOf(garbageEl);
                            if (idx !== -1) garbageList.splice(idx, 1);
                            playerBin.classList.remove('kicking');
                            setHappy();
                            updateInteractHint();
                            ensureTwoGarbage();
                        }, 800);
                        score += 5;
                        updateScoreUI();
                        showFeedback('👍', `判断正确!+5分`, true);
                        setTimeout(() => setHappy(), 500);
                    }
                } else {
                    if (actualIsCorrect) {
                        setKicking();
                        playerBin.classList.add('kicking');
                        garbageEl.classList.add('flying');
                        setTimeout(() => {
                            if (garbageEl.parentNode) garbageEl.parentNode.removeChild(garbageEl);
                            const idx = garbageList.indexOf(garbageEl);
                            if (idx !== -1) garbageList.splice(idx, 1);
                            playerBin.classList.remove('kicking');
                            setHappy();
                            updateInteractHint();
                            ensureTwoGarbage();
                        }, 800);
                    } else {
                        setEating();
                        binMouth.classList.add('show');
                        playerBin.classList.add('eating');
                        suckGarbageToBin(garbageEl);
                        setTimeout(() => {
                            binMouth.classList.remove('show');
                            playerBin.classList.remove('eating');
                            setHappy();
                        }, 600);
                    }
                    score -= 5;
                    updateScoreUI();
                    showFeedback('💥', `哎呀,扣5分`, false);
                }

                if (score < -20) {
                    setTimeout(() => {
                        triggerQuiz();
                    }, 700);
                }

                const cleanUp = () => {
                    if (!garbageEl.parentNode) {
                        isProcessing = false;
                        updateInteractHint();
                        ensureTwoGarbage();
                    } else {
                        setTimeout(cleanUp, 200);
                    }
                };
                setTimeout(cleanUp, 800);
            }

            // ---------- 触发高级答题(无语音) ----------
            function triggerAdvancedQuiz() {
                if (isQuizActive) return;
                isQuizActive = true;

                let availableIndices = [];
                for (let i = 0; i < ADVANCED_QUIZ.length; i++) {
                    if (!recentAdvancedIndices.includes(i)) {
                        availableIndices.push(i);
                    }
                }
                if (availableIndices.length === 0) {
                    recentAdvancedIndices = [];
                    availableIndices = ADVANCED_QUIZ.map((_, i) => i);
                }

                const randomIdx = availableIndices[Math.floor(Math.random() * availableIndices.length)];
                recentAdvancedIndices.push(randomIdx);
                if (recentAdvancedIndices.length > MAX_RECENT) {
                    recentAdvancedIndices.shift();
                }

                const q = ADVANCED_QUIZ[randomIdx];
                quizQuestion.textContent = '🧠 ' + q.question;
                const optionBtns = quizOptions.querySelectorAll('.quiz-option');
                optionBtns.forEach((btn, idx) => {
                    btn.textContent = q.options[idx];
                    btn.dataset.index = idx;
                    btn.className = 'quiz-option';
                    btn.disabled = false;
                    btn.style.pointerEvents = 'auto';
                });
                quizResult.textContent = '💡 选一个答案吧!';
                quizContinue.classList.add('hidden');
                quizOverlay.style.display = 'flex';
                quizOverlay.dataset.answer = q.answer;
                quizOverlay.dataset.explain = q.explain;
                quizOverlay.dataset.answered = 'false';
                quizOverlay.dataset.isAdvanced = 'true';
            }

            // ---------- 触发普通答题 ----------
            function triggerQuiz() {
                if (isQuizActive) return;
                isQuizActive = true;
                garbageList.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); });
                garbageList = [];
                updateInteractHint();

                const q = ADVANCED_QUIZ[Math.floor(Math.random() * ADVANCED_QUIZ.length)];
                quizQuestion.textContent = '❓ ' + q.question;
                const optionBtns = quizOptions.querySelectorAll('.quiz-option');
                optionBtns.forEach((btn, idx) => {
                    btn.textContent = q.options[idx];
                    btn.dataset.index = idx;
                    btn.className = 'quiz-option';
                    btn.disabled = false;
                    btn.style.pointerEvents = 'auto';
                });
                quizResult.textContent = '💡 选一个答案吧!';
                quizContinue.classList.add('hidden');
                quizOverlay.style.display = 'flex';
                quizOverlay.dataset.answer = q.answer;
                quizOverlay.dataset.explain = q.explain;
                quizOverlay.dataset.answered = 'false';
                quizOverlay.dataset.isAdvanced = 'false';
            }

            // ---------- 答题选项点击 ----------
            function handleQuizOptionClick(e) {
                const btn = e.currentTarget;
                if (btn.disabled) return;
                const overlay = quizOverlay;
                if (overlay.dataset.answered === 'true') return;
                const selectedIndex = parseInt(btn.dataset.index);
                const correctIndex = parseInt(overlay.dataset.answer);
                const explain = overlay.dataset.explain;
                const isCorrect = (selectedIndex === correctIndex);
                const allBtns = quizOptions.querySelectorAll('.quiz-option');
                allBtns.forEach(b => { b.disabled = true;
                    b.style.pointerEvents = 'none'; });
                allBtns.forEach((b, idx) => {
                    if (idx === correctIndex) b.classList.add('correct');
                    else if (idx === selectedIndex && !isCorrect) b.classList.add('wrong');
                });

                const isAdvanced = overlay.dataset.isAdvanced === 'true';

                if (isCorrect) {
                    let msg = '✅ 答对了!' + explain;
                    if (isAdvanced) {
                        score += 30;
                        msg = `🌟 答对了!+30分!` + explain;
                    } else {
                        score += 15;
                        msg = '✅ 答对了!+15分!' + explain;
                    }
                    quizResult.textContent = msg;
                    updateScoreUI();
                    playCorrectSound();
                } else {
                    let msg = '❌ 正确答案是:' + allBtns[correctIndex].textContent + '。' + explain;
                    if (isAdvanced) {
                        score -= 20;
                        msg = `❌ 答错了,扣20分。正确答案是:` + allBtns[correctIndex].textContent + '。' + explain;
                    } else {
                        score -= 5;
                        msg = '❌ 答错了,扣5分。正确答案是:' + allBtns[correctIndex].textContent + '。' + explain;
                    }
                    quizResult.textContent = msg;
                    updateScoreUI();
                    playWrongSound();
                }

                overlay.dataset.answered = 'true';
                overlay.dataset.quizResult = isCorrect ? 'pass' : 'fail';
                quizContinue.classList.remove('hidden');
                quizContinue.textContent = isCorrect ? '👉 继续!' : '😢 继续';
            }

            function handleQuizContinue() {
                const overlay = quizOverlay;
                const result = overlay.dataset.quizResult || 'pass';
                overlay.style.display = 'none';
                isQuizActive = false;

                if (score < -20 && !isGameOver) {
                    setTimeout(() => { triggerQuiz(); }, 500);
                    return;
                }

                setHappy();
                if (!isGameOver) {
                    garbageList.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); });
                    garbageList = [];
                    updateInteractHint();
                    setTimeout(() => {
                        ensureTwoGarbage();
                    }, 400);
                }
            }

            // ---------- 地图装饰 ----------
            function buildMap() {
                const toRemove = [];
                mapContainer.querySelectorAll('.road-h, .road-v, .tree, .flower, .house').forEach(el => toRemove.push(el));
                toRemove.forEach(el => el.remove());

                const roads = [
                    { type: 'h', top: 28, left: 0, width: 100 },
                    { type: 'h', top: 58, left: 0, width: 100 },
                    { type: 'v', top: 0, left: 28, height: 100 },
                    { type: 'v', top: 0, left: 58, height: 100 },
                ];
                roads.forEach(r => {
                    const div = document.createElement('div');
                    if (r.type === 'h') {
                        div.className = 'road-h';
                        div.style.top = r.top + '%';
                        div.style.left = r.left + '%';
                        div.style.width = r.width + '%';
                    } else {
                        div.className = 'road-v';
                        div.style.top = r.top + '%';
                        div.style.left = r.left + '%';
                        div.style.height = r.height + '%';
                    }
                    mapContainer.appendChild(div);
                });

                const trees = [
                    [6, 6],
                    [88, 4],
                    [5, 44],
                    [94, 40],
                    [12, 74],
                    [84, 80],
                    [44, 10],
                    [56, 6],
                    [42, 86],
                    [64, 84],
                    [8, 92],
                    [90, 90]
                ];
                trees.forEach(([x, y]) => {
                    const el = document.createElement('div');
                    el.className = 'tree';
                    el.textContent = ['🌳', '🌲', '🌴'][Math.floor(Math.random() * 3)];
                    el.style.left = x + '%';
                    el.style.top = y + '%';
                    el.style.fontSize = (1.8 + Math.random() * 1.0) + 'rem';
                    mapContainer.appendChild(el);
                });

                for (let i = 0; i < 14; i++) {
                    const x = 5 + Math.random() * 90;
                    const y = 5 + Math.random() * 90;
                    const onRoad = (Math.abs(x - 28) < 14 || Math.abs(x - 58) < 14 || Math.abs(y - 28) < 14 || Math.abs(y -
                        58) < 14);
                    if (onRoad) continue;
                    const el = document.createElement('div');
                    el.className = 'flower';
                    el.textContent = ['🌸', '🌺', '🌻', '🌷', '🌼'][Math.floor(Math.random() * 5)];
                    el.style.left = x + '%';
                    el.style.top = y + '%';
                    el.style.fontSize = (1.0 + Math.random() * 0.8) + 'rem';
                    mapContainer.appendChild(el);
                }

                const houses = [
                    [3, 3],
                    [90, 3],
                    [3, 90],
                    [90, 90]
                ];
                houses.forEach(([x, y]) => {
                    const el = document.createElement('div');
                    el.className = 'house';
                    el.textContent = ['🏠', '🏡', '🏘️'][Math.floor(Math.random() * 3)];
                    el.style.left = x + '%';
                    el.style.top = y + '%';
                    el.style.fontSize = (2.2 + Math.random() * 0.6) + 'rem';
                    mapContainer.appendChild(el);
                });
            }

            // ---------- 主循环 ----------
            function gameLoop() {
                if (!isPaused && !isGameOver && !isQuizActive) {
                    let dx = 0,
                        dy = 0;
                    if (joystickActive) {
                        const magnitude = Math.hypot(joystickDx, joystickDy);
                        if (magnitude > 0.15) {
                            const speed = moveSpeed * Math.min(magnitude, 1);
                            dx = (joystickDx / magnitude) * speed;
                            dy = (joystickDy / magnitude) * speed;
                        }
                    }

                    const wasWalking = (dx !== 0 || dy !== 0);

                    if (wasWalking) {
                        let newX = playerX + dx;
                        let newY = playerY + dy;
                        const mapRect = mapContainer.getBoundingClientRect();
                        const binW = playerBin.offsetWidth || 60;
                        const binH = playerBin.offsetHeight || 68;
                        const maxX = 100 - (binW / mapRect.width) * 100;
                        const maxY = 100 - (binH / mapRect.height) * 100;
                        newX = Math.max(0, Math.min(maxX, newX));
                        newY = Math.max(0, Math.min(maxY, newY));
                        playerX = newX;
                        playerY = newY;
                        playerBin.style.left = playerX + '%';
                        playerBin.style.top = playerY + '%';
                    }

                    if (wasWalking && !isProcessing) {
                        playerBin.classList.add('walking');
                    } else {
                        playerBin.classList.remove('walking');
                    }

                    updateInteractHint();
                    ensureTwoGarbage();
                }
                animationFrame = requestAnimationFrame(gameLoop);
            }

            // ---------- 摇杆 ----------
            function setupJoystick() {
                let pointerId = null;

                function getPos(e) {
                    const rect = joystickBase.getBoundingClientRect();
                    const cx = rect.left + rect.width / 2;
                    const cy = rect.top + rect.height / 2;
                    const radius = rect.width / 2;
                    let dx = (e.clientX - cx) / radius;
                    let dy = (e.clientY - cy) / radius;
                    const mag = Math.hypot(dx, dy);
                    if (mag > 1) {
                        dx /= mag;
                        dy /= mag;
                    }
                    return { dx, dy };
                }

                function updateThumb(dx, dy) {
                    const radius = joystickBase.offsetWidth / 2;
                    const thumbRadius = joystickThumb.offsetWidth / 2;
                    const maxDist = radius - thumbRadius - 2;
                    const mag = Math.hypot(dx, dy);
                    const clampedMag = Math.min(mag, 1);
                    const moveX = dx / (mag || 1) * clampedMag * maxDist;
                    const moveY = dy / (mag || 1) * clampedMag * maxDist;
                    joystickThumb.style.transform = `translate(calc(-50% + ${moveX}px), calc(-50% + ${moveY}px))`;
                }

                function onPointerDown(e) {
                    e.preventDefault();
                    if (pointerId !== null) return;
                    pointerId = e.pointerId;
                    joystickBase.setPointerCapture(pointerId);
                    joystickBase.classList.add('active');
                    const pos = getPos(e);
                    joystickDx = pos.dx;
                    joystickDy = pos.dy;
                    joystickActive = true;
                    updateThumb(joystickDx, joystickDy);
                }

                function onPointerMove(e) {
                    e.preventDefault();
                    if (pointerId === null) return;
                    const pos = getPos(e);
                    joystickDx = pos.dx;
                    joystickDy = pos.dy;
                    joystickActive = true;
                    updateThumb(joystickDx, joystickDy);
                }

                function onPointerUp(e) {
                    e.preventDefault();
                    if (pointerId === null) return;
                    pointerId = null;
                    joystickBase.classList.remove('active');
                    joystickDx = 0;
                    joystickDy = 0;
                    joystickActive = false;
                    joystickThumb.style.transform = 'translate(-50%, -50%)';
                }

                joystickBase.addEventListener('pointerdown', onPointerDown);
                document.addEventListener('pointermove', onPointerMove);
                document.addEventListener('pointerup', onPointerUp);
                document.addEventListener('pointercancel', onPointerUp);
            }

            // ---------- 计时器 ----------
            function startTimer() {
                if (timerInterval) clearInterval(timerInterval);
                timerInterval = setInterval(() => {
                    if (isPaused || isQuizActive) return;
                    timeLeft -= 0.1;
                    if (timeLeft < 0) timeLeft = 0;
                    updateTimerUI();
                    if (timeLeft <= 0) {
                        clearInterval(timerInterval);
                        timerInterval = null;
                        endGame();
                    }
                }, 100);
            }

            // ---------- 结束游戏 ----------
            function endGame() {
                if (isGameOver) return;
                isGameOver = true;
                if (timerInterval) {
                    clearInterval(timerInterval);
                    timerInterval = null;
                }
                if (animationFrame) {
                    cancelAnimationFrame(animationFrame);
                    animationFrame = null;
                }
                garbageList.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); });
                garbageList = [];
                playerBin.classList.remove('walking');
                interactHint.classList.remove('show', 'special-hint');

                if (score > highScore) {
                    highScore = score;
                    localStorage.setItem('garbageEatHighScore', String(highScore));
                }
                updateHighScoreDisplay();

                finalScoreText.textContent = score + ' 分';
                finalHighScore.textContent = highScore;
                gameOverOverlay.style.display = 'flex';
            }

            // ---------- 返回选择界面 ----------
            function goToSelect() {
                if (timerInterval) {
                    clearInterval(timerInterval);
                    timerInterval = null;
                }
                if (animationFrame) {
                    cancelAnimationFrame(animationFrame);
                    animationFrame = null;
                }
                garbageList.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); });
                garbageList = [];
                isGameOver = true;
                gameOverOverlay.style.display = 'none';
                quizOverlay.style.display = 'none';
                gameScreen.style.display = 'none';
                selectScreen.style.display = 'flex';
                updateHighScoreDisplay();
                interactHint.classList.remove('show', 'special-hint');
                recentAdvancedIndices = [];
            }

            function restartSameGame() {
                gameOverOverlay.style.display = 'none';
                recentAdvancedIndices = [];
                if (currentCategory) {
                    startGame(currentCategory);
                } else {
                    goToSelect();
                }
            }

            // ---------- 启动游戏 ----------
            function startGame(cat) {
                currentCategory = cat;
                selectedCat = cat;
                const emoji = getCategoryEmoji(cat);
                const name = getCategoryName(cat);
                catIcon.textContent = emoji;
                categoryDisplay.textContent = emoji + ' ' + name;

                score = 0;
                timeLeft = maxTime;
                isGameOver = false;
                isPaused = false;
                isQuizActive = false;
                isProcessing = false;
                playerX = 30;
                playerY = 30;
                playerBin.style.left = playerX + '%';
                playerBin.style.top = playerY + '%';
                updateScoreUI();
                updateTimerUI();
                garbageList.forEach(el => { if (el.parentNode) el.parentNode.removeChild(el); });
                garbageList = [];
                quizOverlay.style.display = 'none';
                gameOverOverlay.style.display = 'none';
                feedback.className = '';
                binMouth.classList.remove('show');
                binStars.classList.remove('show');
                playerBin.classList.remove('eating', 'kicking', 'walking');
                setHappy();
                interactHint.classList.remove('show', 'special-hint');
                joystickDx = 0;
                joystickDy = 0;
                joystickActive = false;
                joystickThumb.style.transform = 'translate(-50%, -50%)';
                joystickBase.classList.remove('active');
                recentAdvancedIndices = [];

                selectScreen.style.display = 'none';
                gameScreen.style.display = 'flex';

                buildMap();
                if (animationFrame) cancelAnimationFrame(animationFrame);
                if (timerInterval) clearInterval(timerInterval);
                gameLoop();
                startTimer();
                setTimeout(() => {
                    ensureTwoGarbage();
                }, 400);
            }

            // ---------- 键盘快捷键 ----------
            document.addEventListener('keydown', (e) => {
                const key = e.key;
                if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'W', 'a', 'A', 's', 'S', 'd', 'D'].includes(
                    key)) {
                    e.preventDefault();
                    let dx = 0,
                        dy = 0;
                    if (key === 'ArrowUp' || key === 'w' || key === 'W') dy = -1;
                    if (key === 'ArrowDown' || key === 's' || key === 'S') dy = 1;
                    if (key === 'ArrowLeft' || key === 'a' || key === 'A') dx = -1;
                    if (key === 'ArrowRight' || key === 'd' || key === 'D') dx = 1;
                    joystickDx = dx;
                    joystickDy = dy;
                    joystickActive = true;
                    joystickBase.classList.add('active');
                    const radius = joystickBase.offsetWidth / 2;
                    const thumbRadius = joystickThumb.offsetWidth / 2;
                    const maxDist = radius - thumbRadius - 2;
                    const mag = Math.hypot(dx, dy);
                    const clampedMag = Math.min(mag, 1);
                    const moveX = dx / (mag || 1) * clampedMag * maxDist;
                    const moveY = dy / (mag || 1) * clampedMag * maxDist;
                    joystickThumb.style.transform = `translate(calc(-50% + ${moveX}px), calc(-50% + ${moveY}px))`;
                }
                if (key === '1') btnEat.click();
                if (key === '2') btnSpit.click();
            });
            document.addEventListener('keyup', (e) => {
                const key = e.key;
                if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'w', 'W', 'a', 'A', 's', 'S', 'd', 'D'].includes(
                    key)) {
                    e.preventDefault();
                    joystickDx = 0;
                    joystickDy = 0;
                    joystickActive = false;
                    joystickBase.classList.remove('active');
                    joystickThumb.style.transform = 'translate(-50%, -50%)';
                }
            });

            // ---------- 暴露全局 ----------
            window.startGame = startGame;

            // ---------- 事件绑定 ----------
            btnEat.addEventListener('click', () => { handleClassification(true); });
            btnSpit.addEventListener('click', () => { handleClassification(false); });

            const optionBtns = quizOptions.querySelectorAll('.quiz-option');
            optionBtns.forEach(btn => btn.addEventListener('click', handleQuizOptionClick));
            quizContinue.addEventListener('click', handleQuizContinue);

            restartSame.addEventListener('click', restartSameGame);
            gameoverBack.addEventListener('click', goToSelect);
            backBtn.addEventListener('click', goToSelect);
            resetBtn.addEventListener('click', goToSelect);

            pauseBtn.addEventListener('click', function() {
                if (isGameOver || isQuizActive) return;
                isPaused = !isPaused;
                pauseBtn.textContent = isPaused ? '▶️ 继续' : '⏸️ 暂停';
                if (isPaused) {
                    playerBin.classList.remove('walking');
                } else {
                    if (!isGameOver) {
                        ensureTwoGarbage();
                    }
                }
            });

            setupJoystick();
            updateHighScoreDisplay();
            selectScreen.style.display = 'flex';
            gameScreen.style.display = 'none';
        })();
    </script>

</body>
</html>

Game Source: 垃圾小卫士 · 双垃圾挑战

Creator: MysticLion44

Libraries: none

Complexity: complex (2387 lines, 90.9 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-mysticlion44" to link back to the original. Then publish at arcadelab.ai/publish.