{"asset_map": {"monkey_sitting": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/monkey_sitting_8c5028c1-7f9f-49e3-a843-cdd9b5d75cdb.webp", "type": "image", "prompt": "Generate a full-body, side-view illustration of a cute cartoon monkey sitting down. The monkey has brown fur, a tan face and belly, large expressive eyes, and a mischievous grin. Designed in a vibrant cartoon style with thick black outlines, rounded exaggerated proportions, and a curled tail. High quality, professional game asset, transparent background.", "description": "A mischievous cartoon monkey sitting in a neutral pose for the game level.", "subtype": "character", "aspect_ratio": [804, 1266], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "monkey_hit": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/monkey_hit_acfa6038-9375-495a-8ba5-77d57820fca8.webp", "type": "image", "prompt": "Modify the monkey from image 1 to show a surprised, shocked expression with wide eyes and an open mouth. The body should be tilted backward with arms flailing outward as if knocked off balance. Maintain the exact same art style, brown fur color, and thick black outlines as image 1. Full body illustration, transparent background.", "description": "The monkey character reacting with a surprised expression when hit by a banana.", "subtype": "character", "aspect_ratio": [801, 1069], "compression_suggestion": "medium", "duration_secs": null, "loop": null, "animation": null}, "banana_projectile": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/banana_projectile_f2f5f0b6-6028-4e4e-9203-de0bc9e60dc9.webp", "type": "image", "prompt": "Generate a single cartoon banana viewed from the side. Bright yellow skin with small brown spots, thick black outlines, and subtle shading. Simple, clean, curved classic banana shape. Vibrant cartoon art style, high resolution, transparent background.", "description": "A bright yellow cartoon banana used as the player's projectile.", "subtype": "object", "aspect_ratio": [1048, 812], "compression_suggestion": "high", "duration_secs": null, "loop": null, "animation": null}, "banana_cannon": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/banana_cannon_155d739d-15f3-435d-8fd5-2f51ec310b5a.webp", "type": "image", "prompt": "Generate a side-view cartoon cannon. The barrel is shaped like a giant, thick banana (yellow with brown spots). The base is a wooden cart with wheels in brown tones. Exaggerated playful proportions, thick black outlines, vibrant colors. Matches the style of a tropical jungle game. Transparent background.", "description": "A playful banana-shaped cannon used to launch projectiles.", "subtype": "object", "aspect_ratio": [1005, 949], "compression_suggestion": "low", "duration_secs": null, "loop": null, "animation": null}, "jungle_background": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/jungle_background_5ec6d4c2-774d-43a9-bfe4-53b728f0d82d.webp", "type": "image", "prompt": "Generate a 9:16 tall image of a vibrant cartoon jungle background. Sky gradient from light blue at the top to warm sunset orange and pink in the middle. Layers of tropical foliage, palm trees, vines, and broad leaves in various greens. Include wooden platforms and sturdy tree branches at different heights. Bright tropical flowers in pink and yellow. Thick black outlines on major elements, cheerful atmosphere, flat vector-influenced cartoon style, high detail.", "description": "A 9:16 portrait jungle background with sky, trees, and platforms.", "subtype": "background", "aspect_ratio": [1024, 1536], "compression_suggestion": "original", "duration_secs": null, "loop": null, "animation": null}, "banana_launch_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/banana_launch_sound_d8e81e29-43e8-4d4f-9390-4344c19b4d87.mp3", "type": "audio", "prompt": "A short, punchy, and comedic cartoon 'thwip' or 'boing' sound effect. Imagine a pressurized banana being launched from a wooden tube. High-pitched, bouncy, and clean, with a subtle spring-like tail. No realistic explosion or gunpowder sounds; keep it lighthearted and playful for a mobile physics game.", "description": "Comedic cartoonish sound effect for the banana cannon launch.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.04, "loop": false, "animation": null}, "monkey_hit_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/monkey_hit_sound_084b8a6f-0c99-4eab-95b0-d61d777a1389.mp3", "type": "audio", "prompt": "A funny, cartoonish impact sound effect. A soft 'bonk' or 'splat' followed immediately by a quick, surprised monkey 'ooh-ooh!' vocalization. The tone is silly and exaggerated, not painful. High quality, dry recording, suitable for frequent repetition in a physics-based arcade game.", "description": "Funny impact sound of a banana hitting a monkey with a vocal reaction.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 2, "loop": false, "animation": null}, "victory_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/victory_sound_0fa3cf46-40a5-4c2e-ac56-a2e2f77858c3.mp3", "type": "audio", "prompt": "A 3-second triumphant and cheerful victory fanfare. Bright instrumentation featuring xylophones, marimbas, and a celebratory brass swell. Major key, upbeat tropical jungle rhythm, ends on a high, sustained happy note. Fast tempo, bright and shimmering finish.", "description": "Short, triumphant jungle-themed jingle for winning a level.", "subtype": "music", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 3, "loop": false, "animation": null}, "fail_sound": {"url": "https://d2oir5eh8rty2e.cloudfront.net/assets/sounds/effect/fail_sound_dc3917b3-2f73-4a34-8a53-3ff4fedb91f7.mp3", "type": "audio", "prompt": "A comedic, lighthearted failure sound effect. A descending 'wah-wah-wah-waaaaah' trombone or slide whistle effect. Deflating and silly, following a classic cartoon style. The sound should feel slightly bouncy and not overly discouraging, fitting a playful jungle theme.", "description": "Comedic descending 'fail' sound for when ammo runs out.", "subtype": "effect", "aspect_ratio": null, "compression_suggestion": "original", "duration_secs": 1.88, "loop": false, "animation": null}}, "game_code": "<!DOCTYPE html>\n<html>\n<head>\n    <title>Banana Blaster</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=Fredoka:wght@400;600;700&family=Inter:wght@400;600&display=swap\" rel=\"stylesheet\">\n    <style>\n        /* CSS Reset */\n        *, *::before, *::after {\n            box-sizing: border-box;\n            margin: 0;\n            padding: 0;\n        }\n\n        /* CSS Custom Properties */\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: #87CEEB;\n        }\n\n        #game-canvas {\n            position: absolute;\n            top: 0;\n            left: 0;\n            width: 100%;\n            height: 100%;\n        }\n\n        /* HUD Elements */\n        .hud-container {\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            pointer-events: none;\n            z-index: 100;\n        }\n\n        .hud-element {\n            background: rgba(255, 255, 255, 0.95);\n            border: 4px solid #333;\n            border-radius: 16px;\n            padding: 12px 20px;\n            font-family: 'Fredoka', sans-serif;\n            font-size: 28px;\n            font-weight: 700;\n            color: #333;\n            text-shadow: 2px 2px 0px rgba(255, 255, 255, 0.5);\n            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);\n        }\n\n        .ammo-counter {\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        .banana-icon {\n            font-size: 32px;\n        }\n\n        /* Power Meter */\n        .power-meter-container {\n            position: absolute;\n            bottom: var(--space-md);\n            left: var(--space-sm);\n            width: 120px;\n            pointer-events: none;\n            z-index: 100;\n        }\n\n        .power-label {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 20px;\n            font-weight: 700;\n            color: #fff;\n            text-align: center;\n            margin-bottom: 8px;\n            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.8);\n        }\n\n        .power-meter {\n            width: 100%;\n            height: 200px;\n            background: rgba(0, 0, 0, 0.6);\n            border: 4px solid #fff;\n            border-radius: 12px;\n            position: relative;\n            overflow: hidden;\n        }\n\n        .power-fill {\n            position: absolute;\n            bottom: 0;\n            left: 0;\n            width: 100%;\n            height: 0%;\n            background: linear-gradient(to top, #ff4444, #ffaa00, #44ff44);\n            transition: height var(--transition-fast) var(--ease-out);\n        }\n\n        /* Modal Overlay */\n        .modal-overlay {\n            position: absolute;\n            top: 0;\n            left: 0;\n            right: 0;\n            bottom: 0;\n            background: rgba(0, 0, 0, 0.7);\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            z-index: 200;\n            pointer-events: none;\n            opacity: 0;\n            transition: opacity var(--transition-normal) var(--ease-out);\n        }\n\n        .modal-overlay.visible {\n            pointer-events: auto;\n            opacity: 1;\n        }\n\n        .modal-overlay > * {\n            pointer-events: auto;\n        }\n\n        /* Modal Content */\n        .modal {\n            background: linear-gradient(135deg, #ffe259 0%, #ffa751 100%);\n            border: 8px solid #333;\n            border-radius: 32px;\n            padding: var(--space-xl);\n            text-align: center;\n            max-width: 560px;\n            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);\n            transform: scale(0.8);\n            transition: transform var(--transition-normal) var(--ease-bounce);\n        }\n\n        .modal-overlay.visible .modal {\n            transform: scale(1);\n        }\n\n        .modal-title {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 56px;\n            font-weight: 700;\n            color: #333;\n            margin-bottom: var(--space-md);\n            text-shadow: 3px 3px 0px rgba(255, 255, 255, 0.5);\n        }\n\n        .modal-message {\n            font-family: 'Inter', sans-serif;\n            font-size: 24px;\n            color: #333;\n            margin-bottom: var(--space-lg);\n            line-height: 1.4;\n        }\n\n        /* Button Styles */\n        .button {\n            font-family: 'Fredoka', sans-serif;\n            font-size: 28px;\n            font-weight: 700;\n            padding: 16px 40px;\n            background: linear-gradient(to bottom, #4CAF50, #388E3C);\n            color: #fff;\n            border: 4px solid #2E7D32;\n            border-radius: 16px;\n            cursor: pointer;\n            box-shadow: 0 6px 0 #2E7D32, 0 8px 16px rgba(0, 0, 0, 0.3);\n            transition: all var(--transition-fast) var(--ease-out);\n            min-width: 160px;\n            text-shadow: 2px 2px 0px rgba(0, 0, 0, 0.3);\n        }\n            transform: translateY(-2px);\n            box-shadow: 0 8px 0 #2E7D32, 0 10px 20px rgba(0, 0, 0, 0.4);\n        }\n\n        .button:active {\n            transform: translateY(4px);\n            box-shadow: 0 2px 0 #2E7D32, 0 4px 8px rgba(0, 0, 0, 0.3);\n        }\n\n        .button:focus-visible {\n            outline: 4px solid #fff;\n            outline-offset: 4px;\n        }\n            // Initialize game from config or procedural generation\n            function initGame(levelIdx = 0) {\n                currentLevelIndex = levelIdx;\n                \n                let levelMonkeys = [];\n                \n                if (window.gameConfig.levels && window.gameConfig.levels[currentLevelIndex]) {\n                    levelMonkeys = window.gameConfig.levels[currentLevelIndex].monkeys;\n                } else {\n                    // Procedural generation for level 10 and beyond\n                    const count = 5 + Math.floor(currentLevelIndex / 50); // Slightly more monkeys over time\n                    const finalCount = Math.min(count, 12);\n                    for (let i = 0; i < finalCount; i++) {\n                        const seedX = currentLevelIndex * 1337 + i * 42;\n                        const seedY = currentLevelIndex * 7331 + i * 24;\n                        levelMonkeys.push({\n                            x: 100 + getSeededRandom(seedX) * 520,\n                            y: 150 + getSeededRandom(seedY) * 800\n                        });\n                    }\n                }\n                \n                // Increase ammo slightly as levels get harder\n                const bonusAmmo = Math.floor(currentLevelIndex / 20);\n                ammoRemaining = window.gameConfig.settings.ammoCount + bonusAmmo;\n                \n                monkeys = levelMonkeys.map(m => ({\n                    x: m.x,\n                    y: m.y,\n                    hit: false,\n                    falling: false,\n                    velocity: { x: 0, y: 0 },\n                    rotation: 0,\n                    rotationSpeed: 0\n                }));\n                bananas = [];\n                gameEnded = false;\n                updateAmmoDisplay();\n                updateLevelDisplay();\n                hideModal();\n            document.getElementById('modal-button').addEventListener('click', () => {\n                if (gameEnded && monkeys.every(m => m.hit)) {\n                    // Win - go to next level\n                    currentLevelIndex++;\n                    initGame(currentLevelIndex);\n                } else {\n                    // Loss - retry current level\n                    initGame(currentLevelIndex);\n                }\n            });\n        @media (prefers-reduced-motion: reduce) {\n            * {\n                animation-duration: 0.01ms !important;\n                animation-iteration-count: 1 !important;\n                transition-duration: 0.01ms !important;\n            }\n        }\n    </style>\n</head>\n<body>\n    <div id=\"game-world\">\n        <canvas id=\"game-canvas\"></canvas>\n        \n        <!-- HUD -->\n        <div class=\"hud-container\">\n            <div class=\"hud-element ammo-counter\">\n                <span class=\"banana-icon\">\ud83c\udf4c</span>\n                <span id=\"ammo-display\">10</span>\n            </div>\n            <div class=\"hud-element level-counter\">\n                <span>Level: </span>\n                <span id=\"level-display\">1</span>\n            </div>\n        </div>\n\n        <!-- Power Meter -->\n        <div class=\"power-meter-container\" id=\"power-meter-container\" style=\"display: none;\">\n            <div class=\"power-label\">POWER</div>\n            <div class=\"power-meter\">\n                <div class=\"power-fill\" id=\"power-fill\"></div>\n            </div>\n        </div>\n\n        <!-- Modal Overlay -->\n        <div class=\"modal-overlay\" id=\"modal-overlay\">\n            <div class=\"modal\">\n                <div class=\"modal-title\" id=\"modal-title\">Victory!</div>\n                <div class=\"modal-message\" id=\"modal-message\">You knocked down all the monkeys!</div>\n                <button class=\"button\" id=\"modal-button\">Play Again</button>\n            </div>\n        </div>\n    </div>\n\n    <script>\n        /*\n         * BANANA BLASTER\n         * \n         * A physics-based arcade game where players use a banana cannon to knock down\n         * mischievous monkeys. Aim and fire bananas with realistic physics to clear\n         * each level before running out of ammo!\n         * \n         * Features:\n         * - Intuitive aim and power controls (touch and mouse)\n         * - Physics simulation with gravity and bouncing\n         * - Trajectory prediction\n         * - Edit mode for customizing monkey positions and game parameters\n         * - Cartoon art style with funny animations and sound effects\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            function getSeededRandom(seed) {\n                const x = Math.sin(seed) * 10000;\n                return x - Math.floor(x);\n            }\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 cache\n            const assets = {};\n            const audioContext = new (window.AudioContext || window.webkitAudioContext)();\n            const audioBuffers = {};\n\n            // Preload assets\n            await preloadAssets([\n                'monkey_sitting',\n                'monkey_hit',\n                'banana_projectile',\n                'banana_cannon',\n                'jungle_background',\n                'banana_launch_sound',\n                'monkey_hit_sound',\n                'victory_sound',\n                'fail_sound'\n            ]);\n\n            async function preloadAssets(assetIds) {\n                const promises = assetIds.map(async (id) => {\n                    const assetInfo = lib.getAsset(id);\n                    if (!assetInfo) return;\n\n                    if (assetInfo.type === 'image') {\n                        const img = new Image();\n                        img.src = assetInfo.url;\n                        await new Promise((resolve, reject) => {\n                            img.onload = resolve;\n                            img.onerror = reject;\n                        });\n                        assets[id] = img;\n                    } else if (assetInfo.type === 'audio') {\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                        } catch (error) {\n                            console.error(`Failed to load audio: ${id}`, error);\n                        }\n                    }\n                });\n                await Promise.all(promises);\n            }\n\n            function playSound(soundId) {\n                if (audioBuffers[soundId]) {\n                    const source = audioContext.createBufferSource();\n                    source.buffer = audioBuffers[soundId];\n                    source.connect(audioContext.destination);\n                    source.start(0);\n                }\n            }\n\n            // Device detection\n            const isTouchDevice = lib.shouldUseTouchControls();\n\n            // Game parameters UI\n            lib.showGameParameters({\n                name: 'Game Settings',\n                params: {\n                    'Ammo Count': {\n                        key: 'settings.ammoCount',\n                        type: 'slider',\n                        min: 5,\n                        max: 20,\n                        step: 1\n                    },\n                    'Max Power': {\n                        key: 'settings.maxPower',\n                        type: 'slider',\n                        min: 500,\n                        max: 1500,\n                        step: 50\n                    },\n                    'Gravity': {\n                        key: 'settings.gravity',\n                        type: 'slider',\n                        min: 300,\n                        max: 800,\n                        step: 50\n                    },\n                    'Edit Level': {\n                        key: 'settings.editingLevel',\n                        type: 'slider',\n                        min: 0,\n                        max: 99,\n                        step: 1,\n                        onChange: (val) => {\n                            window.gameConfig.settings.editingLevel = val;\n                            if (currentMode === 'edit') {\n                                currentLevelIndex = val;\n                                initGame(currentLevelIndex);\n                            }\n                        }\n                    }\n                }\n            });\n\n            // Game state variables (runtime only, not persisted)\n            let currentMode = mode;\n            let currentLevelIndex = 0;\n            let monkeys = [];\n            let bananas = [];\n            let cannonAngle = -45;\n            let cannonPower = 0;\n            let charging = false;\n            let chargeStartTime = 0;\n            let ammoRemaining = 0;\n            let gameEnded = false;\n            let selectedMonkey = null;\n            let dragOffset = { x: 0, y: 0 };\n            let mousePos = { x: 360, y: 640 };\n            let lastTime = 0;\n\n            // Constants\n            const CANNON_POS = { x: 100, y: 1150 };\n            const CANNON_SIZE = { width: 100, height: 80 };\n            const MONKEY_SIZE = 80;\n            const BANANA_SIZE = { width: 40, height: 20 };\n            const MAX_CHARGE_TIME = 1500; // ms\n            const BOUNCE_COEFFICIENT = 0.3;\n            const MAX_DELTA = 0.11;\n\n            // Initialize game from config or procedural generation\n            function initGame(levelIdx = 0) {\n                currentLevelIndex = levelIdx;\n                \n                let levelMonkeys = [];\n                \n                if (window.gameConfig.levels && window.gameConfig.levels[currentLevelIndex]) {\n                    levelMonkeys = window.gameConfig.levels[currentLevelIndex].monkeys;\n                } else {\n                    // Procedural generation for level 10 and beyond\n                    const count = 5 + Math.floor(currentLevelIndex / 50); \n                    const finalCount = Math.min(count, 12);\n                    for (let i = 0; i < finalCount; i++) {\n                        const seedX = currentLevelIndex * 1337 + i * 42;\n                        const seedY = currentLevelIndex * 7331 + i * 24;\n                        levelMonkeys.push({\n                            x: 100 + getSeededRandom(seedX) * 520,\n                            y: 150 + getSeededRandom(seedY) * 800\n                        });\n                    }\n                }\n                \n                // Increase ammo slightly as levels get harder\n                const bonusAmmo = Math.floor(currentLevelIndex / 20);\n                ammoRemaining = window.gameConfig.settings.ammoCount + bonusAmmo;\n                \n                monkeys = levelMonkeys.map(m => ({\n                    x: m.x,\n                    y: m.y,\n                    hit: false,\n                    falling: false,\n                    velocity: { x: 0, y: 0 },\n                    rotation: 0,\n                    rotationSpeed: 0\n                }));\n                bananas = [];\n                gameEnded = false;\n                updateAmmoDisplay();\n                updateLevelDisplay();\n                hideModal();\n            }\n\n            function updateAmmoDisplay() {\n                document.getElementById('ammo-display').textContent = ammoRemaining;\n            }\n\n            function updateLevelDisplay() {\n                document.getElementById('level-display').textContent = (currentLevelIndex + 1);\n            }\n\n            // Modal functions\n            function showModal(title, message, buttonText = 'Play Again') {\n                document.getElementById('modal-title').textContent = title;\n                document.getElementById('modal-message').textContent = message;\n                document.getElementById('modal-button').textContent = buttonText;\n                document.getElementById('modal-overlay').classList.add('visible');\n            }\n\n            function hideModal() {\n                document.getElementById('modal-overlay').classList.remove('visible');\n            }\n\n            document.getElementById('modal-button').addEventListener('click', () => {\n                if (gameEnded && monkeys.every(m => m.hit)) {\n                    // Win - go to next level\n                    currentLevelIndex++;\n                    initGame(currentLevelIndex);\n                } else {\n                    // Loss - retry current level\n                    initGame(currentLevelIndex);\n                }\n            });\n\n            // Drawing functions\n            function drawBackground() {\n                if (assets['jungle_background']) {\n                    const img = assets['jungle_background'];\n                    const imgAspect = img.width / img.height;\n                    const canvasAspect = canvas.width / canvas.height;\n                    \n                    let drawWidth, drawHeight, drawX, drawY;\n                    \n                    // Cover scaling\n                    if (imgAspect > canvasAspect) {\n                        drawHeight = canvas.height;\n                        drawWidth = drawHeight * imgAspect;\n                        drawX = (canvas.width - drawWidth) / 2;\n                        drawY = 0;\n                    } else {\n                        drawWidth = canvas.width;\n                        drawHeight = drawWidth / imgAspect;\n                        drawX = 0;\n                        drawY = (canvas.height - drawHeight) / 2;\n                    }\n                    \n                    ctx.drawImage(img, drawX, drawY, drawWidth, drawHeight);\n                } else {\n                    ctx.fillStyle = '#87CEEB';\n                    ctx.fillRect(0, 0, canvas.width, canvas.height);\n                }\n            }\n\n            function drawCannon() {\n                ctx.save();\n                ctx.translate(CANNON_POS.x, CANNON_POS.y);\n                ctx.rotate((cannonAngle * Math.PI) / 180);\n                \n                if (assets['banana_cannon']) {\n                    const img = assets['banana_cannon'];\n                    const aspect = img.width / img.height;\n                    const width = CANNON_SIZE.width;\n                    const height = width / aspect;\n                    ctx.drawImage(img, 0, -height / 2, width, height);\n                } else {\n                    ctx.fillStyle = '#8B4513';\n                    ctx.fillRect(0, -20, 80, 40);\n                }\n                \n                ctx.restore();\n\n                // Draw aim line in play mode when charging\n                if (currentMode === 'play' && charging) {\n                    drawTrajectoryPrediction();\n                }\n            }\n\n            function drawTrajectoryPrediction() {\n                const angle = (cannonAngle * Math.PI) / 180;\n                const velocity = (cannonPower / 100) * window.gameConfig.settings.maxPower;\n                const vx = Math.cos(angle) * velocity;\n                const vy = Math.sin(angle) * velocity;\n                \n                ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';\n                ctx.lineWidth = 3;\n                ctx.setLineDash([10, 10]);\n                ctx.beginPath();\n                \n                let px = CANNON_POS.x;\n                let py = CANNON_POS.y;\n                ctx.moveTo(px, py);\n                \n                const steps = 30;\n                const dt = 0.033;\n                let tvx = vx;\n                let tvy = vy;\n                \n                for (let i = 0; i < steps; i++) {\n                    tvy += window.gameConfig.settings.gravity * dt;\n                    px += tvx * dt;\n                    py += tvy * dt;\n                    \n                    if (py > canvas.height) break;\n                    \n                    ctx.lineTo(px, py);\n                }\n                \n                ctx.stroke();\n                ctx.setLineDash([]);\n            }\n\n            function drawMonkeys() {\n                monkeys.forEach((monkey, index) => {\n                    ctx.save();\n                    ctx.translate(monkey.x, monkey.y);\n                    \n                    if (monkey.falling) {\n                        ctx.rotate(monkey.rotation);\n                    }\n                    \n                    const img = monkey.hit ? assets['monkey_hit'] : assets['monkey_sitting'];\n                    if (img) {\n                        const aspect = img.width / img.height;\n                        const size = MONKEY_SIZE;\n                        const width = size;\n                        const height = size / aspect;\n                        ctx.drawImage(img, -width / 2, -height / 2, width, height);\n                    } else {\n                        ctx.fillStyle = '#8B4513';\n                        ctx.beginPath();\n                        ctx.arc(0, 0, MONKEY_SIZE / 2, 0, Math.PI * 2);\n                        ctx.fill();\n                    }\n                    \n                    // Edit mode: draw selection highlight\n                    if (currentMode === 'edit' && selectedMonkey === index) {\n                        ctx.strokeStyle = '#FFD700';\n                        ctx.lineWidth = 4;\n                        ctx.beginPath();\n                        ctx.arc(0, 0, MONKEY_SIZE / 2 + 10, 0, Math.PI * 2);\n                        ctx.stroke();\n                    }\n                    \n                    ctx.restore();\n                });\n            }\n\n            function drawBananas() {\n                bananas.forEach(banana => {\n                    ctx.save();\n                    ctx.translate(banana.x, banana.y);\n                    ctx.rotate(banana.rotation);\n                    \n                    if (assets['banana_projectile']) {\n                        const img = assets['banana_projectile'];\n                        const aspect = img.width / img.height;\n                        const width = BANANA_SIZE.width;\n                        const height = width / aspect;\n                        ctx.drawImage(img, -width / 2, -height / 2, width, height);\n                    } else {\n                        ctx.fillStyle = '#FFD700';\n                        ctx.beginPath();\n                        ctx.ellipse(0, 0, 20, 10, 0, 0, Math.PI * 2);\n                        ctx.fill();\n                    }\n                    \n                    ctx.restore();\n                });\n            }\n\n            function drawPowerMeter() {\n                document.getElementById('power-fill').style.height = cannonPower + '%';\n            }\n\n            // Physics functions\n            function updateBananas(dt) {\n                const gravity = window.gameConfig.settings.gravity;\n                \n                bananas.forEach((banana, index) => {\n                    // Update velocity\n                    banana.velocity.y += gravity * dt;\n                    \n                    // Update position\n                    banana.x += banana.velocity.x * dt;\n                    banana.y += banana.velocity.y * dt;\n                    \n                    // Update rotation based on velocity\n                    banana.rotation = Math.atan2(banana.velocity.y, banana.velocity.x);\n                    \n                    // Ground bounce\n                    if (banana.y > canvas.height - 20) {\n                        banana.y = canvas.height - 20;\n                        banana.velocity.y = -banana.velocity.y * BOUNCE_COEFFICIENT;\n                        banana.velocity.x *= 0.7;\n                        banana.bounces++;\n                    }\n                    \n                    // Remove if off screen or too many bounces\n                    if (banana.x < -50 || banana.x > canvas.width + 50 || \n                        banana.y > canvas.height + 50 || banana.bounces > 2) {\n                        bananas.splice(index, 1);\n                    }\n                });\n            }\n\n            function updateMonkeys(dt) {\n                monkeys.forEach(monkey => {\n                    if (monkey.falling) {\n                        // Apply gravity\n                        monkey.velocity.y += window.gameConfig.settings.gravity * dt;\n                        \n                        // Update position\n                        monkey.x += monkey.velocity.x * dt;\n                        monkey.y += monkey.velocity.y * dt;\n                        \n                        // Update rotation\n                        monkey.rotation += monkey.rotationSpeed * dt;\n                        \n                        // Remove if off screen\n                        if (monkey.y > canvas.height + 100) {\n                            monkey.y = canvas.height + 100;\n                            monkey.velocity.y = 0;\n                        }\n                    }\n                });\n            }\n\n            function checkCollisions() {\n                bananas.forEach((banana, bIndex) => {\n                    monkeys.forEach((monkey, mIndex) => {\n                        if (!monkey.hit && !monkey.falling) {\n                            const dx = banana.x - monkey.x;\n                            const dy = banana.y - monkey.y;\n                            const distance = Math.sqrt(dx * dx + dy * dy);\n                            \n                            if (distance < (MONKEY_SIZE / 2 + BANANA_SIZE.width / 2)) {\n                                // Hit!\n                                monkey.hit = true;\n                                monkey.falling = true;\n                                monkey.velocity.x = banana.velocity.x * 0.3;\n                                monkey.velocity.y = banana.velocity.y * 0.3;\n                                monkey.rotationSpeed = (Math.random() - 0.5) * 10;\n                                \n                                playSound('monkey_hit_sound');\n                                \n                                // Remove banana\n                                bananas.splice(bIndex, 1);\n                                \n                                // Check win condition\n                                checkWinCondition();\n                            }\n                        }\n                    });\n                });\n            }\n\n            function checkWinCondition() {\n                const allHit = monkeys.every(m => m.hit);\n                if (allHit && !gameEnded) {\n                    gameEnded = true;\n                    playSound('victory_sound');\n                    setTimeout(() => {\n                        const winMsg = `Level ${currentLevelIndex + 1} Cleared! Keep going to level 10,000!`;\n                        showModal('Victory!', winMsg, 'Next Level');\n                    }, 500);\n                }\n            }\n\n            function checkLoseCondition() {\n                if (ammoRemaining === 0 && bananas.length === 0 && !gameEnded) {\n                    const allHit = monkeys.every(m => m.hit);\n                    if (!allHit) {\n                        gameEnded = true;\n                        playSound('fail_sound');\n                        setTimeout(() => {\n                            showModal('Out of Bananas!', 'Try again to knock down all the monkeys!', 'Retry');\n                        }, 500);\n                    }\n                }\n            }\n\n            // Input handling\n            function fireBanana() {\n                if (ammoRemaining <= 0 || bananas.length > 0 || gameEnded) return;\n                \n                const angle = (cannonAngle * Math.PI) / 180;\n                const velocity = (cannonPower / 100) * window.gameConfig.settings.maxPower;\n                \n                bananas.push({\n                    x: CANNON_POS.x + Math.cos(angle) * 80,\n                    y: CANNON_POS.y + Math.sin(angle) * 80,\n                    velocity: {\n                        x: Math.cos(angle) * velocity,\n                        y: Math.sin(angle) * velocity\n                    },\n                    rotation: angle,\n                    bounces: 0\n                });\n                \n                ammoRemaining--;\n                updateAmmoDisplay();\n                playSound('banana_launch_sound');\n                \n                cannonPower = 0;\n                drawPowerMeter();\n            }\n\n            // Edit mode functions\n            function getMonkeyAtPosition(x, y) {\n                for (let i = monkeys.length - 1; i >= 0; i--) {\n                    const monkey = monkeys[i];\n                    const dx = x - monkey.x;\n                    const dy = y - monkey.y;\n                    const distance = Math.sqrt(dx * dx + dy * dy);\n                    if (distance < MONKEY_SIZE / 2) {\n                        return i;\n                    }\n                }\n                return null;\n            }\n\n            function updateMonkeyConfig() {\n                if (!window.gameConfig.levels) window.gameConfig.levels = [];\n                if (!window.gameConfig.levels[currentLevelIndex]) window.gameConfig.levels[currentLevelIndex] = { monkeys: [] };\n                \n                window.gameConfig.levels[currentLevelIndex].monkeys = monkeys.map(m => ({\n                    x: Math.round(m.x),\n                    y: Math.round(m.y)\n                }));\n            }\n\n            // Mode-specific setup\n            if (currentMode === 'edit') {\n                // Edit mode setup\n                currentLevelIndex = window.gameConfig.settings.editingLevel || 0;\n                initGame(currentLevelIndex);\n                document.getElementById('power-meter-container').style.display = 'none';\n                \n                // Add edit instructions\n                const instructions = document.createElement('div');\n                instructions.className = 'edit-instructions';\n                instructions.textContent = 'Drag monkeys to reposition them';\n                gameWorld.appendChild(instructions);\n\n                // Mouse/touch handlers for edit mode\n                function handleEditStart(x, y) {\n                    const monkeyIndex = getMonkeyAtPosition(x, y);\n                    if (monkeyIndex !== null) {\n                        selectedMonkey = monkeyIndex;\n                        dragOffset.x = x - monkeys[monkeyIndex].x;\n                        dragOffset.y = y - monkeys[monkeyIndex].y;\n                    }\n                }\n\n                function handleEditMove(x, y) {\n                    if (selectedMonkey !== null) {\n                        monkeys[selectedMonkey].x = Math.max(50, Math.min(670, x - dragOffset.x));\n                        monkeys[selectedMonkey].y = Math.max(100, Math.min(1100, y - dragOffset.y));\n                        updateMonkeyConfig();\n                    }\n                }\n\n                function handleEditEnd() {\n                    selectedMonkey = null;\n                }\n\n                canvas.addEventListener('mousedown', (e) => {\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (e.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (e.clientY - rect.top) * (canvas.height / rect.height);\n                    handleEditStart(x, y);\n                });\n\n                canvas.addEventListener('mousemove', (e) => {\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (e.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (e.clientY - rect.top) * (canvas.height / rect.height);\n                    handleEditMove(x, y);\n                });\n\n                canvas.addEventListener('mouseup', handleEditEnd);\n\n                canvas.addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    const touch = e.touches[0];\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (touch.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (touch.clientY - rect.top) * (canvas.height / rect.height);\n                    handleEditStart(x, y);\n                });\n\n                canvas.addEventListener('touchmove', (e) => {\n                    e.preventDefault();\n                    const touch = e.touches[0];\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (touch.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (touch.clientY - rect.top) * (canvas.height / rect.height);\n                    handleEditMove(x, y);\n                });\n\n                canvas.addEventListener('touchend', (e) => {\n                    e.preventDefault();\n                    handleEditEnd();\n                });\n\n            } else {\n                // Play mode setup\n                initGame(0);\n                document.getElementById('power-meter-container').style.display = 'block';\n\n                // Input handlers for play mode\n                function handlePlayStart(x, y) {\n                    if (gameEnded || bananas.length > 0 || ammoRemaining === 0) return;\n                    \n                    charging = true;\n                    chargeStartTime = Date.now();\n                    mousePos.x = x;\n                    mousePos.y = y;\n                }\n\n                function handlePlayMove(x, y) {\n                    mousePos.x = x;\n                    mousePos.y = y;\n                    \n                    // Update cannon angle to point at mouse/touch\n                    const dx = x - CANNON_POS.x;\n                    const dy = y - CANNON_POS.y;\n                    cannonAngle = Math.atan2(dy, dx) * (180 / Math.PI);\n                    cannonAngle = Math.max(-90, Math.min(0, cannonAngle));\n                    \n                    if (charging) {\n                        const chargeTime = Math.min(Date.now() - chargeStartTime, MAX_CHARGE_TIME);\n                        cannonPower = (chargeTime / MAX_CHARGE_TIME) * 100;\n                        drawPowerMeter();\n                    }\n                }\n\n                function handlePlayEnd() {\n                    if (charging) {\n                        charging = false;\n                        if (cannonPower > 10) {\n                            fireBanana();\n                        }\n                        cannonPower = 0;\n                        drawPowerMeter();\n                    }\n                }\n\n                canvas.addEventListener('mousedown', (e) => {\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (e.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (e.clientY - rect.top) * (canvas.height / rect.height);\n                    handlePlayStart(x, y);\n                });\n\n                canvas.addEventListener('mousemove', (e) => {\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (e.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (e.clientY - rect.top) * (canvas.height / rect.height);\n                    handlePlayMove(x, y);\n                });\n\n                canvas.addEventListener('mouseup', handlePlayEnd);\n\n                canvas.addEventListener('touchstart', (e) => {\n                    e.preventDefault();\n                    const touch = e.touches[0];\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (touch.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (touch.clientY - rect.top) * (canvas.height / rect.height);\n                    handlePlayStart(x, y);\n                });\n\n                canvas.addEventListener('touchmove', (e) => {\n                    e.preventDefault();\n                    const touch = e.touches[0];\n                    const rect = canvas.getBoundingClientRect();\n                    const x = (touch.clientX - rect.left) * (canvas.width / rect.width);\n                    const y = (touch.clientY - rect.top) * (canvas.height / rect.height);\n                    handlePlayMove(x, y);\n                });\n\n                canvas.addEventListener('touchend', (e) => {\n                    e.preventDefault();\n                    handlePlayEnd();\n                });\n            }\n\n            // Game loop\n            function gameLoop(timestamp) {\n                const deltaTime = Math.min((timestamp - lastTime) / 1000, MAX_DELTA);\n                lastTime = timestamp;\n\n                // Clear canvas\n                ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n                // Draw everything\n                drawBackground();\n                drawCannon();\n                drawMonkeys();\n                drawBananas();\n\n                if (currentMode === 'play') {\n                    // Update physics\n                    if (deltaTime > 0) {\n                        updateBananas(deltaTime);\n                        updateMonkeys(deltaTime);\n                        checkCollisions();\n                        checkLoseCondition();\n                    }\n                }\n\n                requestAnimationFrame(gameLoop);\n            }\n\n            lastTime = performance.now();\n            requestAnimationFrame(gameLoop);\n        }\n    </script>\n</body>\n</html>", "game_config": "{\"monkeys\":[{\"x\":500,\"y\":900},{\"x\":300,\"y\":600},{\"x\":600,\"y\":400},{\"x\":200,\"y\":500},{\"x\":550,\"y\":250}],\"settings\":{\"ammoCount\":8,\"maxPower\":1000,\"gravity\":550,\"editingLevel\":99},\"levels\":[{\"monkeys\":[{\"y\":900,\"x\":500},{\"x\":300,\"y\":600},{\"y\":400,\"x\":600},{\"y\":500,\"x\":200},{\"x\":550,\"y\":250}]},{\"monkeys\":[{\"x\":200,\"y\":300},{\"x\":300,\"y\":400},{\"x\":400,\"y\":500},{\"x\":500,\"y\":600},{\"y\":700,\"x\":600}]},{\"monkeys\":[{\"x\":360,\"y\":200},{\"y\":350,\"x\":260},{\"y\":350,\"x\":460},{\"y\":500,\"x\":160},{\"y\":500,\"x\":560}]},{\"monkeys\":[{\"y\":400,\"x\":100},{\"x\":200,\"y\":400},{\"y\":400,\"x\":360},{\"x\":520,\"y\":400},{\"y\":400,\"x\":620}]},{\"monkeys\":[{\"x\":360,\"y\":300},{\"x\":360,\"y\":500},{\"y\":700,\"x\":360},{\"x\":360,\"y\":900},{\"x\":360,\"y\":1100}]},{\"monkeys\":[{\"y\":200,\"x\":200},{\"x\":520,\"y\":200},{\"y\":400,\"x\":360},{\"x\":200,\"y\":600},{\"x\":520,\"y\":600}]},{\"monkeys\":[{\"y\":150,\"x\":150},{\"x\":570,\"y\":150},{\"x\":150,\"y\":450},{\"y\":450,\"x\":570},{\"y\":300,\"x\":360}]},{\"monkeys\":[{\"y\":800,\"x\":400},{\"x\":450,\"y\":850},{\"x\":500,\"y\":900},{\"y\":950,\"x\":550},{\"x\":600,\"y\":1000}]},{\"monkeys\":[{\"y\":200,\"x\":100},{\"y\":300,\"x\":200},{\"x\":300,\"y\":400},{\"y\":300,\"x\":400},{\"y\":200,\"x\":500}]},{\"monkeys\":[{\"x\":200,\"y\":1000},{\"y\":1000,\"x\":300},{\"y\":1000,\"x\":400},{\"y\":1000,\"x\":500},{\"y\":1000,\"x\":600}]}]}", "game_design_document": "## Core Game Loop and Mechanics\n\n**Banana Blaster** is a physics-based arcade game where players use a banana cannon to knock down monkeys perched throughout a jungle scene.\n\n**Moment-to-Moment Gameplay:**\n- Player adjusts the cannon angle by dragging/moving\n- Player sets power level (visual indicator shows charge)\n- Player fires banana which follows realistic physics trajectory\n- Banana impacts monkeys, structures, or environment\n- Monkeys react with funny animations when hit and fall down\n- Player observes results and adjusts strategy for next shot\n\n**Progression:**\n- Each level has a set number of bananas (ammo)\n- Goal: knock down all monkeys before running out of bananas\n- Earn star rating based on efficiency (3 stars = minimal shots, 1 star = barely made it)\n- Unlock next level upon completion\n\n**Win/Lose Conditions:**\n- **Win**: All monkeys knocked down with remaining ammo\n- **Lose**: Run out of bananas with monkeys still standing\n- Can retry level immediately\n\n**Engagement Hooks:**\n- Satisfying physics and collision reactions\n- Strategic shot planning (angle, power, ricochets)\n- Funny monkey reactions and sound effects\n- Star rating system encourages optimization\n- Quick retry loop keeps players engaged\n\n## Starting Scope\n\n**First Iteration (Implement Now):**\n- Single level with 3-5 monkeys in simple positions\n- Fixed cannon at bottom-left of screen\n- Basic physics for banana projectiles (gravity, bounce)\n- Simple collision detection (banana hits monkey = monkey falls)\n- Ammo counter (start with 10 bananas)\n- Win/lose screen with retry button\n- Touch controls: drag to aim, release to fire (power based on drag distance)\n- Desktop controls: mouse to aim, click and hold to charge power, release to fire\n- Cartoon art style with bright colors\n\n**Future Iterations (Add Later):**\n- Multiple levels with increasing difficulty\n- Destructible jungle structures (crates, platforms)\n- Different monkey types (small, large, moving)\n- Power-ups (explosive bananas, multi-shot)\n- Star rating system\n- Level select screen\n- Background music and more sound effects\n- Particle effects for impacts\n\n## Game Perspective\n\n**Fixed-screen** perspective with a single jungle scene visible at all times. The camera does not scroll. All action happens within the 720\u00d71280 viewport.\n\nThe cannon is positioned in the bottom-left corner, and monkeys are distributed throughout the upper portions of the screen on various perches, branches, and platforms.\n\n## Game World and Level Design\n\n**Level 1 Layout:**\n- **Cannon**: Fixed position at bottom-left (x: 100, y: 1150)\n- **Monkeys** (5 total):\n  - Monkey 1: Sitting on a low branch (x: 500, y: 900)\n  - Monkey 2: On a higher platform (x: 300, y: 600)\n  - Monkey 3: Perched on a tall tree branch (x: 600, y: 400)\n  - Monkey 4: Behind some leaves (x: 200, y: 500)\n  - Monkey 5: On the highest branch (x: 550, y: 250)\n\n**Environment Elements:**\n- Vibrant jungle background with trees, vines, and foliage\n- Wooden platforms and branches where monkeys sit\n- Ground at bottom with grass and tropical plants\n- Sky gradient from light blue to tropical sunset colors\n\n**Physics Properties:**\n- Gravity: Standard downward acceleration\n- Banana bounce: 0.3 coefficient (loses energy on bounce)\n- Monkey fall: Ragdoll-style tumble when hit\n- Collision: Banana disappears after hitting monkey or after 2 bounces\n\n## Edit Mode Design\n\n**Editable Properties:**\n\n1. **Monkey Positions**\n   - X and Y coordinates for each monkey\n   - Constraint: Must stay within screen bounds (50-670 x, 100-1100 y)\n\n2. **Ammo Count**\n   - Number of bananas available\n   - Constraint: 5-20 bananas\n\n3. **Monkey Count**\n   - Number of monkeys in level\n   - Constraint: 1-8 monkeys\n\n4. **Cannon Power**\n   - Maximum launch velocity\n   - Constraint: 500-1500 (affects how far bananas can fly)\n\n5. **Gravity Strength**\n   - Affects banana arc\n   - Constraint: 300-800 (lower = floatier, higher = faster drop)\n\n**Edit Mode UI:**\n- Visual representation of the level with draggable monkey icons\n- Sliders for numerical properties\n- \"Add Monkey\" and \"Remove Monkey\" buttons\n- \"Test Level\" button to switch to play mode\n- Reset to defaults option\n\n## Control Design\n\n**Mobile (Touch):**\n- **Aiming**: Touch and drag anywhere on screen to rotate cannon (visual arrow shows trajectory prediction)\n- **Power**: Drag distance from cannon determines power (longer drag = more power)\n- **Fire**: Release touch to launch banana\n- Visual feedback: Power meter fills as you drag farther, trajectory arc shows predicted path\n\n**Desktop (Keyboard/Mouse):**\n- **Aiming**: Move mouse to aim cannon (cannon rotates to point toward cursor)\n- **Power**: Click and hold to charge power (hold longer = more power)\n- **Fire**: Release mouse button to launch banana\n- Visual feedback: Power meter fills while holding, trajectory arc shows predicted path\n\n**Shared Mechanics:**\n- Trajectory prediction line shows approximate path (dotted arc)\n- Power meter (0-100%) displayed near cannon\n- Ammo counter always visible at top\n- Cannot fire while previous banana is still in motion\n\n## Art Style and Visual Theme\n\n**Art Style**: Bright, exaggerated cartoon style with thick outlines and vibrant colors\n\n**Color Palette**:\n- Jungle greens: Various shades from lime to deep forest\n- Tropical accents: Bright yellows, oranges, pinks (flowers, fruits)\n- Sky: Gradient from light blue to warm sunset tones\n- Monkeys: Brown with tan faces, expressive features\n- Bananas: Bright yellow with subtle shading\n\n**Visual Consistency**:\n- All assets use thick black outlines (cartoon style)\n- Slightly exaggerated proportions (big heads, expressive eyes)\n- Smooth, rounded shapes (no sharp angles)\n- Consistent lighting from top-left\n- Playful, bouncy aesthetic\n\n**Asset Approach**:\n- **Generated Assets**: Monkeys, bananas, cannon, jungle background elements\n- **Code Rendering**: UI elements (power meter, ammo counter, trajectory line), simple shapes for platforms\n\n**Scale Reference**:\n- Monkey: ~80\u00d780 pixels\n- Banana: ~40\u00d720 pixels\n- Cannon: ~100\u00d780 pixels\n- Screen: 720\u00d71280 pixels", "persistent_storage_config": null, "game_metadata": {"game_id": "01KSJGE6AJPZ7BB1DTZMVNNEBS", "title": "Banana Blaster", "description": "Aim your cannon and launch bananas to knock down mischievous monkeys in this physics-based jungle arcade game!", "thumbnail_url": "https://d2oir5eh8rty2e.cloudfront.net/assets/images/thumbnail_9f9a3fdb-8cf2-4622-9881-bdcaf2c72f68.webp", "created_at": 1779811465, "updated_at": 1779813016233.738, "art_style": null}}