{"asset_map": {"arena_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/arena_background_b28c8f5f-2bb0-4759-a188-60c29dd24c60.webp", "type": "image", "prompt": "Generate a top-down, bird's-eye view 9:16 tall background for a futuristic arena. Base color is deep blue-gray #1a1a2e. Featuring a subtle grid pattern with thin cyan lines, squares approximately 50x50 pixels. Include a slight vignette darkening at the edges and minimal sci-fi circuit-like patterns in the corners. High resolution, clean, arcade-like tech aesthetic.", "description": "A dark, tech-themed background texture with a subtle cyan grid for the arena floor.", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "lightsaber_impact_flash": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/lightsaber_impact_flash_45b0e494-4e5c-4755-97d5-93a9910c22ba.webp", "type": "image", "prompt": "Generate a bright impact flash effect sprite, radial burst of light from center, star-like shape with 6-8 pointed rays, bright white core fading to neon cyan #00ffff at edges. Soft glow effect, high contrast, clean minimalist arcade style, transparent background.", "description": "A bright neon cyan impact flash sprite for lightsaber collisions.", "subtype": "effect", "aspect_ratio": [684, 688], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "pause_icon": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/pause_icon_5914082d-b79c-41dd-a64b-e76d8dd17d68.webp", "type": "image", "prompt": "Generate a minimalist pause button icon, two vertical white bars with a subtle cyan #00ffff glow, clean flat design, high contrast, neon tech aesthetic, transparent background.", "description": "A minimalist white pause icon with a cyan glow for the game UI.", "subtype": "ui", "aspect_ratio": [1018, 1018], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "restart_icon": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/restart_icon_97d88c44-d279-464e-9602-25180f0f5cbb.webp", "type": "image", "prompt": "Generate a minimalist restart icon, circular clockwise arrow, white color with a subtle cyan #00ffff glow, clean flat design, neon tech aesthetic, transparent background.", "description": "A minimalist white circular arrow restart icon with a cyan glow.", "subtype": "ui", "aspect_ratio": [565, 575], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "victory_banner": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/victory_banner_692f06cf-451e-46c8-a186-7c1c2c6d5461.webp", "type": "image", "prompt": "Generate a victory screen graphic banner with bold text 'WAVE COMPLETE!'. Use neon cyan #00ffff and magenta #ff00ff color scheme. Energetic retro-arcade typography with subtle glow effects and decorative star bursts. High resolution, professional quality, transparent background.", "description": "A celebratory neon cyan and magenta banner for wave completion.", "subtype": "ui", "aspect_ratio": [1329, 833], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "defeat_banner": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/defeat_banner_cde1a058-d258-41e3-b64f-85a36ddc732c.webp", "type": "image", "prompt": "Generate a defeat screen graphic banner with bold text 'DEFEATED'. Red #ff3333 and dark gray color scheme. Same energetic retro-arcade typography as the victory banner. Subtle glow effects with cracked or shattered decorative elements. High resolution, transparent background.", "description": "A dramatic red and gray banner for the defeat screen.", "subtype": "ui", "aspect_ratio": [1185, 368], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "lightsaber_swing_sfx": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/lightsaber_swing_sfx_2f3e247e-e991-4101-9117-094b83dbaad1.mp3", "type": "audio", "prompt": "Classic sci-fi energy blade swing, medium-pitched 'vroom' whoosh with a slight doppler effect. Sharp, clean, and airy. Designed for rapid layering in a chaotic physics combat game. High-frequency clarity, no low-end rumble, 0.5 seconds duration.", "description": "A quick, energetic sci-fi hum and whoosh for lightsaber movement.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.88, "loop": false, "animation": null}, "lightsaber_hit_sfx": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/lightsaber_hit_sfx_5614dfba-5aad-4e48-8f0b-7cdfcf3dc8fa.mp3", "type": "audio", "prompt": "Bright electronic zap and clash sound effect. Sharp attack, high-voltage energy crackle, quick decay. Satisfying arcade impact feel. Sci-fi weapon collision, clean and punchy, suitable for frequent hits against stickman ragdolls.", "description": "A sharp, electric clash sound for lightsaber impacts.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2, "loop": false, "animation": null}, "dismember_sfx": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/dismember_sfx_31505184-f34d-43d7-8329-6cee520c4e0f.mp3", "type": "audio", "prompt": "Clean sci-fi energy slice sound effect. A sharp 'shing' combined with a digital buzz. Lighthearted and arcade-like, not gory. Slightly higher pitch than a standard hit, suggesting a successful cut through a ragdoll limb. Short and satisfying.", "description": "A clean, high-pitched energy slice sound for when limbs are severed.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 0.96, "loop": false, "animation": null}, "background_music": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/background_music_b8d388ad-a3ac-47f4-a2e6-8376a43b8770.mp3", "type": "audio", "prompt": "Fast-paced synthwave background music, 135 BPM. Driving electronic bassline, punchy 80s-style drum machine, neon retro-futuristic synth leads. Energetic and intense arcade action vibe. Seamlessly loopable, maintaining high tension without being overwhelming. Instrumental only.", "description": "Intense synthwave track with a driving beat for arena combat.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 30, "loop": true, "animation": null}, "victory_sfx": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/victory_sfx_78394e10-d3ff-4451-a175-8179e0b8e8fc.mp3", "type": "audio", "prompt": "Short triumphant victory fanfare, 2 seconds long. Bright ascending synth melody, celebratory electronic chime, modern arcade production. Resolves on a strong, positive major chord. Matches a neon synthwave aesthetic.", "description": "Triumphant electronic jingle for surviving the wave.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "defeat_sfx": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/defeat_sfx_d5c9bab6-4d94-46d6-9c9b-f55ee7d297d6.mp3", "type": "audio", "prompt": "Arcade defeat sound effect, 1.5 seconds. Descending electronic synth melody, slightly filtered 'power-down' effect. Melancholic but clean, matching the game's retro-future style. Clear ending, non-looping.", "description": "Descending synth tone for player defeat or game over.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "arena_background_v2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/arena_background_v2_ffa0f50a-81ea-43b1-b435-1f8bdfe991e9.webp", "type": "image", "prompt": "Generate a high-resolution, top-down view of a detailed sci-fi cyber-arena floor. The scene features polished digital art with cinematic lighting, showcasing heavy metal plating, bolts, and mechanical vents. Vibrant neon cyan and magenta energy conduits run through the floor in a complex geometric layout. Dark, moody atmosphere for a futuristic battle. No grid lines. The style should be consistent with the tech-theme of existing arena elements but with significantly more detail and depth.", "description": "A highly detailed sci-fi cyber-arena floor with glowing neon conduits and mechanical plating.", "subtype": "background", "aspect_ratio": [832, 1248], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "victory_sfx_v2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/victory_sfx_v2_051326d6-903c-42cc-bbce-7986b16b49f8.mp3", "type": "audio", "prompt": "A triumphant, cinematic sci-fi orchestral fanfare in the style of John Williams. Features soaring trumpet melodies, bold trombone stabs, energetic rhythmic strings (spiccato), and crashing orchestral cymbals. The composition should have a heroic, uplifting resolution in a bright major key. High-quality studio recording feel, epic space opera aesthetic, 5 seconds long, one-shot.", "description": "A heroic, Star Wars-inspired orchestral victory fanfare for successful mission completion.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 6, "loop": false, "animation": null}, "defeat_sfx_v2": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/defeat_sfx_v2_c72976c0-2d5c-4366-988b-a20a1e843ae0.mp3", "type": "audio", "prompt": "A short, cinematic orchestral defeat motif in the style of a space opera. The sound starts with a dramatic low brass swell followed by somber, descending strings. It should evoke a sense of tragedy and loss. High-quality orchestral recording, minor key, cinematic resolution with a slow fade out. No percussion, no electronic elements, purely instrumental.", "description": "Dramatic Star Wars-inspired orchestral defeat motif with low brass and somber strings.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 6, "loop": false, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Stickman Lightsaber Arena</title>\n    <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n    <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n    <link href=\"https://fonts.googleapis.com/css2?family=Orbitron:wght@700;900&family=Inter:wght@400;600;700&display=swap\" rel=\"stylesheet\">\n    <style>\n        :root {\n            --transition-fast: 150ms;\n            --transition-normal: 300ms;\n            --transition-slow: 500ms;\n            --ease-out: cubic-bezier(0.0, 0, 0.2, 1);\n            --ease-bounce: cubic-bezier(0.68, -0.6, 0.32, 1.6);\n            --space-xs: 8px;\n            --space-sm: 16px;\n            --space-md: 24px;\n            --space-lg: 32px;\n            --space-xl: 48px;\n        }\n\n        body, html {\n            margin: 0;\n            padding: 0;\n            width: 100%;\n            height: 100%;\n            overflow: hidden;\n            touch-action: none;\n            -ms-touch-action: none;\n            font-family: 'Inter', sans-serif;\n        }\n\n        #game-world {\n            position: relative;\n            width: 100%;\n            height: 100%;\n            background-color: #1a1a2e;\n        }\n\n        #game-canvas {\n            display: block;\n            width: 100%;\n            height: 100%;\n        }\n\n        /* UI Overlay */\n        .ui-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            pointer-events: none;\n            z-index: 10;\n        }\n\n        .ui-overlay > * {\n            pointer-events: auto;\n        }\n\n        /* HUD Elements */\n        .hud {\n            position: absolute;\n            top: var(--space-sm);\n            left: var(--space-sm);\n            right: var(--space-sm);\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-start;\n            gap: var(--space-sm);\n        }\n\n        .hud-item {\n            background: rgba(0, 0, 0, 0.7);\n            border: 2px solid rgba(0, 255, 255, 0.5);\n            border-radius: 8px;\n            padding: var(--space-sm);\n            color: #00ffff;\n            text-shadow: 0 0 10px rgba(0, 255, 255, 0.8);\n            font-family: 'Orbitron', sans-serif;\n            font-weight: 700;\n            pointer-events: none;\n        }\n\n        .hud-label {\n            font-size: 18px;\n            opacity: 0.8;\n            margin-bottom: 4px;\n        }\n\n        .hud-value {\n            font-size: 32px;\n            line-height: 1;\n        }\n\n        .timer {\n            flex: 1;\n            text-align: center;\n        }\n\n        .score, .kills {\n            min-width: 120px;\n        }\n\n        /* Pause Button */\n        .pause-btn {\n            position: absolute;\n            top: var(--space-sm);\n            right: var(--space-sm);\n            width: 96px;\n            height: 96px;\n            background: rgba(0, 0, 0, 0.7);\n            border: 2px solid rgba(0, 255, 255, 0.5);\n            border-radius: 8px;\n            cursor: pointer;\n            transition: all var(--transition-fast) var(--ease-out);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .pause-btn:hover {\n            background: rgba(0, 255, 255, 0.2);\n            border-color: #00ffff;\n            transform: scale(1.05);\n        }\n\n        .pause-btn:active {\n            transform: scale(0.95);\n        }\n\n        .pause-btn img {\n            width: 60px;\n            height: 60px;\n            filter: drop-shadow(0 0 8px rgba(0, 255, 255, 0.8));\n        }\n\n        /* Game Over Overlay */\n        .game-over-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.85);\n            display: none;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            gap: var(--space-lg);\n            animation: fadeIn var(--transition-slow) var(--ease-out);\n            z-index: 100;\n        }\n\n        .game-over-overlay.visible {\n            display: flex;\n        }\n\n        @keyframes fadeIn {\n            from { opacity: 0; }\n            to { opacity: 1; }\n        }\n\n        .game-over-banner {\n            width: 80%;\n            max-width: 500px;\n            height: auto;\n            filter: drop-shadow(0 0 20px rgba(0, 255, 255, 0.6));\n            animation: bannerPop var(--transition-slow) var(--ease-bounce);\n        }\n\n        @keyframes bannerPop {\n            0% { transform: scale(0.5); opacity: 0; }\n            100% { transform: scale(1); opacity: 1; }\n        }\n\n        .final-stats {\n            background: rgba(0, 0, 0, 0.8);\n            border: 2px solid rgba(0, 255, 255, 0.5);\n            border-radius: 16px;\n            padding: var(--space-lg);\n            min-width: 400px;\n            max-width: 90%;\n        }\n\n        .stat-row {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            padding: var(--space-sm) 0;\n            border-bottom: 1px solid rgba(0, 255, 255, 0.2);\n            font-size: 24px;\n        }\n\n        .stat-row:last-child {\n            border-bottom: none;\n            font-size: 32px;\n            font-weight: 700;\n            color: #00ffff;\n            text-shadow: 0 0 10px rgba(0, 255, 255, 0.8);\n        }\n\n        .stat-label {\n            color: #e0e0e0;\n        }\n\n        .stat-value {\n            color: #00ffff;\n            font-family: 'Orbitron', sans-serif;\n            font-weight: 700;\n        }\n\n        .btn-container {\n            display: flex;\n            gap: 40px;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .action-btn {\n            display: flex;\n            flex-direction: column;\n            align-items: center;\n            gap: 10px;\n            background: none;\n            border: none;\n            cursor: pointer;\n            transition: all var(--transition-fast) var(--ease-out);\n        }\n\n        .action-btn:hover {\n            transform: scale(1.1);\n        }\n\n        .btn-circle {\n            width: 96px;\n            height: 96px;\n            background: rgba(0, 255, 255, 0.2);\n            border: 2px solid #00ffff;\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            box-shadow: 0 0 15px rgba(0, 255, 255, 0.3);\n        }\n\n        .btn-label {\n            color: #00ffff;\n            font-family: 'Orbitron', sans-serif;\n            font-size: 16px;\n            font-weight: 700;\n        }\n\n        .action-btn img {\n            width: 50px;\n            height: 50px;\n            filter: drop-shadow(0 0 8px rgba(0, 255, 255, 0.8));\n        }\n\n        .next-btn .btn-circle {\n            background: rgba(0, 255, 255, 0.4);\n            border-width: 3px;\n        }\n\n        .next-btn img {\n            transform: rotate(-90deg);\n        }\n\n        /* Tutorial Overlay */\n        .tutorial-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.9);\n            display: none;\n            flex-direction: column;\n            align-items: center;\n            justify-content: center;\n            z-index: 200;\n            color: #00ffff;\n            padding: var(--space-lg);\n            text-align: center;\n            font-family: 'Inter', sans-serif;\n        }\n\n        .tutorial-overlay.visible {\n            display: flex;\n        }\n\n        .tutorial-title {\n            font-family: 'Orbitron', sans-serif;\n            font-size: 40px;\n            margin-bottom: var(--space-lg);\n            text-shadow: 0 0 10px rgba(0, 255, 255, 0.8);\n        }\n\n        .tutorial-step {\n            display: none;\n            flex-direction: column;\n            align-items: center;\n            gap: var(--space-md);\n            animation: fadeIn var(--transition-normal) var(--ease-out);\n        }\n\n        .tutorial-step.active {\n            display: flex;\n        }\n\n        .tutorial-img {\n            width: 200px;\n            height: 200px;\n            border: 2px dashed #00ffff;\n            border-radius: 50%;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            margin-bottom: var(--space-sm);\n            position: relative;\n        }\n\n        .hand-icon {\n            width: 80px;\n            height: 80px;\n            filter: drop-shadow(0 0 10px #00ffff);\n            animation: swipeAnim 2s infinite ease-in-out;\n        }\n\n        @keyframes swipeAnim {\n            0% { transform: translate(-40px, 40px); opacity: 0; }\n            20% { opacity: 1; }\n            80% { transform: translate(40px, -40px); opacity: 1; }\n            100% { transform: translate(40px, -40px); opacity: 0; }\n        }\n\n        .mouse-icon {\n            width: 80px;\n            height: 80px;\n            filter: drop-shadow(0 0 10px #00ffff);\n            animation: circleAnim 3s infinite linear;\n        }\n\n        @keyframes circleAnim {\n            from { transform: rotate(0deg) translateX(40px) rotate(0deg); }\n            to { transform: rotate(360deg) translateX(40px) rotate(-360deg); }\n        }\n\n        .tutorial-text {\n            font-size: 20px;\n            line-height: 1.6;\n            max-width: 500px;\n        }\n\n        .tutorial-btn {\n            margin-top: var(--space-xl);\n            padding: 15px 40px;\n            background: rgba(0, 255, 255, 0.2);\n            border: 2px solid #00ffff;\n            color: #00ffff;\n            font-family: 'Orbitron', sans-serif;\n            font-size: 24px;\n            cursor: pointer;\n            transition: all var(--transition-fast);\n        }\n\n        .tutorial-btn:hover {\n            background: rgba(0, 255, 255, 0.4);\n            transform: scale(1.05);\n        }\n\n        /* Virtual Joystick */\n        .joystick-container {\n            position: absolute;\n            pointer-events: none;\n            display: none;\n        }\n\n        .joystick-base {\n            width: 120px;\n            height: 120px;\n            border-radius: 50%;\n            background: rgba(0, 255, 255, 0.2);\n            border: 2px solid rgba(0, 255, 255, 0.5);\n            position: absolute;\n            transform: translate(-50%, -50%);\n        }\n\n        .joystick-stick {\n            width: 60px;\n            height: 60px;\n            border-radius: 50%;\n            background: rgba(0, 255, 255, 0.6);\n            border: 2px solid #00ffff;\n            position: absolute;\n            transform: translate(-50%, -50%);\n            box-shadow: 0 0 15px rgba(0, 255, 255, 0.8);\n        }\n\n        /* Paused State */\n        .paused-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n            background: rgba(0, 0, 0, 0.7);\n            display: none;\n            align-items: center;\n            justify-content: center;\n        }\n\n        .paused-overlay.visible {\n            display: flex;\n        }\n\n        .paused-text {\n            font-family: 'Orbitron', sans-serif;\n            font-size: 60px;\n            font-weight: 900;\n            color: #00ffff;\n            text-shadow: 0 0 20px rgba(0, 255, 255, 0.8);\n        }\n\n        /* Leaderboard Styling */\n        .leaderboard-container {\n            background: rgba(0, 0, 0, 0.8);\n            border: 2px solid rgba(0, 255, 255, 0.5);\n            border-radius: 16px;\n            padding: var(--space-lg);\n            min-width: 400px;\n            max-width: 90%;\n            margin-top: var(--space-md);\n            animation: fadeIn var(--transition-slow) var(--ease-out);\n        }\n\n        .leaderboard-title {\n            font-family: 'Orbitron', sans-serif;\n            font-size: 32px;\n            font-weight: 900;\n            color: #00ffff;\n            text-align: center;\n            margin-bottom: var(--space-md);\n            text-shadow: 0 0 15px rgba(0, 255, 255, 0.8);\n            letter-spacing: 3px;\n        }\n\n        .user-rank {\n            text-align: center;\n            font-family: 'Orbitron', sans-serif;\n            font-size: 20px;\n            color: #ffff00;\n            margin-bottom: var(--space-sm);\n            text-shadow: 0 0 10px rgba(255, 255, 0, 0.6);\n        }\n\n        .leaderboard-list {\n            display: flex;\n            flex-direction: column;\n            gap: var(--space-xs);\n        }\n\n        .leaderboard-entry {\n            display: flex;\n            justify-content: space-between;\n            align-items: center;\n            padding: var(--space-sm);\n            background: rgba(0, 255, 255, 0.05);\n            border: 1px solid rgba(0, 255, 255, 0.2);\n            border-radius: 8px;\n            transition: all var(--transition-fast) var(--ease-out);\n        }\n\n        .leaderboard-entry:hover {\n            background: rgba(0, 255, 255, 0.1);\n            border-color: rgba(0, 255, 255, 0.4);\n            transform: translateX(4px);\n        }\n\n        .leaderboard-entry.current-user {\n            background: rgba(255, 255, 0, 0.15);\n            border-color: rgba(255, 255, 0, 0.6);\n            box-shadow: 0 0 15px rgba(255, 255, 0, 0.3);\n        }\n\n        .rank-badge {\n            font-family: 'Orbitron', sans-serif;\n            font-weight: 700;\n            font-size: 18px;\n            color: #00ffff;\n            min-width: 50px;\n            text-shadow: 0 0 8px rgba(0, 255, 255, 0.6);\n        }\n\n        .current-user .rank-badge {\n            color: #ffff00;\n            text-shadow: 0 0 8px rgba(255, 255, 0, 0.6);\n        }\n\n        .player-name {\n            flex: 1;\n            font-size: 16px;\n            color: #e0e0e0;\n            margin: 0 var(--space-sm);\n            overflow: hidden;\n            text-overflow: ellipsis;\n            white-space: nowrap;\n        }\n\n        .current-user .player-name {\n            color: #ffffff;\n            font-weight: 600;\n        }\n\n        .player-score {\n            font-family: 'Orbitron', sans-serif;\n            font-weight: 700;\n            font-size: 20px;\n            color: #00ffff;\n            text-shadow: 0 0 8px rgba(0, 255, 255, 0.6);\n            min-width: 80px;\n            text-align: right;\n        }\n\n        .current-user .player-score {\n            color: #ffff00;\n            text-shadow: 0 0 8px rgba(255, 255, 0, 0.6);\n        }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        \n        <div class=\"ui-overlay\">\n            <!-- HUD -->\n            <div class=\"hud\">\n                <div class=\"hud-item score\">\n                    <div class=\"hud-label\">SCORE</div>\n                    <div class=\"hud-value\" id=\"score-value\">0</div>\n                </div>\n                <div class=\"hud-item timer\">\n                    <div class=\"hud-label\">TIME</div>\n                    <div class=\"hud-value\" id=\"timer-value\">5:00</div>\n                </div>\n                <div class=\"hud-item kills\">\n                    <div class=\"hud-label\">KILLS</div>\n                    <div class=\"hud-value\" id=\"kills-value\">0</div>\n                </div>\n            </div>\n\n            <!-- Pause Button -->\n            <button class=\"pause-btn\" id=\"pause-btn\" title=\"Pause Game\">\n                <img id=\"pause-icon\" src=\"\" alt=\"Pause\">\n            </button>\n\n            <!-- Virtual Joystick -->\n            \n            <div class=\"tutorial-overlay\" id=\"tutorial-overlay\">\n                <div class=\"tutorial-title\">TUTORIAL</div>\n                \n                <div class=\"tutorial-step active\" id=\"step-1\">\n                    <div class=\"tutorial-img\">\n                        <svg class=\"hand-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n                            <path d=\"M18 11V6a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v5m4 0a2 2 0 0 0-2-2v0a2 2 0 0 0-2 2v5m4-5v5a4 4 0 0 1-4 4h-3a5 5 0 0 1-5-5v-7a2 2 0 0 1 2-2v0a2 2 0 0 1 2 2v7m0-7a2 2 0 0 1 2-2v0a2 2 0 0 1 2 2v1\"></path>\n                        </svg>\n                    </div>\n                    <div class=\"tutorial-text\">\n                        <b>Movement</b><br>\n                        Slide on the left side of the screen or use WASD to move. Touch the yellow marker in the arena to continue.\n                    </div>\n                </div>\n\n                <div class=\"tutorial-step\" id=\"step-2\">\n                    <div class=\"tutorial-img\">\n                        <svg class=\"mouse-icon\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n                            <circle cx=\"12\" cy=\"12\" r=\"10\"></circle>\n                            <path d=\"M12 2v4m0 12v4M2 12h4m12 0h4\"></path>\n                        </svg>\n                    </div>\n                    <div class=\"tutorial-text\">\n                        <b>Combat</b><br>\n                        Slide on the right or move your mouse to aim your lightsaber. Kill the training dummy to proceed!\n                    </div>\n                </div>\n\n                <div class=\"tutorial-step\" id=\"step-3\">\n                    <div class=\"tutorial-text\">\n                        <b>Power-ups</b><br>\n                        Pick up the randomly spawned items to gain advantages. Touch them to collect!\n                    </div>\n                </div>\n\n                <div class=\"tutorial-step\" id=\"step-4\">\n                    <div class=\"tutorial-title\">POWER-UP GUIDE</div>\n                    <div class=\"tutorial-text\" style=\"text-align: left; font-size: 16px;\">\n                        <b style=\"color:#00ff00\">L - Length:</b> Increases saber length.<br>\n                        <b style=\"color:#ffff00\">W - Width:</b> Makes your saber much thicker.<br>\n                        <b style=\"color:#0000ff\">S - Shield:</b> Protects you from one lethal hit.<br>\n                        <b style=\"color:#ff00ff\">F - Fast:</b> Significant movement speed boost.<br>\n                        <b style=\"color:#00ffff\">+ - Extra:</b> Adds another saber to your other arm.<br>\n                        <b style=\"color:#a020f0\">C - Convert:</b> Next hit converts enemy into an ally!<br>\n                        <b style=\"color:#ff4500\">T - TNT:</b> Spawns an explosive barrel nearby.\n                    </div>\n                </div>\n\n                <button class=\"tutorial-btn\" id=\"tutorial-next-btn\">GOT IT</button>\n            </div>\n\n            <div class=\"joystick-container\" id=\"joystick\">\n                <div class=\"joystick-base\"></div>\n                <div class=\"joystick-stick\"></div>\n            </div>\n\n            <!-- Paused Overlay -->\n            <div class=\"paused-overlay\" id=\"paused-overlay\">\n                <div class=\"paused-text\">PAUSED</div>\n            </div>\n\n            <!-- Game Over Overlay -->\n            <div class=\"game-over-overlay\" id=\"game-over-overlay\">\n                <img class=\"game-over-banner\" id=\"game-over-banner\" src=\"\" alt=\"Game Over\">\n                <div class=\"final-stats\">\n                    <div class=\"stat-row\">\n                        <span class=\"stat-label\">Kills:</span>\n                        <span class=\"stat-value\" id=\"final-kills\">0</span>\n                    </div>\n                    <div class=\"stat-row\">\n                        <span class=\"stat-label\">Time Survived:</span>\n                        <span class=\"stat-value\" id=\"final-time\">0:00</span>\n                    </div>\n                    <div class=\"stat-row\">\n                        <span class=\"stat-label\">Final Score:</span>\n                        <span class=\"stat-value\" id=\"final-score\">0</span>\n                    </div>\n                </div>\n                <div class=\"btn-container\">\n                    <button class=\"action-btn restart-btn\" id=\"retry-btn\">\n                        <div class=\"btn-circle\">\n                            <img class=\"retry-icon-img\" src=\"\" alt=\"Restart\">\n                        </div>\n                        <div class=\"btn-label\">RETRY</div>\n                    </button>\n                    <button class=\"action-btn next-btn\" id=\"next-level-btn\">\n                        <div class=\"btn-circle\">\n                            <img class=\"next-icon-img\" src=\"\" alt=\"Next\">\n                        </div>\n                        <div class=\"btn-label\">NEXT</div>\n                    </button>\n                </div>\n            </div>\n        </div>\n    </div>\n\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js\"></script>\n    <script>\n        /**\n         * STICKMAN LIGHTSABER ARENA\n         * \n         * A physics-based combat game where players control a ragdoll stickman with a lightsaber.\n         * The game features floppy, unpredictable physics similar to Human Fall Flat, creating\n         * chaotic and humorous combat situations. Players must survive for 5 minutes against\n         * increasingly difficult waves of enemies.\n         * \n         * Core Mechanics:\n         * - Ragdoll physics for all characters using Matter.js\n         * - Dismemberment system (head = instant kill, weapon arm = disarm)\n         * - Touch/swipe controls for movement\n         * - Physics-based combat (attacks emerge from movement)\n         * - Score based on kills, damage dealt, and survival time\n         */\n\n        async function run(mode) {\n            const gameWorld = document.getElementById('game-world');\n            const canvas = document.getElementById('game-canvas');\n            const ctx = canvas.getContext('2d');\n\n            // Initialize display and resize handling\n            function resizeCanvas() {\n                canvas.width = gameWorld.clientWidth;\n                canvas.height = gameWorld.clientHeight;\n            }\n            resizeCanvas();\n            window.addEventListener('resize', resizeCanvas);\n\n            // Asset preloading\n            const assetCache = {};\n            const audioBuffers = {};\n            let audioContext;\n            let backgroundMusicSource = null;\n\n            async function preloadAssets() {\n                // Initialize audio context\n                audioContext = new (window.AudioContext || window.webkitAudioContext)();\n\n                const imageAssets = [\n                    'arena_background_v2',\n                    'lightsaber_impact_flash',\n                    'pause_icon',\n                    'restart_icon',\n                    'victory_banner',\n                    'defeat_banner'\n                ];\n\n                const audioAssets = [\n                    'lightsaber_swing_sfx',\n                    'lightsaber_hit_sfx',\n                    'dismember_sfx',\n                    'background_music',\n                    'victory_sfx',\n                    'defeat_sfx'\n                ];\n\n                // Load images\n                const imagePromises = imageAssets.map(id => {\n                    return new Promise((resolve, reject) => {\n                        const assetInfo = lib.getAsset(id);\n                        if (assetInfo) {\n                            const img = new Image();\n                            img.onload = () => {\n                                assetCache[id] = img;\n                                resolve();\n                            };\n                            img.onerror = reject;\n                            img.src = assetInfo.url;\n                        } else {\n                            resolve(); // Fallback for missing images\n                        }\n                    });\n                });\n\n                // Load audio\n                const audioPromises = audioAssets.map(id => {\n                    return new Promise(async (resolve, reject) => {\n                        let assetInfo = lib.getAsset(id);\n                        // Use Star Wars inspired versions if available\n                        if (id === 'victory_sfx' && lib.getAsset('victory_sfx_v2')) assetInfo = lib.getAsset('victory_sfx_v2');\n                        if (id === 'defeat_sfx' && lib.getAsset('defeat_sfx_v2')) assetInfo = lib.getAsset('defeat_sfx_v2');\n                        \n                        if (assetInfo) {\n                            try {\n                                const response = await fetch(assetInfo.url);\n                                const arrayBuffer = await response.arrayBuffer();\n                                const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);\n                                audioBuffers[id] = audioBuffer;\n                                resolve();\n                            } catch (error) {\n                                resolve(); // Fallback for missing audio\n                            }\n                        } else {\n                            resolve(); // Fallback\n                        }\n                    });\n                });\n\n                await Promise.all([...imagePromises, ...audioPromises]);\n\n                // Set UI images\n                if (assetCache.pause_icon) document.getElementById('pause-icon').src = assetCache.pause_icon.src;\n                if (assetCache.restart_icon) {\n                    document.querySelectorAll('.retry-icon-img').forEach(el => el.src = assetCache.restart_icon.src);\n                    document.querySelectorAll('.next-icon-img').forEach(el => {\n                        el.src = assetCache.restart_icon.src;\n                        el.style.transform = 'rotate(-90deg)'; \n                    });\n                }\n            }\n\n            // Audio playback functions\n            const lastSoundPlayTime = {};\n            function playSound(soundId, volume = 1.0, cooldown = 0) {\n                if (!audioBuffers[soundId]) return;\n                \n                const currentTime = performance.now();\n                if (cooldown > 0) {\n                    if (lastSoundPlayTime[soundId] && currentTime - lastSoundPlayTime[soundId] < cooldown) {\n                        return;\n                    }\n                    lastSoundPlayTime[soundId] = currentTime;\n                }\n                \n                const source = audioContext.createBufferSource();\n                const gainNode = audioContext.createGain();\n                \n                source.buffer = audioBuffers[soundId];\n                gainNode.gain.value = volume;\n                \n                source.connect(gainNode);\n                gainNode.connect(audioContext.destination);\n                \n                source.start(0);\n                return source;\n            }\n\n            function playBackgroundMusic() {\n                if (backgroundMusicSource) {\n                    backgroundMusicSource.stop();\n                }\n                \n                backgroundMusicSource = audioContext.createBufferSource();\n                const gainNode = audioContext.createGain();\n                \n                backgroundMusicSource.buffer = audioBuffers.background_music;\n                backgroundMusicSource.loop = true;\n                gainNode.gain.value = 0.3;\n                \n                backgroundMusicSource.connect(gainNode);\n                gainNode.connect(audioContext.destination);\n                \n                backgroundMusicSource.start(0);\n            }\n\n            function stopBackgroundMusic() {\n                if (backgroundMusicSource) {\n                    backgroundMusicSource.stop();\n                    backgroundMusicSource = null;\n                }\n            }\n\n            await preloadAssets();\n\n            // Load saved game state\n            let savedState = null;\n            try {\n                const response = await lib.getUserGameState();\n                if (response && response.state) {\n                    savedState = response.state;\n                    lib.log('Loaded saved game state:', savedState);\n                }\n            } catch (error) {\n                lib.log(`Failed to load saved state: ${error.message}`);\n                // Continue with default state\n            }\n\n            // Matter.js setup\n            const Engine = Matter.Engine;\n            const World = Matter.World;\n            const Bodies = Matter.Bodies;\n            const Body = Matter.Body;\n            const Constraint = Matter.Constraint;\n            const Events = Matter.Events;\n            const Vector = Matter.Vector;\n\n            const engine = Engine.create();\n            engine.gravity.y = 0; // Top-down view, no gravity\n\n            // Game state\n            let gameState = {\n                currentLevel: 1,\n                isPaused: false,\n                isGameOver: false,\n                isVictory: false,\n                isTutorialActive: true,\n                tutorialStep: 1,\n                tutorialStage: 'move', // 'move', 'combat', 'items'\n                tutorialTarget: null, // detection point or enemy or item\n                timeRemaining: 0,\n                levelStartTime: 0,\n                score: 0,\n                levelStartScore: 0, // Track score at level start for rollback\n                kills: 0,\n                dismemberments: 0,\n                oneHitKills: 0,\n                damageDealt: 0,\n                damageTaken: 0,\n                player: null,\n                enemies: [],\n                coverObjects: [],\n                impactFlashes: [],\n                powerUps: [],\n                lastPowerUpSpawnTime: 0,\n                lastTime: 0,\n                traps: [],\n                explosiveBarrels: []\n            };\n\n            // Input state\n            const input = {\n                joystick: { x: 0, y: 0, active: false },\n                mouse: { x: 0, y: 0, active: false },\n                keys: { w: false, a: false, s: false, d: false, up: false, left: false, down: false, right: false },\n                touchId: null\n            };\n\n            // Show game parameters\n            lib.showGameParameters({\n                name: 'Game Settings',\n                params: {\n                    'Starting Level': {\n                        key: 'startingLevel',\n                        type: 'slider',\n                        min: 1,\n                        max: 10,\n                        step: 1\n                    },\n                    'Starting Enemies': {\n                        key: 'startingEnemyCount',\n                        type: 'slider',\n                        min: 1,\n                        max: 5,\n                        step: 1\n                    },\n                    'Enemy Speed': {\n                        key: 'enemySpeedMultiplier',\n                        type: 'slider',\n                        min: 0.5,\n                        max: 2.0,\n                        step: 0.1\n                    },\n                    'Physics Floppiness': {\n                        key: 'limbFloppiness',\n                        type: 'slider',\n                        min: 0.1,\n                        max: 1.0,\n                        step: 0.1\n                    },\n                    'Player Lightsaber Length': {\n                        key: 'playerLightsaberLength',\n                        type: 'slider',\n                        min: 60,\n                        max: 120,\n                        step: 10\n                    },\n                    'Critical Hit Multiplier': {\n                        key: 'criticalHitMultiplier',\n                        type: 'slider',\n                        min: 1.5,\n                        max: 5.0,\n                        step: 0.5\n                    },\n                    'Player Lightsaber Color': {\n                        key: 'playerLightsaberColor',\n                        type: 'color'\n                    },\n                    'Enemy Lightsaber Color': {\n                        key: 'enemyLightsaberColor',\n                        type: 'color'\n                    }\n                }\n            });\n\n            // Enhanced Collision: Check actual intersection\n            function checkLineIntersection(a, b, c, d) {\n                let det = (b.x - a.x) * (d.y - c.y) - (b.y - a.y) * (d.x - c.x);\n                if (det === 0) return false;\n                let t = ((c.x - a.x) * (d.y - c.y) - (c.y - a.y) * (d.x - c.x)) / det;\n                let u = ((c.x - a.x) * (b.y - a.y) - (c.y - a.y) * (b.x - a.x)) / det;\n                return t >= 0 && t <= 1 && u >= 0 && u <= 1;\n            }\n\n            // Create arena boundaries\n            function createArenaBoundaries() {\n                const thickness = 20;\n                const walls = [\n                    Bodies.rectangle(canvas.width / 2, -thickness / 2, canvas.width, thickness, { isStatic: true, label: 'wall' }),\n                    Bodies.rectangle(canvas.width / 2, canvas.height + thickness / 2, canvas.width, thickness, { isStatic: true, label: 'wall' }),\n                    Bodies.rectangle(-thickness / 2, canvas.height / 2, thickness, canvas.height, { isStatic: true, label: 'wall' }),\n                    Bodies.rectangle(canvas.width + thickness / 2, canvas.height / 2, thickness, canvas.height, { isStatic: true, label: 'wall' })\n                ];\n                World.add(engine.world, walls);\n            }\n\n            // Create Explosive Barrels\n            function createExplosiveBarrels() {\n                const numBarrels = 2 + Math.floor(Math.random() * 2);\n                for (let i = 0; i < numBarrels; i++) {\n                    const x = 100 + Math.random() * (canvas.width - 200);\n                    const y = 100 + Math.random() * (canvas.height - 200);\n                    const barrel = Bodies.circle(x, y, 20, {\n                        friction: 0.5,\n                        restitution: 0.3,\n                        label: 'barrel'\n                    });\n                    gameState.explosiveBarrels.push({\n                        body: barrel,\n                        timer: -1, // -1 means not triggered\n                        isExploded: false\n                    });\n                    World.add(engine.world, barrel);\n                }\n            }\n\n            // Create Laser Traps\n            function createLaserTraps(avoidPositions = []) {\n                let numTraps = 0;\n                if (gameState.currentLevel > 20) numTraps = 2;\n                else if (gameState.currentLevel > 10) numTraps = 1;\n                \n                const safeDist = 150;\n                for (let i = 0; i < numTraps; i++) {\n                    let x, y, tooClose;\n                    let attempts = 0;\n                    do {\n                        x = 150 + Math.random() * (canvas.width - 300);\n                        y = 150 + Math.random() * (canvas.height - 300);\n                        tooClose = avoidPositions.some(pos => {\n                            const dx = x - pos.x;\n                            const dy = y - pos.y;\n                            return Math.sqrt(dx*dx + dy*dy) < safeDist;\n                        });\n                        attempts++;\n                    } while (tooClose && attempts < 30);\n\n                    const trap = {\n                        x: x,\n                        y: y,\n                        angle: Math.random() * Math.PI * 2,\n                        rotationSpeed: 0.005 + Math.random() * 0.01,\n                        length: 80 + Math.random() * 40\n                    };\n                    gameState.traps.push(trap);\n                }\n            }\n\n            // Create cover objects\n            function createCoverObjects() {\n                const numCovers = window.gameConfig.numCoverObjects;\n                const playAreaMargin = 100;\n                \n                for (let i = 0; i < numCovers; i++) {\n                    const x = playAreaMargin + Math.random() * (canvas.width - playAreaMargin * 2);\n                    const y = playAreaMargin + Math.random() * (canvas.height - playAreaMargin * 2);\n                    const width = 80 + Math.random() * 40;\n                    const height = 40 + Math.random() * 20;\n                    \n                    const cover = Bodies.rectangle(x, y, width, height, {\n                        isStatic: true,\n                        friction: 0.5,\n                        render: { fillStyle: '#444' }\n                    });\n                    \n                    gameState.coverObjects.push(cover);\n                    World.add(engine.world, cover);\n                }\n            }\n\n            // Create stickman with ragdoll physics\n            function createStickman(x, y, isPlayer = false) {\n                const scale = 1.0;\n                const headRadius = 10 * scale;\n                const torsoWidth = 8 * scale;\n                const torsoHeight = 40 * scale;\n                const limbWidth = 6 * scale;\n                const upperArmLength = 15 * scale;\n                const forearmLength = 15 * scale;\n                const upperLegLength = 18 * scale;\n                const lowerLegLength = 17 * scale;\n\n                const stiffness = window.gameConfig.limbFloppiness;\n                const damping = 0.1;\n\n                // Create body parts\n                const head = Bodies.circle(x, y - torsoHeight / 2 - headRadius, headRadius, {\n                    density: 0.001,\n                    friction: 0.5,\n                    restitution: 0.3\n                });\n\n                const torso = Bodies.rectangle(x, y, torsoWidth, torsoHeight, {\n                    density: 0.002,\n                    friction: 0.5,\n                    restitution: 0.3\n                });\n\n                // Arms\n                const leftUpperArm = Bodies.rectangle(\n                    x - torsoWidth / 2 - upperArmLength / 2,\n                    y - torsoHeight / 4,\n                    upperArmLength,\n                    limbWidth,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const leftForearm = Bodies.rectangle(\n                    x - torsoWidth / 2 - upperArmLength - forearmLength / 2,\n                    y - torsoHeight / 4,\n                    forearmLength,\n                    limbWidth,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const rightUpperArm = Bodies.rectangle(\n                    x + torsoWidth / 2 + upperArmLength / 2,\n                    y - torsoHeight / 4,\n                    upperArmLength,\n                    limbWidth,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const rightForearm = Bodies.rectangle(\n                    x + torsoWidth / 2 + upperArmLength + forearmLength / 2,\n                    y - torsoHeight / 4,\n                    forearmLength,\n                    limbWidth,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                // Legs\n                const leftUpperLeg = Bodies.rectangle(\n                    x - torsoWidth / 4,\n                    y + torsoHeight / 2 + upperLegLength / 2,\n                    limbWidth,\n                    upperLegLength,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const leftLowerLeg = Bodies.rectangle(\n                    x - torsoWidth / 4,\n                    y + torsoHeight / 2 + upperLegLength + lowerLegLength / 2,\n                    limbWidth,\n                    lowerLegLength,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const rightUpperLeg = Bodies.rectangle(\n                    x + torsoWidth / 4,\n                    y + torsoHeight / 2 + upperLegLength / 2,\n                    limbWidth,\n                    upperLegLength,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                const rightLowerLeg = Bodies.rectangle(\n                    x + torsoWidth / 4,\n                    y + torsoHeight / 2 + upperLegLength + lowerLegLength / 2,\n                    limbWidth,\n                    lowerLegLength,\n                    { density: 0.001, friction: 0.5 }\n                );\n\n                // Lightsaber\n                const lightsaberLength = isPlayer ? window.gameConfig.playerLightsaberLength : (80 + (gameState.currentLevel - 1) * 5);\n                const lightsaber = Bodies.rectangle(\n                    x + torsoWidth / 2 + upperArmLength + forearmLength + lightsaberLength / 2,\n                    y - torsoHeight / 4,\n                    lightsaberLength,\n                    12,\n                    { density: 0.0002, friction: 0.1, isSensor: true, label: 'lightsaber' }\n                );\n\n                // Create constraints (joints)\n                const constraints = [];\n\n                // Head to torso\n                constraints.push(Constraint.create({\n                    bodyA: head,\n                    bodyB: torso,\n                    pointA: { x: 0, y: headRadius },\n                    pointB: { x: 0, y: -torsoHeight / 2 },\n                    stiffness: 1.0, // Heads should be firmly attached until cut\n                    damping: 0.1,\n                    length: 0\n                }));\n\n                // Left arm\n                constraints.push(Constraint.create({\n                    bodyA: torso,\n                    bodyB: leftUpperArm,\n                    pointA: { x: -torsoWidth / 2, y: -torsoHeight / 4 },\n                    pointB: { x: upperArmLength / 2, y: 0 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                constraints.push(Constraint.create({\n                    bodyA: leftUpperArm,\n                    bodyB: leftForearm,\n                    pointA: { x: -upperArmLength / 2, y: 0 },\n                    pointB: { x: forearmLength / 2, y: 0 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                // Right arm (weapon arm)\n                constraints.push(Constraint.create({\n                    bodyA: torso,\n                    bodyB: rightUpperArm,\n                    pointA: { x: torsoWidth / 2, y: -torsoHeight / 4 },\n                    pointB: { x: -upperArmLength / 2, y: 0 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                const rightElbowConstraint = Constraint.create({\n                    bodyA: rightUpperArm,\n                    bodyB: rightForearm,\n                    pointA: { x: upperArmLength / 2, y: 0 },\n                    pointB: { x: -forearmLength / 2, y: 0 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                });\n                constraints.push(rightElbowConstraint);\n\n                // Lightsaber to right forearm\n                const lightsaberConstraint = Constraint.create({\n                    bodyA: rightForearm,\n                    bodyB: lightsaber,\n                    pointA: { x: forearmLength / 2, y: 0 },\n                    pointB: { x: -lightsaberLength / 2, y: 0 },\n                    stiffness: 0.08, // \u5927\u5e45\u964d\u4f4e\u521a\u5ea6\uff0c\u4f7f\u5149\u5251\u4e0e\u8eaf\u4f53\u4e0d\u518d\u7d27\u7ef7\uff0c\u5448\u73b0\u51fa\u66f4\u7075\u6d3b\u7684\u6446\u52a8\u611f\n                    damping: 0.05,\n                    length: 0\n                });\n                constraints.push(lightsaberConstraint);\n\n                // Left leg\n                constraints.push(Constraint.create({\n                    bodyA: torso,\n                    bodyB: leftUpperLeg,\n                    pointA: { x: -torsoWidth / 4, y: torsoHeight / 2 },\n                    pointB: { x: 0, y: -upperLegLength / 2 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                constraints.push(Constraint.create({\n                    bodyA: leftUpperLeg,\n                    bodyB: leftLowerLeg,\n                    pointA: { x: 0, y: upperLegLength / 2 },\n                    pointB: { x: 0, y: -lowerLegLength / 2 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                // Right leg\n                constraints.push(Constraint.create({\n                    bodyA: torso,\n                    bodyB: rightUpperLeg,\n                    pointA: { x: torsoWidth / 4, y: torsoHeight / 2 },\n                    pointB: { x: 0, y: -upperLegLength / 2 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                constraints.push(Constraint.create({\n                    bodyA: rightUpperLeg,\n                    bodyB: rightLowerLeg,\n                    pointA: { x: 0, y: upperLegLength / 2 },\n                    pointB: { x: 0, y: -lowerLegLength / 2 },\n                    stiffness: stiffness,\n                    damping: damping,\n                    length: 0\n                }));\n\n                // Add all bodies and constraints to world\n                const allBodies = [\n                    head, torso,\n                    leftUpperArm, leftForearm, rightUpperArm, rightForearm,\n                    leftUpperLeg, leftLowerLeg, rightUpperLeg, rightLowerLeg,\n                    lightsaber\n                ];\n\n                allBodies.forEach(b => {\n                    b.owner = null; // To be set after creation\n                    b.partType = '';\n                });\n                head.partType = 'head';\n                torso.partType = 'torso';\n                rightUpperArm.partType = 'arm';\n                rightForearm.partType = 'arm';\n\n                World.add(engine.world, allBodies);\n                World.add(engine.world, constraints);\n\n                const stickman = {\n                    isPlayer: isPlayer,\n                    isAlive: true,\n                    hasWeapon: true,\n                    receivedDamage: false,\n                    lightsaberLength: lightsaberLength,\n                    head: head,\n                    torso: torso,\n                    leftUpperArm: leftUpperArm,\n                    leftForearm: leftForearm,\n                    rightUpperArm: rightUpperArm,\n                    rightForearm: rightForearm,\n                    leftUpperLeg: leftUpperLeg,\n                    leftLowerLeg: leftLowerLeg,\n                    rightUpperLeg: rightUpperLeg,\n                    rightLowerLeg: rightLowerLeg,\n                    lightsaber: lightsaber,\n                    constraints: constraints,\n                    rightElbowConstraint: rightElbowConstraint,\n                    lightsaberConstraint: lightsaberConstraint,\n                    allBodies: allBodies,\n                    lastSwingTime: 0\n                };\n\n                allBodies.forEach(b => b.owner = stickman);\n                return stickman;\n            }\n\n            // Save game progress\n            async function saveGameProgress() {\n                try {\n                    const saveData = {\n                        currentLevel: gameState.currentLevel,\n                        score: gameState.levelStartScore\n                    };\n                    await lib.saveUserGameState(saveData);\n                    lib.log('Progress saved:', saveData);\n                } catch (error) {\n                    lib.log(`Failed to save progress: ${error.message}`);\n                }\n            }\n\n            // Initialize game\n            function initGame(resetLevel = true) {\n                // Clear existing world\n                World.clear(engine.world);\n                Engine.clear(engine);\n\n                if (resetLevel) {\n                    gameState.currentLevel = window.gameConfig.startingLevel || 1;\n                    gameState.score = 0;\n                    gameState.levelStartScore = 0;\n                    gameState.kills = 0;\n                    gameState.dismemberments = 0;\n                    gameState.oneHitKills = 0;\n                } else {\n                    // Moving to next level or retrying - track score at level start\n                    gameState.levelStartScore = gameState.score;\n                    \n                    // Save progress when starting a new level\n                    if (mode === 'play') {\n                        saveGameProgress();\n                    }\n                }\n\n                // Reset per-level game state\n                gameState.isPaused = false;\n                gameState.isGameOver = false;\n                gameState.isVictory = false;\n                gameState.timeRemaining = window.gameConfig.waveDuration * 60;\n                gameState.levelStartTime = performance.now();\n                gameState.damageDealt = 0;\n                gameState.damageTaken = 0;\n                gameState.enemies = [];\n                gameState.coverObjects = [];\n                gameState.impactFlashes = [];\n                gameState.powerUps = [];\n                gameState.explosiveBarrels = [];\n                gameState.traps = [];\n                gameState.lastPowerUpSpawnTime = performance.now();\n                gameState.lastTime = performance.now();\n\n                // Create arena\n                createArenaBoundaries();\n                createCoverObjects();\n\n                // Create player\n                gameState.player = createStickman(canvas.width / 2, canvas.height / 2, true);\n                gameState.player.extraSabers = [];\n                gameState.player.isConverting = false;\n\n                // Handle Tutorial Level Logic\n                if (gameState.currentLevel === 1 && mode === 'play' && gameState.isTutorialActive) {\n                    initTutorialStage('move');\n                    document.getElementById('tutorial-overlay').classList.add('visible');\n                } else {\n                    gameState.isTutorialActive = false;\n                    spawnLevelEnemies();\n                }\n\n                updateUI();\n                document.getElementById('game-over-overlay').classList.remove('visible');\n\n                if (mode === 'play' && !backgroundMusicSource) {\n                    playBackgroundMusic();\n                }\n            }\n\n            function initTutorialStage(stage) {\n                gameState.tutorialStage = stage;\n                if (stage === 'move') gameState.tutorialStep = 1;\n                else if (stage === 'combat') gameState.tutorialStep = 2;\n                else if (stage === 'items') gameState.tutorialStep = 3;\n                else if (stage === 'items_info') gameState.tutorialStep = 4;\n                \n                // Clear previous targets\n                if (gameState.tutorialTarget) {\n                    if (gameState.tutorialTarget.allBodies) {\n                        gameState.tutorialTarget.allBodies.forEach(b => World.remove(engine.world, b));\n                    } else {\n                        World.remove(engine.world, gameState.tutorialTarget);\n                    }\n                    gameState.tutorialTarget = null;\n                }\n                gameState.enemies = [];\n                gameState.powerUps.forEach(p => World.remove(engine.world, p.body));\n                gameState.powerUps = [];\n\n                if (stage === 'move') {\n                    // Create detection point\n                    const x = canvas.width * 0.2 + Math.random() * canvas.width * 0.6;\n                    const y = canvas.height * 0.2 + Math.random() * canvas.height * 0.6;\n                    gameState.tutorialTarget = Bodies.circle(x, y, 40, { isSensor: true, isStatic: true, label: 'tutorial_marker' });\n                    World.add(engine.world, gameState.tutorialTarget);\n                } else if (stage === 'combat') {\n                    // Spawn one stationary enemy\n                    const enemy = createStickman(canvas.width / 2, canvas.height * 0.3, false);\n                    enemy.aiState = { targetX: enemy.torso.position.x, targetY: enemy.torso.position.y, changeTargetTime: Infinity };\n                    gameState.enemies.push(enemy);\n                    gameState.tutorialTarget = enemy;\n                } else if (stage === 'items') {\n                    // Spawn random items\n                    for(let i=0; i<3; i++) spawnPowerUp();\n                }\n            }\n\n            function spawnLevelEnemies() {\n                // Level scaling\n                const baseEnemies = window.gameConfig.startingEnemyCount;\n                const numEnemies = baseEnemies + (gameState.currentLevel - 1);\n                \n                const enemyStartPositions = [];\n                for (let i = 0; i < numEnemies; i++) {\n                    let x, y, distToPlayer;\n                    const margin = 100;\n                    const minPlayerDist = 250;\n                    \n                    let attempts = 0;\n                    do {\n                        x = margin + Math.random() * (canvas.width - margin * 2);\n                        y = margin + Math.random() * (canvas.height - margin * 2);\n                        const dx = x - canvas.width / 2;\n                        const dy = y - canvas.height / 2;\n                        distToPlayer = Math.sqrt(dx * dx + dy * dy);\n                        attempts++;\n                    } while (distToPlayer < minPlayerDist && attempts < 20);\n                    \n                    enemyStartPositions.push({x, y});\n                    const isElite = Math.random() < (0.1 + gameState.currentLevel * 0.05);\n                    const enemy = createStickman(x, y, false);\n                    \n                    if (isElite) {\n                        World.remove(engine.world, enemy.lightsaberConstraint);\n                        World.remove(engine.world, enemy.lightsaber);\n                        \n                        const eliteSaberLength = 120;\n                        enemy.lightsaber = Bodies.rectangle(x, y, eliteSaberLength, 12, { \n                            density: 0.0002, friction: 0.1, isSensor: true, label: 'lightsaber' \n                        });\n                        enemy.lightsaber.owner = enemy;\n                        enemy.lightsaberLength = eliteSaberLength;\n                        World.add(engine.world, enemy.lightsaber);\n                        \n                        enemy.lightsaberConstraint = Constraint.create({\n                            bodyA: enemy.rightForearm,\n                            bodyB: enemy.lightsaber,\n                            pointA: { x: 15 / 2, y: 0 },\n                            pointB: { x: -eliteSaberLength / 2, y: 0 },\n                            stiffness: 0.08,\n                            damping: 0.05,\n                            length: 0\n                        });\n                        World.add(engine.world, enemy.lightsaberConstraint);\n                    }\n\n                    enemy.aiState = { targetX: x, targetY: y, changeTargetTime: 0 };\n                    gameState.enemies.push(enemy);\n                }\n\n                // Create Traps (avoiding player and enemy spawn points)\n                createLaserTraps([{x: canvas.width/2, y: canvas.height/2}, ...enemyStartPositions]);\n            }\n\n            // Update UI\n            function updateUI() {\n                const minutes = Math.floor(gameState.timeRemaining / 60);\n                const seconds = Math.floor(gameState.timeRemaining % 60);\n                document.getElementById('timer-value').textContent = \n                    `${minutes}:${seconds.toString().padStart(2, '0')}`;\n                document.getElementById('score-value').textContent = Math.floor(gameState.score);\n                document.getElementById('kills-value').textContent = gameState.kills;\n                \n                // Add Level display to HUD if not present\n                let levelItem = document.getElementById('level-item');\n                if (!levelItem) {\n                    const hud = document.querySelector('.hud');\n                    levelItem = document.createElement('div');\n                    levelItem.id = 'level-item';\n                    levelItem.className = 'hud-item level';\n                    levelItem.innerHTML = '<div class=\"hud-label\">LEVEL</div><div class=\"hud-value\" id=\"level-value\">1</div>';\n                    hud.insertBefore(levelItem, hud.querySelector('.timer'));\n                }\n                document.getElementById('level-value').textContent = gameState.currentLevel;\n            }\n            // End game\n            async function endGame(victory) {\n                if (gameState.isGameOver) return;\n                gameState.isGameOver = true;\n                gameState.isVictory = victory;\n\n                stopBackgroundMusic();\n\n                const timeTaken = Math.max(0, (performance.now() - gameState.levelStartTime) / 1000);\n                const timeBonus = victory ? Math.max(0, 300 - timeTaken) * 20 : 0;\n                const killScore = gameState.kills * 200;\n                const dismemberScore = gameState.dismemberments * 100;\n                const oneHitScore = gameState.oneHitKills * 500;\n                \n                gameState.score += (timeBonus + killScore + dismemberScore + oneHitScore);\n\n                // Update final stats UI\n                document.getElementById('final-kills').textContent = gameState.kills;\n                document.getElementById('final-score').textContent = Math.floor(gameState.score);\n                const minutes = Math.floor(timeTaken / 60);\n                const seconds = Math.floor(timeTaken % 60);\n                document.getElementById('final-time').textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;\n\n                // Show appropriate banner\n                const banner = document.getElementById('game-over-banner');\n                if (banner && assetCache.victory_banner && assetCache.defeat_banner) {\n                    banner.src = victory ? assetCache.victory_banner.src : assetCache.defeat_banner.src;\n                }\n\n                // Submit score to leaderboard and display\n                await submitAndDisplayLeaderboard();\n\n                // Handle button visibility\n                const nextBtn = document.getElementById('next-level-btn');\n                const retryBtn = document.getElementById('retry-btn');\n                \n                if (nextBtn) {\n                    nextBtn.style.display = victory ? 'flex' : 'none';\n                    nextBtn.onclick = async (e) => {\n                        e.stopPropagation();\n                        gameState.currentLevel++;\n                        gameState.kills = 0;\n                        gameState.dismemberments = 0;\n                        gameState.oneHitKills = 0;\n                        initGame(false);\n                    };\n                }\n                \n                if (retryBtn) {\n                    const label = retryBtn.querySelector('.btn-label');\n                    if (label) label.textContent = victory ? 'REPLAY' : 'RETRY';\n                    \n                    retryBtn.onclick = (e) => {\n                        e.stopPropagation();\n                        // Rollback score to level start instead of resetting to 0\n                        gameState.score = gameState.levelStartScore;\n                        gameState.kills = 0;\n                        gameState.dismemberments = 0;\n                        gameState.oneHitKills = 0;\n                        initGame(false);\n                    };\n                }\n\n                playSound(victory ? 'victory_sfx' : 'defeat_sfx', 0.7);\n\n                setTimeout(() => {\n                    const overlay = document.getElementById('game-over-overlay');\n                    if (overlay) overlay.classList.add('visible');\n                }, 500);\n            }\n\n            // Submit score and display leaderboard\n            async function submitAndDisplayLeaderboard() {\n                try {\n                    const finalScore = Math.floor(gameState.score);\n                    const response = await lib.addPlayerScoreToLeaderboard(finalScore, 5);\n                    \n                    if (response.success) {\n                        displayLeaderboard(response.entries, response.userRank);\n                    }\n                } catch (error) {\n                    lib.log(`Failed to submit score to leaderboard: ${error.message}`);\n                    // Game still works without leaderboard\n                    hideLeaderboard();\n                }\n            }\n\n            // Display leaderboard in game over screen\n            function displayLeaderboard(entries, userRank) {\n                let leaderboardEl = document.getElementById('leaderboard-container');\n                \n                if (!leaderboardEl) {\n                    // Create leaderboard container\n                    leaderboardEl = document.createElement('div');\n                    leaderboardEl.id = 'leaderboard-container';\n                    leaderboardEl.className = 'leaderboard-container';\n                    \n                    const gameOverOverlay = document.getElementById('game-over-overlay');\n                    const finalStats = document.querySelector('.final-stats');\n                    if (gameOverOverlay && finalStats) {\n                        gameOverOverlay.insertBefore(leaderboardEl, finalStats.nextSibling);\n                    }\n                }\n                \n                // Build leaderboard HTML\n                let html = '<div class=\"leaderboard-title\">LEADERBOARD</div>';\n                \n                if (userRank !== null) {\n                    html += `<div class=\"user-rank\">YOUR RANK: #${userRank}</div>`;\n                }\n                \n                html += '<div class=\"leaderboard-list\">';\n                \n                entries.forEach((entry, index) => {\n                    const rank = index + 1;\n                    const isCurrentUser = userRank === rank;\n                    const rowClass = isCurrentUser ? 'leaderboard-entry current-user' : 'leaderboard-entry';\n                    \n                    html += `\n                        <div class=\"${rowClass}\">\n                            <span class=\"rank-badge\">#${rank}</span>\n                            <span class=\"player-name\">${entry.username || entry.userId}</span>\n                            <span class=\"player-score\">${entry.score}</span>\n                        </div>\n                    `;\n                });\n                \n                html += '</div>';\n                \n                leaderboardEl.innerHTML = html;\n                leaderboardEl.style.display = 'block';\n            }\n\n            // Hide leaderboard if submission fails\n            function hideLeaderboard() {\n                const leaderboardEl = document.getElementById('leaderboard-container');\n                if (leaderboardEl) {\n                    leaderboardEl.style.display = 'none';\n                }\n            }\n\n            function checkLightsaberHit(saber, targetBody, currentLength, currentWidth) {\n                if (!targetBody || !saber || targetBody.isDismembered) return false;\n                \n                const angle = saber.angle;\n                const length = currentLength || 80;\n                const thickness = currentWidth || 12; \n                \n                // Get saber line segment\n                const p1 = {\n                    x: saber.position.x - Math.cos(angle) * (length / 2),\n                    y: saber.position.y - Math.sin(angle) * (length / 2)\n                };\n                const p2 = {\n                    x: saber.position.x + Math.cos(angle) * (length / 2),\n                    y: saber.position.y + Math.sin(angle) * (length / 2)\n                };\n\n                // 1. Check if the line segment p1-p2 intersects or is very close to targetBody\n                // For circles (heads), check distance from segment\n                if (targetBody.circleRadius) {\n                    const center = targetBody.position;\n                    const l2 = (p2.x - p1.x)**2 + (p2.y - p1.y)**2;\n                    if (l2 === 0) return false;\n                    let t = ((center.x - p1.x) * (p2.x - p1.x) + (center.y - p1.y) * (p2.y - p1.y)) / l2;\n                    t = Math.max(0, Math.min(1, t));\n                    const projection = {\n                        x: p1.x + t * (p2.x - p1.x),\n                        y: p1.y + t * (p2.y - p1.y)\n                    };\n                    const distSq = (center.x - projection.x)**2 + (center.y - projection.y)**2;\n                    return distSq < (targetBody.circleRadius + thickness/2)**2;\n                }\n\n                // 2. For polygons (torso/arms), check intersection of saber segment with all polygon edges\n                const vertices = targetBody.vertices;\n                for (let i = 0; i < vertices.length; i++) {\n                    const v1 = vertices[i];\n                    const v2 = vertices[(i + 1) % vertices.length];\n                    if (checkLineIntersection(p1, p2, v1, v2)) return true;\n                }\n\n                // Increase thickness for polygon check as well (approximate)\n                const polyDist = Matter.Vertices.contains(vertices, p1) || \n                               Matter.Vertices.contains(vertices, p2) ||\n                               Matter.Vertices.contains(vertices, saber.position);\n                \n                if (polyDist) return true;\n\n                return false;\n            }\n\n            // Check for lightsaber collisions\n            function checkLightsaberCollisions() {\n                const minVelocity = 0;\n\n                // Player attacking\n                if (gameState.player.isAlive) {\n                    const playerSabers = [];\n                    if (gameState.player.lightsaber) {\n                        playerSabers.push({\n                            body: gameState.player.lightsaber,\n                            length: gameState.player.lightsaberLength || 80,\n                            width: gameState.player.lightsaberWidth || 12\n                        });\n                    }\n                    gameState.player.extraSabers.forEach(s => {\n                        playerSabers.push({\n                            body: s.body,\n                            length: s.lengthOverride || window.gameConfig.playerLightsaberLength,\n                            width: s.widthOverride || gameState.player.lightsaberWidth || 12\n                        });\n                    });\n                    \n                    for (const saberObj of playerSabers) {\n                        const saber = saberObj.body;\n                        const currentLength = saberObj.length;\n                        const currentWidth = saberObj.width;\n\n                        // Check barrel collision\n                        for (const barrel of gameState.explosiveBarrels) {\n                            if (!barrel.isExploded && barrel.timer === -1 && checkLightsaberHit(saber, barrel.body, currentLength, currentWidth)) {\n                                barrel.timer = 1.5; // 1.5 seconds countdown\n                                playSound('lightsaber_hit_sfx', 0.5, 100);\n                            }\n                        }\n\n                        for (const enemy of gameState.enemies) {\n                            if (!enemy.isAlive || enemy.isConverted) continue;\n\n                            // Head Check\n                            if (!enemy.isHeadDismembered && checkLightsaberHit(saber, enemy.head, currentLength, currentWidth)) {\n                                if (gameState.player.isConverting) {\n                                    convertEnemy(enemy);\n                                    // Power-up balance: One-time conversion per saber hit\n                                    removePowerUp('convert');\n                                } else {\n                                    if (!enemy.receivedDamage) {\n                                        gameState.oneHitKills++;\n                                        gameState.score += 500;\n                                    }\n                                    dismemberHead(enemy);\n                                    gameState.kills++;\n                                    gameState.score += 200;\n                                    createImpactFlash(enemy.head.position.x, enemy.head.position.y);\n                                    playSound('lightsaber_hit_sfx', 0.2, 100);\n                                }\n                                continue;\n                            }\n\n                            // Arm Check (Weapon side)\n                            if (enemy.hasWeapon && !enemy.isArmDismembered && (checkLightsaberHit(saber, enemy.rightUpperArm, currentLength, currentWidth) || checkLightsaberHit(saber, enemy.rightForearm, currentLength, currentWidth))) {\n                                enemy.receivedDamage = true;\n                                dismemberWeaponArm(enemy);\n                                gameState.dismemberments++;\n                                gameState.score += 100;\n                                createImpactFlash(enemy.rightForearm.position.x, enemy.rightForearm.position.y);\n                                playSound('lightsaber_hit_sfx', 0.2, 100);\n                                continue;\n                            }\n\n                            // Leg Check\n                            if (checkLightsaberHit(saber, enemy.leftUpperLeg, currentLength, currentWidth) || checkLightsaberHit(saber, enemy.leftLowerLeg, currentLength, currentWidth) ||\n                                checkLightsaberHit(saber, enemy.rightUpperLeg, currentLength, currentWidth) || checkLightsaberHit(saber, enemy.rightLowerLeg, currentLength, currentWidth)) {\n                                enemy.receivedDamage = true;\n                                createImpactFlash(saber.position.x, saber.position.y);\n                                playSound('lightsaber_hit_sfx', 0.15, 100);\n                            }\n\n                            // Body Check\n                            if (checkLightsaberHit(saber, enemy.torso, currentLength, currentWidth)) {\n                                enemy.receivedDamage = true;\n                                createImpactFlash(enemy.torso.position.x, enemy.torso.position.y);\n                                playSound('lightsaber_hit_sfx', 0.2, 100);\n                            }\n                        }\n                    }\n                }\n\n                // Enemies attacking player\n                for (const enemy of gameState.enemies) {\n                    if (!enemy.isAlive || !enemy.hasWeapon) continue;\n                    \n                    let attackTargets = [];\n                    if (enemy.isConverted) {\n                        attackTargets = gameState.enemies.filter(e => !e.isConverted && e.isAlive);\n                    } else {\n                        attackTargets = [gameState.player];\n                        attackTargets = attackTargets.concat(gameState.enemies.filter(e => e.isConverted && e.isAlive));\n                    }\n\n                    for (const target of attackTargets) {\n                        if (!target || !target.isAlive) continue;\n                        const enemySaberLen = enemy.lightsaberLength || 80;\n                        const enemySaberWidth = 12;\n                        if (!target.isHeadDismembered && checkLightsaberHit(enemy.lightsaber, target.head, enemySaberLen, enemySaberWidth)) {\n                             if (target.isPlayer && target.hasShield) {\n                                removePowerUp('shield');\n                                createImpactFlash(target.head.position.x, target.head.position.y);\n                                playSound('lightsaber_hit_sfx', 0.8, 100);\n                             } else {\n                                dismemberHead(target);\n                                if (target.isPlayer) endGame(false);\n                                createImpactFlash(target.head.position.x, target.head.position.y);\n                                playSound('lightsaber_hit_sfx', 0.2, 100);\n                             }\n                        }\n                    }\n                }\n            }\n\n            function convertEnemy(enemy) {\n                enemy.isConverted = true;\n                enemy.isAlive = true; \n                enemy.customSaberColor = window.gameConfig.playerLightsaberColor;\n                createImpactFlash(enemy.head.position.x, enemy.head.position.y);\n                playSound('victory_sfx', 0.3);\n            }\n\n            // Create power-up\n            function spawnPowerUp() {\n                const types = ['length', 'width', 'shield', 'speed', 'extra_saber', 'convert', 'tnt'];\n                const type = types[Math.floor(Math.random() * types.length)];\n                const x = 100 + Math.random() * (canvas.width - 200);\n                const y = 100 + Math.random() * (canvas.height - 200);\n                const powerUp = Bodies.circle(x, y, 20, { isSensor: true, isStatic: true, label: 'powerup' });\n                gameState.powerUps.push({ body: powerUp, type: type, spawnTime: performance.now() });\n                World.add(engine.world, powerUp);\n            }\n\n            function updatePowerUps(deltaTime) {\n                const currentTime = performance.now();\n                if (currentTime - gameState.lastPowerUpSpawnTime > 12000 && gameState.powerUps.length < 3) {\n                    spawnPowerUp();\n                    gameState.lastPowerUpSpawnTime = currentTime;\n                }\n                if (!gameState.player || !gameState.player.isAlive) return;\n                for (let i = gameState.powerUps.length - 1; i >= 0; i--) {\n                    const p = gameState.powerUps[i];\n                    if (checkBodyCollision(gameState.player.torso, p.body)) {\n                        applyPowerUp(p.type);\n                        World.remove(engine.world, p.body);\n                        gameState.powerUps.splice(i, 1);\n                        continue;\n                    }\n                    if (currentTime - p.spawnTime > 15000) {\n                        World.remove(engine.world, p.body);\n                        gameState.powerUps.splice(i, 1);\n                    }\n                }\n                if (gameState.player.activePowerUps) {\n                    for (const [type, expiry] of Object.entries(gameState.player.activePowerUps)) {\n                        if (currentTime > expiry) removePowerUp(type);\n                    }\n                }\n            }\n\n            function applyPowerUp(type) {\n                if (!gameState.player.activePowerUps) gameState.player.activePowerUps = {};\n                const duration = 10000;\n                const currentTime = performance.now();\n                if (type === 'length') {\n                    const length = window.gameConfig.playerLightsaberLength * 1.8;\n                    gameState.player.lightsaberLength = length;\n                    // Apply to extra sabers\n                    gameState.player.extraSabers.forEach(s => s.lengthOverride = length);\n                    gameState.player.activePowerUps[type] = currentTime + duration;\n                } else if (type === 'width') {\n                    const width = 30;\n                    gameState.player.lightsaberWidth = width;\n                    // Apply to extra sabers\n                    gameState.player.extraSabers.forEach(s => s.widthOverride = width);\n                    gameState.player.activePowerUps[type] = currentTime + duration;\n                } else if (type === 'shield') {\n                    gameState.player.hasShield = true;\n                } else if (type === 'speed') {\n                    gameState.player.speedBoost = 2.0;\n                    gameState.player.activePowerUps[type] = currentTime + duration;\n                } else if (type === 'extra_saber') {\n                    addExtraSaber(gameState.player);\n                } else if (type === 'convert') {\n                    gameState.player.isConverting = true;\n                    gameState.player.activePowerUps[type] = currentTime + duration;\n                } else if (type === 'tnt') {\n                    spawnTNT(gameState.player.torso.position.x, gameState.player.torso.position.y);\n                }\n                playSound('victory_sfx', 0.5);\n            }\n\n            function spawnTNT(x, y) {\n                const barrel = Bodies.circle(x + (Math.random()-0.5)*100, y + (Math.random()-0.5)*100, 20, {\n                    friction: 0.5,\n                    restitution: 0.3,\n                    label: 'barrel'\n                });\n                gameState.explosiveBarrels.push({\n                    body: barrel,\n                    timer: -1,\n                    isExploded: false\n                });\n                World.add(engine.world, barrel);\n            }\n\n            function addExtraSaber(player) {\n                if (player.isArmDismembered) { \n                    player.isArmDismembered = false; \n                    player.hasWeapon = true;\n                    // Note: original weapon arm restoration might need more logic if fully dismembered, \n                    // but for this simplified ragdoll it works as a logical state reset.\n                }\n                \n                // Only allow extra saber on left forearm (since right is the main one)\n                const slots = [\n                    { body: player.leftForearm, name: 'leftArm' }\n                ];\n                \n                for (const slot of slots) {\n                    if (!player.extraSabers.find(s => s.slot === slot.name)) {\n                        const saberLen = window.gameConfig.playerLightsaberLength;\n                        const saber = Bodies.rectangle(slot.body.position.x, slot.body.position.y, saberLen, 12, { \n                            density: 0.0002, friction: 0.1, isSensor: true, label: 'lightsaber' \n                        });\n                        const constraint = Constraint.create({ \n                            bodyA: slot.body, \n                            bodyB: saber, \n                            pointA: { x: 0, y: 0 }, \n                            pointB: { x: -saberLen / 2, y: 0 }, \n                            stiffness: 0.08, \n                            damping: 0.05,\n                            length: 0 \n                        });\n                        saber.owner = player;\n                        player.extraSabers.push({ body: saber, constraint: constraint, slot: slot.name });\n                        World.add(engine.world, [saber, constraint]);\n                        break;\n                    }\n                }\n            }\n\n            function removePowerUp(type) {\n                delete gameState.player.activePowerUps[type];\n                if (type === 'length') {\n                    const len = window.gameConfig.playerLightsaberLength;\n                    gameState.player.lightsaberLength = len;\n                    gameState.player.extraSabers.forEach(s => s.lengthOverride = null);\n                }\n                else if (type === 'width') {\n                    gameState.player.lightsaberWidth = 12;\n                    gameState.player.extraSabers.forEach(s => s.widthOverride = null);\n                }\n                else if (type === 'shield') gameState.player.hasShield = false;\n                else if (type === 'speed') gameState.player.speedBoost = 1.0;\n                else if (type === 'convert') gameState.player.isConverting = false;\n            }\n\n            function drawPowerUps() {\n                gameState.powerUps.forEach(p => {\n                    ctx.save();\n                    let color = '#fff'; let label = '';\n                    if (p.type === 'length') { color = '#00ff00'; label = 'L'; }\n                    if (p.type === 'width') { color = '#ffff00'; label = 'W'; }\n                    if (p.type === 'shield') { color = '#0000ff'; label = 'S'; }\n                    if (p.type === 'speed') { color = '#ff00ff'; label = 'F'; }\n                    if (p.type === 'extra_saber') { color = '#00ffff'; label = '+'; }\n                    if (p.type === 'convert') { color = '#a020f0'; label = 'C'; }\n                    if (p.type === 'tnt') { color = '#ff4500'; label = 'T'; }\n                    ctx.shadowBlur = 15; ctx.shadowColor = color; ctx.fillStyle = color;\n                    ctx.globalAlpha = 0.6 + Math.sin(performance.now() / 200) * 0.2;\n                    ctx.beginPath(); ctx.arc(p.body.position.x, p.body.position.y, 20, 0, Math.PI * 2); ctx.fill();\n                    ctx.fillStyle = '#fff'; ctx.font = 'bold 20px Orbitron'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText(label, p.body.position.x, p.body.position.y);\n                    ctx.restore();\n                });\n            }\n\n            // Check if two bodies are colliding\n            function checkBodyCollision(bodyA, bodyB) {\n                const dx = bodyA.position.x - bodyB.position.x;\n                const dy = bodyA.position.y - bodyB.position.y;\n                const distance = Math.sqrt(dx * dx + dy * dy);\n                \n                // Simple radius-based collision\n                const radiusA = Math.max(bodyA.bounds.max.x - bodyA.bounds.min.x,\n                                        bodyA.bounds.max.y - bodyA.bounds.min.y) / 2;\n                const radiusB = Math.max(bodyB.bounds.max.x - bodyB.bounds.min.x,\n                                        bodyB.bounds.max.y - bodyB.bounds.min.y) / 2;\n                \n                return distance < (radiusA + radiusB);\n            }\n\n            // Dismember head\n            function dismemberHead(stickman) {\n                if (stickman.isHeadDismembered) return;\n                stickman.isHeadDismembered = true;\n                stickman.isAlive = false;\n                stickman.head.isDismembered = true;\n                \n                // Remove collision for the severed part to avoid repeat hits\n                stickman.head.collisionFilter.mask = 0;\n                stickman.head.isSensor = true;\n\n                // Apply impulse to head\n                Body.applyForce(stickman.head, stickman.head.position, {\n                    x: (Math.random() - 0.5) * 0.05,\n                    y: (Math.random() - 0.5) * 0.05\n                });\n            }\n\n            // Dismember weapon arm\n            function dismemberWeaponArm(stickman) {\n                if (stickman.isArmDismembered) return;\n                stickman.isArmDismembered = true;\n                stickman.hasWeapon = false;\n                stickman.rightForearm.isDismembered = true;\n                stickman.rightUpperArm.isDismembered = true;\n\n                // Disable collision for the arm parts\n                stickman.rightForearm.collisionFilter.mask = 0;\n                stickman.rightUpperArm.collisionFilter.mask = 0;\n                stickman.rightForearm.isSensor = true;\n                stickman.rightUpperArm.isSensor = true;\n\n                // Apply impulse to forearm\n                Body.applyForce(stickman.rightForearm, stickman.rightForearm.position, {\n                    x: (Math.random() - 0.5) * 0.03,\n                    y: (Math.random() - 0.5) * 0.03\n                });\n            }\n\n            // Create impact flash effect\n            function createImpactFlash(x, y) {\n                gameState.impactFlashes.push({\n                    x: x,\n                    y: y,\n                    alpha: 1.0,\n                    scale: 0.5,\n                    lifetime: 0.2\n                });\n            }\n\n            // Update impact flashes\n            function updateImpactFlashes(deltaTime) {\n                for (let i = gameState.impactFlashes.length - 1; i >= 0; i--) {\n                    const flash = gameState.impactFlashes[i];\n                    flash.lifetime -= deltaTime;\n                    flash.alpha = flash.lifetime / 0.2;\n                    flash.scale = 0.5 + (1 - flash.alpha) * 0.5;\n\n                    if (flash.lifetime <= 0) {\n                        gameState.impactFlashes.splice(i, 1);\n                    }\n                }\n            }\n\n            // Enemy AI\n            function updateEnemyAI(enemy, deltaTime) {\n                if (!enemy || !enemy.isAlive) return;\n                const currentTime = performance.now() / 1000;\n                const target = enemy.isConverted ? \n                    (gameState.enemies.find(e => !e.isConverted && e.isAlive) || { torso: { position: { x: canvas.width/2, y: canvas.height/2 } } }) : \n                    gameState.player;\n                if (currentTime > enemy.aiState.changeTargetTime) {\n                    if (target && (target.isPlayer || target.isAlive)) {\n                        const targetPos = target.torso ? target.torso.position : target.position;\n                        const dx = targetPos.x - enemy.torso.position.x;\n                        const dy = targetPos.y - enemy.torso.position.y;\n                        const dist = Math.sqrt(dx*dx + dy*dy);\n                        if (dist > 100) {\n                            enemy.aiState.targetX = targetPos.x + (Math.random() - 0.5) * 100;\n                            enemy.aiState.targetY = targetPos.y + (Math.random() - 0.5) * 100;\n                        } else {\n                            const angle = Math.atan2(dy, dx) + (Math.random() - 0.5) * Math.PI;\n                            enemy.aiState.targetX = targetPos.x + Math.cos(angle) * 80;\n                            enemy.aiState.targetY = targetPos.y + Math.sin(angle) * 80;\n                        }\n                    }\n                    enemy.aiState.changeTargetTime = currentTime + 0.5 + Math.random() * 1.0;\n                }\n                const dx = enemy.aiState.targetX - enemy.torso.position.x;\n                const dy = enemy.aiState.targetY - enemy.torso.position.y;\n                const dist = Math.sqrt(dx*dx + dy*dy);\n                if (dist > 10) {\n                    const force = 0.0003 * window.gameConfig.enemySpeedMultiplier;\n                    Body.applyForce(enemy.torso, enemy.torso.position, { x: (dx / dist) * force, y: (dy / dist) * force });\n                }\n            }\n\n            // Apply player movement\n            function applyPlayerMovement() {\n                if (!gameState.player.isAlive) return;\n\n                let moveX = 0;\n                let moveY = 0;\n\n                // Joystick input\n                if (input.joystick.active) {\n                    moveX = input.joystick.x;\n                    moveY = input.joystick.y;\n                }\n                // Keyboard input\n                else {\n                    if (input.keys.w || input.keys.up) moveY -= 1;\n                    if (input.keys.s || input.keys.down) moveY += 1;\n                    if (input.keys.a || input.keys.left) moveX -= 1;\n                    if (input.keys.d || input.keys.right) moveX += 1;\n\n                    // Normalize diagonal movement\n                    const magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n                    if (magnitude > 0) {\n                        moveX /= magnitude;\n                        moveY /= magnitude;\n                    }\n                }\n\n                // Apply force to torso\n                if (moveX !== 0 || moveY !== 0) {\n                    const baseForce = 0.0005; // \u56de\u8c03\u57fa\u7840\u901f\u5ea6\uff0c\u7531\u4e4b\u524d\u7684 0.0008 \u6539\u56de 0.0005\n                    const speedMultiplier = gameState.player.speedBoost || 1.0;\n                    const force = baseForce * speedMultiplier;\n                    \n                    Body.applyForce(gameState.player.torso, gameState.player.torso.position, {\n                        x: moveX * force,\n                        y: moveY * force\n                    });\n\n                    // Play swing sound occasionally when moving fast\n                    const velocity = Vector.magnitude(gameState.player.torso.velocity);\n                    const currentTime = performance.now() / 1000;\n                    if (velocity > 4 && currentTime - gameState.player.lastSwingTime > 0.3) {\n                        playSound('lightsaber_swing_sfx', 0.3);\n                        gameState.player.lastSwingTime = currentTime;\n                    }\n                }\n\n                // Mouse control for lightsaber\n                if (input.mouse.active) {\n                    const playerPos = gameState.player.torso.position;\n                    const dx = input.mouse.x - playerPos.x;\n                    const dy = input.mouse.y - playerPos.y;\n                    const targetAngle = Math.atan2(dy, dx);\n                    \n                    // Control ALL lightsabers owned\n                    const sabersToControl = [];\n                    if (gameState.player.lightsaber && gameState.player.hasWeapon) {\n                        sabersToControl.push(gameState.player.lightsaber);\n                    }\n                    if (gameState.player.extraSabers) {\n                        gameState.player.extraSabers.forEach(s => sabersToControl.push(s.body));\n                    }\n\n                    sabersToControl.forEach(saber => {\n                        const forceMag = 0.00015;\n                        Body.applyForce(saber, saber.position, {\n                            x: Math.cos(targetAngle) * forceMag,\n                            y: Math.sin(targetAngle) * forceMag\n                        });\n                        \n                        const angleDiff = targetAngle - saber.angle;\n                        const normalizedDiff = Math.atan2(Math.sin(angleDiff), Math.cos(angleDiff));\n                        saber.torque = normalizedDiff * 0.05;\n                    });\n                }\n            }\n\n            function drawStickman(stickman) {\n                const color = stickman.isPlayer ? '#e0e0e0' : (stickman.isConverted ? '#a020f0' : '#c0c0c0');\n                let lColor = stickman.isPlayer ? window.gameConfig.playerLightsaberColor : window.gameConfig.enemyLightsaberColor;\n                if (stickman.isConverted || (stickman.isPlayer && stickman.isConverting)) lColor = '#a020f0';\n                if (stickman.customSaberColor) lColor = stickman.customSaberColor;\n                ctx.strokeStyle = color; ctx.fillStyle = color; ctx.lineWidth = 3; ctx.lineCap = 'round';\n                const limbs = [[stickman.leftUpperArm, stickman.leftForearm], [stickman.rightUpperArm, stickman.rightForearm], [stickman.leftUpperLeg, stickman.leftLowerLeg], [stickman.rightUpperLeg, stickman.rightLowerLeg]];\n                limbs.forEach(([u, l]) => { if (u && l) { ctx.beginPath(); ctx.moveTo(u.position.x, u.position.y); ctx.lineTo(l.position.x, l.position.y); ctx.stroke(); } });\n                if (stickman.torso) {\n                    const angle = stickman.torso.angle;\n                    ctx.save(); ctx.translate(stickman.torso.position.x, stickman.torso.position.y); ctx.rotate(angle); ctx.fillRect(-4, -20, 8, 40); ctx.restore();\n                }\n                if (stickman.head && stickman.isAlive) {\n                    ctx.beginPath(); ctx.arc(stickman.head.position.x, stickman.head.position.y, 10, 0, Math.PI * 2); ctx.fill();\n                    if (stickman.isPlayer && stickman.hasShield) { ctx.save(); ctx.strokeStyle = '#0000ff'; ctx.lineWidth = 4; ctx.setLineDash([5, 5]); ctx.beginPath(); ctx.arc(stickman.head.position.x, stickman.head.position.y, 25, 0, Math.PI * 2); ctx.stroke(); ctx.restore(); }\n                }\n                const dS = (s, len, thick) => {\n                    if (!s) return; const ang = s.angle;\n                    ctx.save(); ctx.globalAlpha = 0.3; ctx.strokeStyle = lColor; ctx.lineWidth = thick + 11; ctx.beginPath(); ctx.moveTo(s.position.x - Math.cos(ang) * len / 2, s.position.y - Math.sin(ang) * len / 2); ctx.lineTo(s.position.x + Math.cos(ang) * len / 2, s.position.y + Math.sin(ang) * len / 2); ctx.stroke(); ctx.restore();\n                    ctx.strokeStyle = lColor; ctx.lineWidth = thick; ctx.beginPath(); ctx.moveTo(s.position.x - Math.cos(ang) * len / 2, s.position.y - Math.sin(ang) * len / 2); ctx.lineTo(s.position.x + Math.cos(ang) * len / 2, s.position.y + Math.sin(ang) * len / 2); ctx.stroke();\n                };\n                if (stickman.hasWeapon && stickman.lightsaber) dS(stickman.lightsaber, stickman.lightsaberLength || 80, stickman.lightsaberWidth || 12);\n                if (stickman.isPlayer && stickman.extraSabers) {\n                    stickman.extraSabers.forEach(s => {\n                        const len = s.lengthOverride || window.gameConfig.playerLightsaberLength;\n                        const thick = s.widthOverride || stickman.lightsaberWidth || 12;\n                        dS(s.body, len, thick);\n                    });\n                }\n            }\n            function drawTraps() {\n                gameState.traps.forEach(t => {\n                    ctx.save(); ctx.translate(t.x, t.y); ctx.rotate(t.angle); ctx.fillStyle = '#666'; ctx.beginPath(); ctx.arc(0, 0, 15, 0, Math.PI * 2); ctx.fill();\n                    ctx.strokeStyle = '#ff0000'; ctx.lineWidth = 6; ctx.shadowBlur = 15; ctx.shadowColor = '#ff0000'; ctx.beginPath(); ctx.moveTo(0, 0); ctx.lineTo(t.length, 0); ctx.stroke(); ctx.restore();\n                });\n            }\n            function drawBarrels() {\n                gameState.explosiveBarrels.forEach(b => {\n                    if (b.isExploded) return;\n                    ctx.save(); ctx.translate(b.body.position.x, b.body.position.y); ctx.rotate(b.body.angle);\n                    ctx.fillStyle = b.timer > 0 ? (Math.floor(performance.now() / 100) % 2 === 0 ? '#ff0000' : '#8b0000') : '#b22222';\n                    ctx.beginPath(); ctx.arc(0, 0, 20, 0, Math.PI * 2); ctx.fill(); ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke();\n                    ctx.fillStyle = '#fff'; ctx.font = 'bold 14px Orbitron'; ctx.textAlign = 'center'; ctx.fillText('TNT', 0, 5); ctx.restore();\n                });\n            }\n            function updateTraps(dt) {\n                gameState.traps.forEach(t => {\n                    t.angle += t.rotationSpeed;\n                    const cL = (tgt) => {\n                        if (!tgt || !tgt.isAlive) return;\n                        const p1 = { x: t.x, y: t.y }; const p2 = { x: t.x + Math.cos(t.angle) * t.length, y: t.y + Math.sin(t.angle) * t.length };\n                        const c = tgt.head.position; const l2 = (p2.x - p1.x)**2 + (p2.y - p1.y)**2;\n                        let r = ((c.x - p1.x) * (p2.x - p1.x) + (c.y - p1.y) * (p2.y - p1.y)) / l2;\n                        r = Math.max(0, Math.min(1, r)); const pr = { x: p1.x + r * (p2.x - p1.x), y: p1.y + r * (p2.y - p1.y) };\n                        if ((c.x - pr.x)**2 + (c.y - pr.y)**2 < (13)**2) {\n                            if (tgt.isPlayer && tgt.hasShield) removePowerUp('shield');\n                            else { dismemberHead(tgt); if (tgt.isPlayer) endGame(false); }\n                        }\n                    };\n                    cL(gameState.player); gameState.enemies.forEach(cL);\n                });\n            }\n            function updateBarrels(dt) {\n                for (let b of gameState.explosiveBarrels) { if (!b.isExploded && b.timer > 0) { b.timer -= dt; if (b.timer <= 0) explodeBarrel(b); } }\n            }\n            function explodeBarrel(b) {\n                b.isExploded = true; const p = b.body.position; playSound('dismember_sfx', 1.0); createImpactFlash(p.x, p.y);\n                const cE = (tgt) => {\n                    if (!tgt || !tgt.isAlive) return;\n                    if (Math.sqrt((tgt.torso.position.x-p.x)**2 + (tgt.torso.position.y-p.y)**2) < 150) {\n                        if (tgt.isPlayer && tgt.hasShield) removePowerUp('shield');\n                        else { dismemberHead(tgt); if (tgt.isPlayer) endGame(false); }\n                    }\n                };\n                cE(gameState.player); gameState.enemies.forEach(cE); World.remove(engine.world, b.body);\n            }\n            function drawCoverObjects() {\n                ctx.fillStyle = '#444';\n                ctx.strokeStyle = '#666';\n                ctx.lineWidth = 2;\n                gameState.coverObjects.forEach(cover => {\n                    const width = cover.bounds.max.x - cover.bounds.min.x;\n                    const height = cover.bounds.max.y - cover.bounds.min.y;\n                    ctx.fillRect(cover.position.x - width / 2, cover.position.y - height / 2, width, height);\n                    ctx.strokeRect(cover.position.x - width / 2, cover.position.y - height / 2, width, height);\n                });\n            }\n\n            // Draw impact flashes\n            function drawImpactFlashes() {\n                const flashImg = assetCache.lightsaber_impact_flash;\n                if (!flashImg) return;\n\n                gameState.impactFlashes.forEach(flash => {\n                    ctx.save();\n                    ctx.globalAlpha = flash.alpha;\n                    const size = 64 * flash.scale;\n                    ctx.drawImage(\n                        flashImg,\n                        flash.x - size / 2,\n                        flash.y - size / 2,\n                        size,\n                        size\n                    );\n                    ctx.restore();\n                });\n            }\n\n            // Setup controls\n            function setupControls() {\n                const joystickContainer = document.getElementById('joystick');\n                const joystickStick = joystickContainer.querySelector('.joystick-stick');\n                const maxRadius = 60;\n                const deadZone = 15;\n\n                // Handle background clicks for unpausing\n                document.getElementById('paused-overlay').addEventListener('click', (e) => {\n                    if (gameState.isPaused) {\n                        gameState.isPaused = false;\n                        document.getElementById('paused-overlay').classList.remove('visible');\n                    }\n                });\n\n                // Add a specific Resume UI element to make it clearer\n                const pausedOverlay = document.getElementById('paused-overlay');\n                if (!document.getElementById('resume-btn')) {\n                    const resumeBtn = document.createElement('div');\n                    resumeBtn.id = 'resume-btn';\n                    resumeBtn.style.marginTop = '40px';\n                    resumeBtn.style.padding = '20px 40px';\n                    resumeBtn.style.background = 'rgba(0, 255, 255, 0.2)';\n                    resumeBtn.style.border = '2px solid #00ffff';\n                    resumeBtn.style.color = '#00ffff';\n                    resumeBtn.style.fontFamily = 'Orbitron';\n                    resumeBtn.style.fontSize = '24px';\n                    resumeBtn.style.cursor = 'pointer';\n                    resumeBtn.textContent = 'RESUME';\n                    pausedOverlay.appendChild(resumeBtn);\n                }\n\n                // Touch controls\n                canvas.addEventListener('touchstart', (e) => {\n                    if (gameState.isPaused || gameState.isGameOver) return;\n                    if (input.touchId !== null) return;\n\n                    const touch = e.touches[0];\n                    const rect = canvas.getBoundingClientRect();\n                    const y = touch.clientY - rect.top;\n\n                    // Only activate joystick in bottom 2/3 of screen\n                    if (y < canvas.height * 0.33) return;\n\n                    input.touchId = touch.identifier;\n                    \n                    joystickContainer.style.left = touch.clientX + 'px';\n                    joystickContainer.style.top = touch.clientY + 'px';\n                    joystickContainer.style.display = 'block';\n                    \n                    joystickStick.style.left = '50%';\n                    joystickStick.style.top = '50%';\n                    \n                    input.joystick.active = true;\n                    input.joystick.x = 0;\n                    input.joystick.y = 0;\n\n                    e.preventDefault();\n                });\n\n                canvas.addEventListener('touchmove', (e) => {\n                    if (input.touchId === null) return;\n\n                    const touch = Array.from(e.touches).find(t => t.identifier === input.touchId);\n                    if (!touch) return;\n\n                    const rect = canvas.getBoundingClientRect();\n                    const baseX = parseFloat(joystickContainer.style.left);\n                    const baseY = parseFloat(joystickContainer.style.top);\n                    \n                    let dx = touch.clientX - baseX;\n                    let dy = touch.clientY - baseY;\n                    \n                    const distance = Math.sqrt(dx * dx + dy * dy);\n                    \n                    if (distance < deadZone) {\n                        input.joystick.x = 0;\n                        input.joystick.y = 0;\n                        joystickStick.style.left = '50%';\n                        joystickStick.style.top = '50%';\n                    } else {\n                        if (distance > maxRadius) {\n                            dx = (dx / distance) * maxRadius;\n                            dy = (dy / distance) * maxRadius;\n                        }\n                        \n                        input.joystick.x = dx / maxRadius;\n                        input.joystick.y = dy / maxRadius;\n                        \n                        joystickStick.style.left = `calc(50% + ${dx}px)`;\n                        joystickStick.style.top = `calc(50% + ${dy}px)`;\n                    }\n\n                    e.preventDefault();\n                });\n\n                function endTouch() {\n                    input.touchId = null;\n                    input.joystick.active = false;\n                    input.joystick.x = 0;\n                    input.joystick.y = 0;\n                    joystickContainer.style.display = 'none';\n                }\n\n                canvas.addEventListener('touchend', (e) => {\n                    const touch = Array.from(e.changedTouches).find(t => t.identifier === input.touchId);\n                    if (touch) {\n                        endTouch();\n                    }\n                });\n\n                canvas.addEventListener('touchcancel', endTouch);\n\n                // Mouse controls for lightsaber\n                canvas.addEventListener('mousedown', (e) => {\n                    if (gameState.isPaused || gameState.isGameOver) return;\n                    input.mouse.active = true;\n                    updateMousePos(e);\n                });\n\n                window.addEventListener('mousemove', (e) => {\n                    if (input.mouse.active) {\n                        updateMousePos(e);\n                    }\n                });\n\n                window.addEventListener('mouseup', () => {\n                    input.mouse.active = false;\n                });\n\n                function updateMousePos(e) {\n                    const rect = canvas.getBoundingClientRect();\n                    input.mouse.x = e.clientX - rect.left;\n                    input.mouse.y = e.clientY - rect.top;\n                }\n\n                // Keyboard controls\n                window.addEventListener('keydown', (e) => {\n                    if (gameState.isPaused || gameState.isGameOver || gameState.isTutorialActive) return;\n\n                    const key = e.key.toLowerCase();\n                    if (key in input.keys) {\n                        input.keys[key] = true;\n                        e.preventDefault();\n                    }\n                });\n\n                window.addEventListener('keyup', (e) => {\n                    const key = e.key.toLowerCase();\n                    if (key in input.keys) {\n                        input.keys[key] = false;\n                        e.preventDefault();\n                    }\n                });\n\n                // Pause button\n                document.getElementById('pause-btn').addEventListener('click', () => {\n                    if (gameState.isGameOver) return;\n                    \n                    gameState.isPaused = !gameState.isPaused;\n                    document.getElementById('paused-overlay').classList.toggle('visible', gameState.isPaused);\n                });\n\n                // Retry button (General listener for the HTML element)\n                document.getElementById('retry-btn').addEventListener('click', (e) => {\n                    // This listener is often overridden by endGame logic, \n                    // but we ensure it doesn't conflict with initial game starts.\n                    if (!gameState.isGameOver) {\n                        initGame();\n                    }\n                });\n\n                // Tutorial controls\n                const tutorialOverlay = document.getElementById('tutorial-overlay');\n                const tutorialNextBtn = document.getElementById('tutorial-next-btn');\n                \n                tutorialNextBtn.addEventListener('click', () => {\n                    // Just close the overlay\n                    tutorialOverlay.classList.remove('visible');\n                    \n                    // If we just finished the info step, start the game\n                    if (gameState.tutorialStage === 'items_info') {\n                        gameState.tutorialStage = 'complete';\n                        gameState.isTutorialActive = false;\n                        spawnLevelEnemies();\n                        gameState.levelStartTime = performance.now();\n                    } else {\n                        // For other steps, we stay in tutorial mode until task is done\n                        gameState.isTutorialActive = false;\n                    }\n                });\n\n                // Clear input on blur\n                window.addEventListener('blur', () => {\n                    Object.keys(input.keys).forEach(key => input.keys[key] = false);\n                    endTouch();\n                });\n            }\n\n            if (mode === 'edit') {\n                // Edit mode: Show parameters, pause physics\n                initGame();\n                \n                // Pause physics\n                engine.timing.timeScale = 0;\n\n                // Simple render loop for edit mode\n                function editLoop() {\n                    ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                    // Draw background\n                    const bg = assetCache.arena_background_v2 || assetCache.arena_background;\n                    if (bg) {\n                        // Tile the background or scale to cover\n                        ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);\n                    } else {\n                        // Fallback background\n                        ctx.fillStyle = '#1a1a2e';\n                        ctx.fillRect(0, 0, canvas.width, canvas.height);\n                    }\n\n                    // Draw cover objects\n                    drawCoverObjects();\n\n                    // Draw stickmen\n                    if (gameState.player) {\n                        drawStickman(gameState.player);\n                    }\n                    gameState.enemies.forEach(enemy => drawStickman(enemy));\n\n                    requestAnimationFrame(editLoop);\n                }\n                requestAnimationFrame(editLoop);\n\n            } else {\n                // Play mode: Full game logic\n                initGame();\n                \n                // Apply saved state if it exists (restore progress after initGame)\n                if (savedState) {\n                    gameState.currentLevel = savedState.currentLevel || 1;\n                    gameState.score = savedState.score || 0;\n                    gameState.levelStartScore = savedState.score || 0;\n                    lib.log('Restored game progress to Level', gameState.currentLevel, 'with score', gameState.score);\n                    \n                    // Re-initialize with the restored level (skip tutorial if loading from saved state)\n                    if (gameState.currentLevel > 1) {\n                        gameState.isTutorialActive = false;\n                        initGame(false);\n                    }\n                }\n                \n                setupControls();\n\n                let lastFrameTime = performance.now();\n\n                function gameLoop(timestamp) {\n                    const deltaTime = Math.min((timestamp - lastFrameTime) / 1000, 0.11);\n                    lastFrameTime = timestamp;\n\n                    // Clear canvas\n                    ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                    // Draw background\n                    const bg = assetCache.arena_background_v2 || assetCache.arena_background;\n                    if (bg) {\n                        ctx.drawImage(bg, 0, 0, canvas.width, canvas.height);\n                    } else {\n                        // Fallback background\n                        ctx.fillStyle = '#1a1a2e';\n                        ctx.fillRect(0, 0, canvas.width, canvas.height);\n                    }\n\n                    if (!gameState.isPaused && !gameState.isGameOver) {\n                        // Update physics\n                        Engine.update(engine, deltaTime * 1000);\n\n                        // Tutorial progression checks\n                        if (gameState.currentLevel === 1 && !gameState.isTutorialActive) {\n                            const tutOverlay = document.getElementById('tutorial-overlay');\n                            const tutBtn = document.getElementById('tutorial-next-btn');\n                            \n                            if (tutOverlay && !tutOverlay.classList.contains('visible')) {\n                                if (gameState.tutorialStage === 'move') {\n                                    if (gameState.tutorialTarget && checkBodyCollision(gameState.player.torso, gameState.tutorialTarget)) {\n                                        initTutorialStage('combat');\n                                        document.querySelectorAll('.tutorial-step').forEach(s => s.classList.remove('active'));\n                                        document.getElementById('step-2').classList.add('active');\n                                        tutOverlay.classList.add('visible');\n                                    }\n                                } else if (gameState.tutorialStage === 'combat') {\n                                    if (gameState.enemies.length > 0 && !gameState.enemies[0].isAlive) {\n                                        initTutorialStage('items');\n                                        document.querySelectorAll('.tutorial-step').forEach(s => s.classList.remove('active'));\n                                        document.getElementById('step-3').classList.add('active');\n                                        if (tutBtn) tutBtn.textContent = 'LET\\'S GO';\n                                        tutOverlay.classList.add('visible');\n                                    }\n                                } else if (gameState.tutorialStage === 'items') {\n                                    if (gameState.powerUps.length === 0) {\n                                        initTutorialStage('items_info');\n                                        document.querySelectorAll('.tutorial-step').forEach(s => s.classList.remove('active'));\n                                        document.getElementById('step-4').classList.add('active');\n                                        if (tutBtn) tutBtn.textContent = 'START BATTLE';\n                                        tutOverlay.classList.add('visible');\n                                    }\n                                } else if (gameState.tutorialStage === 'items_info') {\n                                    // Step 4 is now the final blocking step.\n                                    // It only proceeds to the main game when the user clicks 'GOT IT'\n                                    // in the tutorial-next-btn event listener.\n                                    return; \n                                }\n                            }\n                        }\n\n                        // Apply player movement\n                        applyPlayerMovement();\n\n                        // Update enemy AI\n                        gameState.enemies.forEach(enemy => updateEnemyAI(enemy, deltaTime));\n\n                        // Update traps and barrels\n                        updateTraps(deltaTime);\n                        updateBarrels(deltaTime);\n\n                        // Update power-ups\n                        updatePowerUps(deltaTime);\n\n                        // Check collisions\n                        checkLightsaberCollisions();\n\n                        // Update impact flashes\n                        updateImpactFlashes(deltaTime);\n\n                        // Update timer\n                        gameState.timeRemaining -= deltaTime;\n                        if (gameState.timeRemaining <= 0) {\n                            gameState.timeRemaining = 0;\n                            endGame(true);\n                        }\n\n                // Check victory condition: all enemies killed\n                const anyHostileAlive = gameState.enemies.some(enemy => enemy.isAlive && !enemy.isConverted);\n                if (gameState.enemies.length > 0 && !anyHostileAlive) {\n                    endGame(true);\n                }\n\n                        // Update UI\n                        updateUI();\n                    }\n\n                    // Draw cover objects\n                    drawCoverObjects();\n                    drawBarrels();\n                    drawTraps();\n\n                    // Draw tutorial marker\n                    if (gameState.currentLevel === 1 && gameState.tutorialStage === 'move' && gameState.tutorialTarget) {\n                        ctx.save();\n                        ctx.fillStyle = '#ffff00';\n                        ctx.shadowBlur = 20;\n                        ctx.shadowColor = '#ffff00';\n                        ctx.globalAlpha = 0.5 + Math.sin(performance.now() / 200) * 0.3;\n                        ctx.beginPath();\n                        ctx.arc(gameState.tutorialTarget.position.x, gameState.tutorialTarget.position.y, 40, 0, Math.PI * 2);\n                        ctx.fill();\n                        ctx.restore();\n                    }\n\n                    // Draw stickmen\n                    if (gameState.player) {\n                        drawStickman(gameState.player);\n                    }\n                    gameState.enemies.forEach(enemy => drawStickman(enemy));\n\n                    // Draw impact flashes\n                    drawImpactFlashes();\n\n                    // Draw power-ups\n                    drawPowerUps();\n\n                    requestAnimationFrame(gameLoop);\n                }\n                requestAnimationFrame(gameLoop);\n            }\n        }\n    </script>\n</body>\n</html>", "game_config": "{\n  \"waveDuration\": 5,\n  \"startingEnemyCount\": 2,\n  \"enemySpeedMultiplier\": 1.0,\n  \"limbFloppiness\": 0.5,\n  \"playerLightsaberLength\": 80,\n  \"criticalHitMultiplier\": 3.0,\n  \"playerLightsaberColor\": \"#00ffff\",\n  \"enemyLightsaberColor\": \"#ff00ff\",\n  \"numCoverObjects\": 3,\n  \"startingLevel\": 1\n}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Moment-to-Moment Gameplay:**\nPlayers control a stickman character with exaggerated ragdoll physics similar to Human Fall Flat. The character's limbs and lightsaber flail unpredictably, creating chaotic and humorous combat situations. Players swipe on the screen to move their stickman around the arena, and the physics system handles the rest - limbs swing, the lightsaber wobbles, and every movement feels loose and unpredictable.\n\n**Combat System:**\n- **Dismemberment Mechanics**: \n  - Cutting off the head = instant kill (bonus \"critical hit\" score multiplier)\n  - Cutting off the weapon arm = enemy loses attack capability but can still move\n  - Body hits cause damage but don't disable\n- **Physics-Based Attacks**: The lightsaber swings based on momentum and character movement. Players must learn to use the floppy physics to their advantage, timing movements to create effective swings\n- **Collision Detection**: When a lightsaber blade intersects with a body part (head, arms, torso), it triggers the appropriate effect\n\n**Progression:**\n- Survive for 5 minutes to complete a wave\n- Each new wave increases enemy count (Wave 1: 2 enemies, Wave 2: 3 enemies, Wave 3: 4 enemies, etc.)\n- Elite enemies with longer lightsabers spawn periodically (every 60-90 seconds)\n- Random cover objects spawn throughout the arena for tactical positioning\n\n**Scoring System:**\n- Base points for damage dealt\n- Bonus multiplier for one-hit kills (headshots)\n- Score penalty for taking damage\n- Survival time bonus\n- Final score displayed at wave completion\n\n**Win/Loss Conditions:**\n- Win: Survive 5 minutes\n- Loss: Player's head is cut off or health depletes to zero\n\n**Engagement Drivers:**\n- Unpredictable physics creates emergent comedy and challenge\n- Risk/reward of going for headshots vs. safe body hits\n- Tactical use of cover and positioning\n- Increasing difficulty keeps players on edge\n- Score chasing for perfect runs\n\n## Starting Scope\n\n**First Iteration (Minimal Viable Product):**\n1. **Single Arena**: One fixed-screen arena with boundaries\n2. **Basic Physics**: Implement ragdoll physics for player and 2-3 enemy stickmen with floppy limbs and lightsaber\n3. **Core Combat**: \n   - Swipe movement controls\n   - Lightsaber collision detection\n   - Head removal = instant kill\n   - Weapon arm removal = disarm\n4. **One Wave**: 5-minute survival against 2-3 enemies\n5. **Simple Scoring**: Track kills and survival time\n6. **Basic Cover**: 2-3 static rectangular cover objects\n7. **Simple UI**: Timer, kill count, basic game over screen\n\n**Future Iterations:**\n- Multiple waves with increasing difficulty\n- Elite enemies with longer weapons\n- Dynamic cover spawning\n- Advanced scoring system with damage tracking\n- Health bars and visual damage indicators\n- Particle effects for lightsaber trails and impacts\n- More varied arena layouts\n- Power-ups and weapon variety\n- Replay system\n\n## Game Perspective\n\n**Fixed-Screen Top-Down View**\n\nThe game uses a fixed-screen, top-down perspective showing the entire arena at once. This perspective is ideal because:\n- Players can see all enemies and cover simultaneously\n- Physics-based movement is easier to control from above\n- Limb flailing and ragdoll effects are clearly visible\n- No camera management needed - focus stays on chaotic action\n- Works perfectly within the 720\u00d71280 mobile viewport\n\nThe arena fits entirely within the screen, with the player stickman and enemies clearly visible as they flop around the battlefield.\n\n## Game World and Level Design\n\n**Arena Layout:**\n- **Dimensions**: Approximately 600\u00d7900 pixel play area (leaving room for UI)\n- **Boundaries**: Solid walls on all sides to contain the action\n- **Floor**: Simple textured ground (can be rendered with CSS or simple canvas pattern)\n- **Cover Objects**: \n  - 2-3 rectangular obstacles (100\u00d750 pixels each)\n  - Randomly positioned at arena start\n  - Provide tactical hiding spots and block lightsaber swings\n  - Rendered as simple colored rectangles with borders\n\n**Stickman Design:**\n- **Body Structure**: \n  - Head: 20px circle\n  - Torso: 40px vertical line\n  - Arms: 30px lines (2 segments each - upper arm, forearm)\n  - Legs: 35px lines (2 segments each - thigh, shin)\n  - Total height when standing: ~120px\n- **Lightsaber**: 80px glowing line extending from hand, 4px thick\n- **Physics Joints**: All limbs connected with loose spring joints for floppy movement\n\n**Enemy Variations:**\n- **Regular Enemies**: Same size as player, standard 80px lightsaber\n- **Elite Enemies**: Slightly larger (1.3x scale), 120px lightsaber, different color scheme\n\n## Edit Mode Design\n\n**Editable Properties:**\n\n1. **Arena Settings:**\n   - Arena size (width/height within reasonable bounds)\n   - Number of cover objects (0-6)\n   - Cover object size range\n\n2. **Difficulty Settings:**\n   - Starting enemy count (1-5)\n   - Wave duration (3-10 minutes)\n   - Elite spawn frequency (30-120 seconds)\n   - Enemy movement speed multiplier (0.5-2.0)\n\n3. **Physics Settings:**\n   - Limb floppiness (joint stiffness: 0.1-1.0)\n   - Lightsaber length for player (60-120px)\n   - Gravity strength (affects how limbs fall)\n\n4. **Visual Settings:**\n   - Player lightsaber color (RGB picker)\n   - Enemy lightsaber color (RGB picker)\n   - Elite lightsaber color (RGB picker)\n   - Arena background color\n\n5. **Scoring Settings:**\n   - Critical hit multiplier (1.5-5.0x)\n   - Damage penalty multiplier (0.5-2.0x)\n\n**Constraints:**\n- Minimum 1 enemy, maximum 8 enemies to prevent performance issues\n- Lightsaber length capped at 150px to maintain balance\n- Arena must be at least 400\u00d7600px to be playable\n- Physics values bounded to prevent breaking the simulation\n\n## Control Design\n\n**Mobile (Touch) - Primary Platform:**\n\n1. **Movement Control:**\n   - Single virtual joystick appears where player touches the screen (anywhere in bottom 2/3 of screen)\n   - Joystick controls stickman's center of mass movement\n   - Joystick base moves with finger if dragged beyond max radius (120px)\n   - Dead zone: 15px\n   - Movement is analog (-1 to +1 on X and Y axes)\n   - Stickman accelerates in the direction of joystick input\n   - Limbs and lightsaber flail based on physics as the body moves\n\n2. **Attack Mechanics:**\n   - Attacks are automatic based on movement and physics\n   - Rapid direction changes cause more violent lightsaber swings\n   - Players learn to \"whip\" the lightsaber by moving in specific patterns\n   - No separate attack button - all combat emerges from movement physics\n\n3. **UI Interactions:**\n   - Top 1/3 of screen reserved for UI (timer, score, pause button)\n   - Pause button in top-right corner (60\u00d760px)\n   - Touches in UI area don't create joystick\n\n**Desktop (Keyboard + Mouse) - Secondary Support:**\n\n1. **Movement:**\n   - WASD or Arrow keys for 8-directional movement\n   - Keys apply force to stickman's center of mass\n   - Multiple keys can be pressed for diagonal movement\n   - Movement is digital but smoothed through physics\n\n2. **Attack Mechanics:**\n   - Same as mobile - attacks emerge from movement physics\n   - Mouse position could optionally influence body orientation (stretch goal)\n\n3. **Other Controls:**\n   - ESC or P to pause\n   - Space bar could add a \"dash\" move (future iteration)\n\n**Input Handling:**\n- Track touch IDs to prevent multi-touch interference\n- Clear all input states on window blur\n- Prevent default browser behaviors for used keys\n- Release pointer lock when pausing (if implemented)\n\n**Control Philosophy:**\nThe control scheme embraces the chaotic physics. Players don't directly control limbs or attacks - they control the center of mass, and the ragdoll physics does the rest. This creates emergent gameplay where mastery comes from understanding how to manipulate the floppy physics system, not from precise button timing.\n\n## Art Style and Visual Theme\n\n**Overall Style: Minimalist Stick Figure with Neon Accents**\n\nThe game embraces a clean, minimalist aesthetic that puts the focus on the chaotic physics and glowing lightsabers. Think \"stick figure animator meets neon arcade.\"\n\n**Visual Theme:**\n- **Mood**: Energetic, arcade-like, slightly comedic\n- **Color Palette**: \n  - Dark background (deep blue-gray #1a1a2e)\n  - Bright neon lightsabers (cyan #00ffff, magenta #ff00ff, yellow #ffff00)\n  - White/light gray stickmen (#e0e0e0)\n  - Subtle grid pattern on floor for depth\n- **Lighting**: Lightsabers cast subtle glow effects on nearby objects\n\n**Asset vs. Code Rendering:**\n\n**Code-Rendered Elements (Canvas/CSS):**\n- Stickman bodies (simple lines and circles)\n- Lightsaber blades (glowing lines with gradient)\n- Arena boundaries (simple rectangles)\n- Cover objects (rectangles with borders)\n- UI elements (timer, score, buttons)\n- Particle effects for impacts (simple circles)\n\n**Generated Assets:**\n- Background texture (subtle grid/tech pattern)\n- Impact flash sprites (for lightsaber hits)\n- UI icons (pause, restart buttons)\n- Victory/defeat screen graphics\n\n**Technical Specifications:**\n- Stickman limbs: 3-4px thick lines\n- Lightsaber blades: 4-6px thick with 10-15px glow radius\n- Heads: 20px diameter circles\n- All elements use anti-aliasing for smooth appearance\n- Consistent line weights across all stick figures\n\n**Animation Style:**\n- All animation driven by physics simulation\n- No keyframe animations needed\n- Lightsaber trails fade over 3-4 frames for motion blur effect\n- Dismembered parts tumble with physics until they fade out (2 seconds)\n\n**Viewing Angle:**\n- Pure top-down orthographic view\n- All stickmen rendered as if lying flat (limbs spread out)\n- Lightsabers extend from hand position\n- No perspective distortion\n\nThis minimalist approach keeps the focus on gameplay while ensuring smooth performance on mobile devices. The neon lightsabers against dark backgrounds create visual pop without requiring complex art assets.", "persistent_storage_config": {"leaderboard": {"sort_ascending": false}}, "game_metadata": {"game_id": "01KRTETJQKECG4P91G9SW42VT3", "title": "Stickman Lightsaber Brawl", "description": "Control a floppy stickman warrior with ragdoll physics! Swipe to move, swing your lightsaber wildly, and survive 5 minutes against increasingly chaotic enemies in this physics-based brawler.", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_44686446-41b6-4649-ab98-8b51c3e139af.webp", "created_at": 1779005106, "updated_at": 1781443785095.7146, "art_style": null}, "task_plan": null}