{"id":1282,"date":"2026-03-18T17:26:01","date_gmt":"2026-03-18T14:26:01","guid":{"rendered":"https:\/\/bantv.ru\/?page_id=1282"},"modified":"2026-04-09T16:01:04","modified_gmt":"2026-04-09T13:01:04","slug":"%d0%b8%d0%b3%d1%80%d0%be%d0%b2%d0%be%d0%b9-%d1%86%d0%b5%d0%bd%d1%82%d1%80-bantv-ggames","status":"publish","type":"page","link":"https:\/\/bantv.ru\/?page_id=1282","title":{"rendered":"\u0418\u0433\u0440\u043e\u0432\u043e\u0439 \u0446\u0435\u043d\u0442\u0440 BanTV GGames"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"1282\" class=\"elementor elementor-1282\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-adb798c elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"adb798c\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-d161c6a\" data-id=\"d161c6a\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-9ba732e elementor-widget elementor-widget-html\" data-id=\"9ba732e\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"ru\">\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\">\r\n    <meta name=\"color-scheme\" content=\"dark only\">\r\n    <meta name=\"theme-color\" content=\"#05060a\">\r\n    <title>BanTV \u2014 Tower Block<\/title>\r\n    <link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.6.0\/css\/all.min.css\">\r\n    <style>\r\n        @import url('https:\/\/fonts.googleapis.com\/css2?family=Space+Grotesk:wght@500;700&family=Inter:wght@400;600&display=swap');\r\n\r\n        *, *::before, *::after {\r\n            margin: 0; padding: 0; box-sizing: border-box;\r\n        }\r\n\r\n        :root {\r\n            --accent1: #623cff;\r\n            --accent2: #e94560;\r\n            --accent3: #78c8ff;\r\n            --bg: #05060a;\r\n            --bg-card: rgba(255,255,255,0.04);\r\n        }\r\n\r\n        body {\r\n            font-family: 'Inter', system-ui, sans-serif;\r\n            background: var(--bg);\r\n            color: #e9f1ff;\r\n            min-height: 100vh;\r\n            min-height: 100dvh;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            overflow: hidden;\r\n            -webkit-tap-highlight-color: transparent;\r\n            -webkit-touch-callout: none;\r\n            user-select: none;\r\n        }\r\n\r\n        \/* === AMBIENT BACKGROUND === *\/\r\n        .ambient-bg {\r\n            position: fixed;\r\n            inset: 0;\r\n            z-index: -1;\r\n            pointer-events: none;\r\n            overflow: hidden;\r\n        }\r\n\r\n        .ambient-bg::before {\r\n            content: '';\r\n            position: absolute;\r\n            width: 500px; height: 500px;\r\n            top: -20%; left: -20%;\r\n            background: radial-gradient(circle, rgba(98,60,255,0.18) 0%, transparent 70%);\r\n            animation: ambientFloat1 20s ease-in-out infinite;\r\n        }\r\n\r\n        .ambient-bg::after {\r\n            content: '';\r\n            position: absolute;\r\n            width: 400px; height: 400px;\r\n            bottom: -15%; right: -15%;\r\n            background: radial-gradient(circle, rgba(233,69,96,0.12) 0%, transparent 70%);\r\n            animation: ambientFloat2 18s ease-in-out infinite;\r\n        }\r\n\r\n        @keyframes ambientFloat1 {\r\n            0%, 100% { transform: translate(0,0) scale(1); }\r\n            33% { transform: translate(80px, 60px) scale(1.15); }\r\n            66% { transform: translate(-40px, 80px) scale(0.9); }\r\n        }\r\n\r\n        @keyframes ambientFloat2 {\r\n            0%, 100% { transform: translate(0,0) scale(1); }\r\n            33% { transform: translate(-60px, -40px) scale(1.1); }\r\n            66% { transform: translate(40px, -60px) scale(0.95); }\r\n        }\r\n\r\n        \/* === CONTAINER === *\/\r\n        .game-container {\r\n            width: 100%;\r\n            max-width: 520px;\r\n            height: 100vh;\r\n            height: 100dvh;\r\n            max-height: 780px;\r\n            margin: 0 auto;\r\n            position: relative;\r\n            display: flex;\r\n            flex-direction: column;\r\n            overflow: hidden;\r\n            background: var(--bg);\r\n        }\r\n\r\n        @media (max-width: 560px) {\r\n            .game-container {\r\n                max-width: 100%;\r\n                max-height: 100vh;\r\n                max-height: 100dvh;\r\n                border-radius: 0;\r\n            }\r\n        }\r\n\r\n        @media (min-width: 561px) {\r\n            .game-container {\r\n                border-radius: 24px;\r\n                box-shadow: 0 20px 60px rgba(0,0,0,0.6), 0 0 80px rgba(98,60,255,0.08);\r\n                border: 1px solid rgba(255,255,255,0.06);\r\n            }\r\n        }\r\n\r\n        \/* === HEADER UI === *\/\r\n        .game-header {\r\n            position: absolute;\r\n            top: 0; left: 0; right: 0;\r\n            display: flex;\r\n            justify-content: space-between;\r\n            align-items: flex-start;\r\n            padding: 16px 20px;\r\n            z-index: 10;\r\n            pointer-events: none;\r\n        }\r\n\r\n        .header-left {\r\n            display: flex;\r\n            flex-direction: column;\r\n            gap: 2px;\r\n        }\r\n\r\n        .brand {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-weight: 700;\r\n            font-size: 18px;\r\n            color: #fff;\r\n            letter-spacing: -0.5px;\r\n        }\r\n\r\n        .brand span {\r\n            background: linear-gradient(135deg, var(--accent1), var(--accent2));\r\n            -webkit-background-clip: text;\r\n            -webkit-text-fill-color: transparent;\r\n            background-clip: text;\r\n        }\r\n\r\n        .score-display {\r\n            display: flex;\r\n            flex-direction: column;\r\n            align-items: flex-end;\r\n            gap: 2px;\r\n        }\r\n\r\n        .score-label {\r\n            font-size: 9px;\r\n            color: rgba(233,241,255,0.35);\r\n            letter-spacing: 2px;\r\n            font-weight: 600;\r\n            text-transform: uppercase;\r\n        }\r\n\r\n        .score-value {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: clamp(28px, 6vw, 36px);\r\n            font-weight: 700;\r\n            color: #fff;\r\n            line-height: 1;\r\n            transition: transform 0.15s ease;\r\n        }\r\n\r\n        .score-value.pop {\r\n            transform: scale(1.3);\r\n            color: var(--accent3);\r\n        }\r\n\r\n        .best-value {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: 12px;\r\n            color: rgba(233,241,255,0.3);\r\n            font-weight: 500;\r\n        }\r\n\r\n        \/* === COMBO INDICATOR === *\/\r\n        .combo-indicator {\r\n            position: absolute;\r\n            top: 50%;\r\n            left: 50%;\r\n            transform: translate(-50%, -50%);\r\n            z-index: 15;\r\n            pointer-events: none;\r\n            text-align: center;\r\n            opacity: 0;\r\n            transition: opacity 0.3s ease;\r\n        }\r\n\r\n        .combo-indicator.active {\r\n            opacity: 1;\r\n        }\r\n\r\n        .combo-text {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: 16px;\r\n            font-weight: 700;\r\n            color: var(--accent3);\r\n            text-transform: uppercase;\r\n            letter-spacing: 4px;\r\n            animation: comboAppear 0.6s ease-out;\r\n        }\r\n\r\n        @keyframes comboAppear {\r\n            0% { transform: scale(0.3) translateY(20px); opacity: 0; }\r\n            50% { transform: scale(1.2) translateY(-5px); opacity: 1; }\r\n            100% { transform: scale(1) translateY(0); opacity: 1; }\r\n        }\r\n\r\n        \/* === CANVAS === *\/\r\n        .canvas-wrapper {\r\n            flex: 1;\r\n            position: relative;\r\n            overflow: hidden;\r\n        }\r\n\r\n        #gameCanvas {\r\n            width: 100%;\r\n            height: 100%;\r\n            display: block;\r\n        }\r\n\r\n        \/* === SCREENS === *\/\r\n        .screen-overlay {\r\n            position: absolute;\r\n            inset: 0;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            z-index: 20;\r\n            background: rgba(5,6,10,0.82);\r\n            backdrop-filter: blur(16px);\r\n            -webkit-backdrop-filter: blur(16px);\r\n            opacity: 1;\r\n            transition: opacity 0.4s ease, visibility 0.4s ease;\r\n            padding: 30px;\r\n        }\r\n\r\n        .screen-overlay.hidden {\r\n            opacity: 0;\r\n            visibility: hidden;\r\n            pointer-events: none;\r\n        }\r\n\r\n        .screen-content {\r\n            text-align: center;\r\n            color: #fff;\r\n            max-width: 320px;\r\n        }\r\n\r\n        .screen-content .game-icon {\r\n            font-size: 48px;\r\n            margin-bottom: 16px;\r\n            display: block;\r\n            animation: floatIcon 3s ease-in-out infinite;\r\n        }\r\n\r\n        @keyframes floatIcon {\r\n            0%, 100% { transform: translateY(0) rotate(-5deg); }\r\n            50% { transform: translateY(-10px) rotate(5deg); }\r\n        }\r\n\r\n        .screen-content h1 {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: clamp(32px, 8vw, 48px);\r\n            font-weight: 700;\r\n            letter-spacing: -2px;\r\n            margin-bottom: 8px;\r\n            line-height: 1.1;\r\n        }\r\n\r\n        .screen-content h1 .gradient-text {\r\n            background: linear-gradient(135deg, var(--accent1), var(--accent2), var(--accent3));\r\n            -webkit-background-clip: text;\r\n            -webkit-text-fill-color: transparent;\r\n            background-clip: text;\r\n        }\r\n\r\n        .screen-content .subtitle {\r\n            color: rgba(233,241,255,0.5);\r\n            font-size: 14px;\r\n            margin-bottom: 28px;\r\n            line-height: 1.5;\r\n        }\r\n\r\n        .final-score-display {\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: clamp(56px, 14vw, 80px);\r\n            font-weight: 700;\r\n            color: #fff;\r\n            line-height: 1;\r\n            margin: 12px 0;\r\n            background: linear-gradient(135deg, #fff, var(--accent3));\r\n            -webkit-background-clip: text;\r\n            -webkit-text-fill-color: transparent;\r\n            background-clip: text;\r\n        }\r\n\r\n        .final-score-label {\r\n            font-size: 11px;\r\n            letter-spacing: 3px;\r\n            color: rgba(233,241,255,0.35);\r\n            text-transform: uppercase;\r\n            font-weight: 600;\r\n        }\r\n\r\n        .new-best-badge {\r\n            display: inline-block;\r\n            padding: 4px 12px;\r\n            background: linear-gradient(135deg, var(--accent2), #ff6b81);\r\n            border-radius: 20px;\r\n            font-size: 11px;\r\n            font-weight: 700;\r\n            letter-spacing: 1px;\r\n            color: #fff;\r\n            margin-top: 8px;\r\n            animation: badgePulse 1.5s ease-in-out infinite;\r\n        }\r\n\r\n        @keyframes badgePulse {\r\n            0%, 100% { box-shadow: 0 0 0 0 rgba(233,69,96,0.4); }\r\n            50% { box-shadow: 0 0 0 8px rgba(233,69,96,0); }\r\n        }\r\n\r\n        .play-btn {\r\n            display: inline-flex;\r\n            align-items: center;\r\n            gap: 10px;\r\n            padding: 16px 40px;\r\n            background: linear-gradient(135deg, var(--accent1), var(--accent2));\r\n            border: none;\r\n            border-radius: 50px;\r\n            color: #fff;\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-size: 16px;\r\n            font-weight: 700;\r\n            letter-spacing: 1px;\r\n            cursor: pointer;\r\n            transition: all 0.3s cubic-bezier(0.23,1,0.32,1);\r\n            box-shadow: 0 10px 30px rgba(98,60,255,0.35);\r\n            margin-top: 8px;\r\n        }\r\n\r\n        .play-btn:hover {\r\n            transform: translateY(-3px) scale(1.03);\r\n            box-shadow: 0 15px 40px rgba(98,60,255,0.5);\r\n        }\r\n\r\n        .play-btn:active {\r\n            transform: translateY(0) scale(0.97);\r\n        }\r\n\r\n        .play-btn i {\r\n            font-size: 18px;\r\n        }\r\n\r\n        \/* === PERFECT FLASH === *\/\r\n        .perfect-flash {\r\n            position: absolute;\r\n            inset: 0;\r\n            z-index: 12;\r\n            pointer-events: none;\r\n            opacity: 0;\r\n            transition: opacity 0.15s ease;\r\n        }\r\n\r\n        .perfect-flash.active {\r\n            opacity: 1;\r\n            animation: flashFade 0.5s ease-out forwards;\r\n        }\r\n\r\n        @keyframes flashFade {\r\n            0% { background: rgba(120,200,255,0.15); }\r\n            100% { background: transparent; }\r\n        }\r\n\r\n        \/* === PERFECT TEXT FLOAT === *\/\r\n        .perfect-text-float {\r\n            position: absolute;\r\n            z-index: 15;\r\n            pointer-events: none;\r\n            font-family: 'Space Grotesk', sans-serif;\r\n            font-weight: 700;\r\n            font-size: 20px;\r\n            color: var(--accent3);\r\n            text-shadow: 0 0 20px rgba(120,200,255,0.5);\r\n            animation: floatUp 1s ease-out forwards;\r\n        }\r\n\r\n        @keyframes floatUp {\r\n            0% { opacity: 1; transform: translateY(0) scale(0.5); }\r\n            30% { transform: translateY(-20px) scale(1.1); }\r\n            100% { opacity: 0; transform: translateY(-80px) scale(0.8); }\r\n        }\r\n\r\n        \/* === PARTICLES CANVAS === *\/\r\n        #particlesCanvas {\r\n            position: absolute;\r\n            inset: 0;\r\n            z-index: 11;\r\n            pointer-events: none;\r\n        }\r\n\r\n        \/* === INSTRUCTIONS === *\/\r\n        .tap-hint {\r\n            position: absolute;\r\n            bottom: 20px;\r\n            left: 50%;\r\n            transform: translateX(-50%);\r\n            z-index: 10;\r\n            display: flex;\r\n            align-items: center;\r\n            gap: 8px;\r\n            padding: 8px 16px;\r\n            background: rgba(255,255,255,0.05);\r\n            border: 1px solid rgba(255,255,255,0.08);\r\n            border-radius: 30px;\r\n            font-size: 11px;\r\n            color: rgba(233,241,255,0.4);\r\n            letter-spacing: 1px;\r\n            text-transform: uppercase;\r\n            font-weight: 600;\r\n            animation: hintPulse 2s ease-in-out infinite;\r\n        }\r\n\r\n        @keyframes hintPulse {\r\n            0%, 100% { opacity: 0.6; }\r\n            50% { opacity: 1; }\r\n        }\r\n\r\n        .tap-hint.hidden {\r\n            display: none;\r\n        }\r\n    <\/style>\r\n<\/head>\r\n<body>\r\n\r\n    <div class=\"ambient-bg\"><\/div>\r\n\r\n    <div class=\"game-container\" id=\"gameContainer\">\r\n        <!-- Header -->\r\n        <div class=\"game-header\">\r\n            <div class=\"header-left\">\r\n                <div class=\"brand\">BANTV<span>.RU<\/span><\/div>\r\n                <div class=\"best-value\">BEST: <span id=\"bestScore\">0<\/span><\/div>\r\n            <\/div>\r\n            <div class=\"score-display\">\r\n                <span class=\"score-label\">SCORE<\/span>\r\n                <span class=\"score-value\" id=\"currentScore\">0<\/span>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Canvas Area -->\r\n        <div class=\"canvas-wrapper\" id=\"canvasWrapper\">\r\n            <canvas id=\"gameCanvas\"><\/canvas>\r\n            <canvas id=\"particlesCanvas\"><\/canvas>\r\n            <div class=\"perfect-flash\" id=\"perfectFlash\"><\/div>\r\n            <div class=\"combo-indicator\" id=\"comboIndicator\">\r\n                <div class=\"combo-text\" id=\"comboText\"><\/div>\r\n            <\/div>\r\n            <div class=\"tap-hint\" id=\"tapHint\">\r\n                <i class=\"fa-solid fa-hand-pointer\"><\/i>\r\n                \u041d\u0430\u0436\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0431\u043b\u043e\u043a\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Menu Screen -->\r\n        <div class=\"screen-overlay\" id=\"menuScreen\">\r\n            <div class=\"screen-content\">\r\n                <span class=\"game-icon\">\ud83c\udfd7\ufe0f<\/span>\r\n                <h1>TOWER<br><span class=\"gradient-text\">BLOCK<\/span><\/h1>\r\n                <p class=\"subtitle\">\u041a\u043b\u0438\u043a\u0430\u0439 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442,<br>\u0447\u0442\u043e\u0431\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0431\u0430\u0448\u043d\u044e<\/p>\r\n                <button class=\"play-btn\" id=\"playBtn\">\r\n                    <i class=\"fa-solid fa-play\"><\/i>\r\n                    \u0418\u0413\u0420\u0410\u0422\u042c\r\n                <\/button>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <!-- Game Over Screen -->\r\n        <div class=\"screen-overlay hidden\" id=\"gameOverScreen\">\r\n            <div class=\"screen-content\">\r\n                <span class=\"game-icon\">\ud83d\udca5<\/span>\r\n                <h1 style=\"font-size: clamp(24px, 6vw, 32px); color: var(--accent2);\">\u0411\u0410\u0428\u041d\u042f<br>\u0423\u041f\u0410\u041b\u0410!<\/h1>\r\n                <div class=\"final-score-label\">\u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422<\/div>\r\n                <div class=\"final-score-display\" id=\"finalScore\">0<\/div>\r\n                <div id=\"newBestBadge\" class=\"new-best-badge hidden\">\ud83d\udd25 \u041d\u041e\u0412\u042b\u0419 \u0420\u0415\u041a\u041e\u0420\u0414!<\/div>\r\n                <button class=\"play-btn\" id=\"retryBtn\">\r\n                    <i class=\"fa-solid fa-rotate-right\"><\/i>\r\n                    \u0415\u0429\u0401 \u0420\u0410\u0417\r\n                <\/button>\r\n            <\/div>\r\n        <\/div>\r\n    <\/div>\r\n\r\n    <script>\r\n    class TowerGame {\r\n        constructor() {\r\n            this.canvas = document.getElementById('gameCanvas');\r\n            this.ctx = this.canvas.getContext('2d');\r\n            this.pCanvas = document.getElementById('particlesCanvas');\r\n            this.pCtx = this.pCanvas.getContext('2d');\r\n            this.wrapper = document.getElementById('canvasWrapper');\r\n            \r\n            this.scoreEl = document.getElementById('currentScore');\r\n            this.bestEl = document.getElementById('bestScore');\r\n            this.finalScoreEl = document.getElementById('finalScore');\r\n            this.menuScreen = document.getElementById('menuScreen');\r\n            this.gameOverScreen = document.getElementById('gameOverScreen');\r\n            this.tapHint = document.getElementById('tapHint');\r\n            this.comboIndicator = document.getElementById('comboIndicator');\r\n            this.comboText = document.getElementById('comboText');\r\n            this.perfectFlash = document.getElementById('perfectFlash');\r\n            this.newBestBadge = document.getElementById('newBestBadge');\r\n            \r\n            this.running = false;\r\n            this.score = 0;\r\n            this.best = parseInt(localStorage.getItem('bantv_tower_best') || '0');\r\n            this.bestEl.textContent = this.best;\r\n            this.combo = 0;\r\n            this.maxCombo = 0;\r\n            \r\n            this.resize();\r\n            window.addEventListener('resize', () => this.resize());\r\n            \r\n            const trigger = (e) => {\r\n                if (this.running) {\r\n                    e.preventDefault();\r\n                    this.placeBlock();\r\n                }\r\n            };\r\n            this.wrapper.addEventListener('mousedown', trigger);\r\n            this.wrapper.addEventListener('touchstart', trigger, { passive: false });\r\n            \r\n            document.getElementById('playBtn').addEventListener('click', (e) => {\r\n                e.stopPropagation();\r\n                this.start();\r\n            });\r\n            document.getElementById('retryBtn').addEventListener('click', (e) => {\r\n                e.stopPropagation();\r\n                this.start();\r\n            });\r\n            \r\n            \/\/ Prevent context menu\r\n            this.wrapper.addEventListener('contextmenu', e => e.preventDefault());\r\n            \r\n            this.particles = [];\r\n            this.fallingPieces = [];\r\n            this.stars = this.generateStars();\r\n            \r\n            this.animate();\r\n        }\r\n\r\n        resize() {\r\n            const rect = this.wrapper.getBoundingClientRect();\r\n            this.W = rect.width;\r\n            this.H = rect.height;\r\n            const dpr = window.devicePixelRatio || 1;\r\n            \r\n            this.canvas.width = this.W * dpr;\r\n            this.canvas.height = this.H * dpr;\r\n            this.ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\r\n            \r\n            this.pCanvas.width = this.W * dpr;\r\n            this.pCanvas.height = this.H * dpr;\r\n            this.pCtx.setTransform(dpr, 0, 0, dpr, 0, 0);\r\n        }\r\n\r\n        generateStars() {\r\n            const stars = [];\r\n            for (let i = 0; i < 80; i++) {\r\n                stars.push({\r\n                    x: Math.random(),\r\n                    y: Math.random(),\r\n                    size: Math.random() * 1.5 + 0.5,\r\n                    alpha: Math.random() * 0.5 + 0.1,\r\n                    twinkleSpeed: Math.random() * 0.02 + 0.005\r\n                });\r\n            }\r\n            return stars;\r\n        }\r\n\r\n        drawStars(time) {\r\n            this.stars.forEach(star => {\r\n                const alpha = star.alpha * (0.5 + 0.5 * Math.sin(time * star.twinkleSpeed));\r\n                this.ctx.fillStyle = `rgba(233,241,255,${alpha})`;\r\n                this.ctx.beginPath();\r\n                this.ctx.arc(star.x * this.W, star.y * this.H, star.size, 0, Math.PI * 2);\r\n                this.ctx.fill();\r\n            });\r\n        }\r\n\r\n        init() {\r\n            this.running = false;\r\n            this.score = 0;\r\n            this.combo = 0;\r\n            this.maxCombo = 0;\r\n            this.scoreEl.textContent = '0';\r\n            this.viewY = 0;\r\n            this.targetViewY = 0;\r\n            this.blocks = [];\r\n            this.fallingPieces = [];\r\n            this.particles = [];\r\n            this.baseW = Math.min(140, this.W * 0.3);\r\n            this.baseD = this.baseW;\r\n            this.blockH = Math.min(32, this.H * 0.045);\r\n            this.shakeAmount = 0;\r\n            this.shakeDecay = 0.9;\r\n        }\r\n\r\n        start() {\r\n            this.init();\r\n            this.running = true;\r\n            this.menuScreen.classList.add('hidden');\r\n            this.gameOverScreen.classList.add('hidden');\r\n            this.tapHint.classList.remove('hidden');\r\n            \r\n            \/\/ Base block\r\n            this.blocks.push({\r\n                x: 0, z: 0,\r\n                w: this.baseW, d: this.baseD,\r\n                y: 0, colorIndex: 0,\r\n                perfect: false\r\n            });\r\n\r\n            this.spawnBlock();\r\n        }\r\n\r\n        getColor(index, lightness) {\r\n            const hue = (210 + index * 12) % 360;\r\n            return `hsl(${hue}, 65%, ${lightness}%)`;\r\n        }\r\n\r\n        spawnBlock() {\r\n            const prev = this.blocks[this.blocks.length - 1];\r\n            const isX = this.blocks.length % 2 === 0;\r\n            const speed = 1.8 + (this.score * 0.08);\r\n\r\n            this.blocks.push({\r\n                x: isX ? -300 : prev.x,\r\n                z: isX ? prev.z : -300,\r\n                w: prev.w, d: prev.d,\r\n                y: prev.y + this.blockH,\r\n                colorIndex: this.blocks.length,\r\n                moving: true,\r\n                axis: isX ? 'x' : 'z',\r\n                dir: 1,\r\n                speed: Math.min(speed, 7),\r\n                perfect: false\r\n            });\r\n        }\r\n\r\n        placeBlock() {\r\n            this.tapHint.classList.add('hidden');\r\n            \r\n            const current = this.blocks[this.blocks.length - 1];\r\n            const prev = this.blocks[this.blocks.length - 2];\r\n            const axis = current.axis;\r\n            const diff = current[axis] - prev[axis];\r\n            const size = axis === 'x' ? current.w : current.d;\r\n            const overlap = size - Math.abs(diff);\r\n\r\n            \/\/ Get center of block for particle effects\r\n            const screenPos = this.toScreen(current.x, current.y, current.z);\r\n\r\n            if (overlap > 0) {\r\n                if (Math.abs(diff) < 6) {\r\n                    \/\/ PERFECT!\r\n                    current[axis] = prev[axis];\r\n                    current.perfect = true;\r\n                    this.combo++;\r\n                    if (this.combo > this.maxCombo) this.maxCombo = this.combo;\r\n                    \r\n                    \/\/ Show combo\r\n                    if (this.combo >= 2) {\r\n                        this.showCombo(this.combo);\r\n                    }\r\n                    \r\n                    this.showPerfectText(screenPos.x, screenPos.y);\r\n                    this.spawnPerfectParticles(screenPos.x, screenPos.y);\r\n                    this.triggerPerfectFlash();\r\n                    \r\n                    if (navigator.vibrate) navigator.vibrate(30);\r\n                } else {\r\n                    this.combo = 0;\r\n                    \/\/ Trim block\r\n                    const fallX = axis === 'x' ? \r\n                        (diff > 0 ? current.x + current.w\/2 - overlap\/2 : current.x - current.w\/2 - overlap\/2) :\r\n                        current.x;\r\n                    const fallZ = axis === 'z' ?\r\n                        (diff > 0 ? current.z + current.d\/2 - overlap\/2 : current.z - current.d\/2 - overlap\/2) :\r\n                        current.z;\r\n                    const fallW = axis === 'x' ? Math.abs(diff) : (axis === 'x' ? current.w : Math.abs(diff));\r\n                    const fallD = axis === 'z' ? Math.abs(diff) : (axis === 'z' ? current.d : Math.abs(diff));\r\n                    \r\n                    if (axis === 'x') {\r\n                        current.w = overlap;\r\n                        current.x = prev.x + diff \/ 2;\r\n                    } else {\r\n                        current.d = overlap;\r\n                        current.z = prev.z + diff \/ 2;\r\n                    }\r\n                    \r\n                    \/\/ Falling piece\r\n                    this.fallingPieces.push({\r\n                        x: fallX, z: fallZ,\r\n                        w: Math.abs(diff), d: axis === 'z' ? current.d : current.w,\r\n                        y: current.y,\r\n                        vy: 0,\r\n                        colorIndex: current.colorIndex,\r\n                        rotation: 0,\r\n                        rotSpeed: (Math.random() - 0.5) * 0.1\r\n                    });\r\n                }\r\n\r\n                current.moving = false;\r\n                this.score++;\r\n                this.scoreEl.textContent = this.score;\r\n                \r\n                \/\/ Score pop animation\r\n                this.scoreEl.classList.add('pop');\r\n                setTimeout(() => this.scoreEl.classList.remove('pop'), 150);\r\n                \r\n                \/\/ Camera\r\n                if (this.score > 2) {\r\n                    this.targetViewY += this.blockH;\r\n                }\r\n                \r\n                this.spawnBlock();\r\n            } else {\r\n                this.endGame();\r\n            }\r\n        }\r\n\r\n        endGame() {\r\n            this.running = false;\r\n            this.shakeAmount = 12;\r\n            \r\n            \/\/ Make current block fall\r\n            const current = this.blocks[this.blocks.length - 1];\r\n            current.moving = false;\r\n            this.fallingPieces.push({\r\n                x: current.x, z: current.z,\r\n                w: current.w, d: current.d,\r\n                y: current.y,\r\n                vy: 0,\r\n                colorIndex: current.colorIndex,\r\n                rotation: 0,\r\n                rotSpeed: (Math.random() - 0.5) * 0.15\r\n            });\r\n            \r\n            \/\/ Spawn failure particles\r\n            const sp = this.toScreen(current.x, current.y, current.z);\r\n            this.spawnFailParticles(sp.x, sp.y);\r\n            \r\n            if (navigator.vibrate) navigator.vibrate([50, 30, 100]);\r\n            \r\n            \/\/ Update best\r\n            let isNewBest = false;\r\n            if (this.score > this.best) {\r\n                this.best = this.score;\r\n                localStorage.setItem('bantv_tower_best', this.best);\r\n                isNewBest = true;\r\n            }\r\n            this.bestEl.textContent = this.best;\r\n            \r\n            \/\/ Show game over after delay\r\n            setTimeout(() => {\r\n                this.finalScoreEl.textContent = this.score;\r\n                if (isNewBest) {\r\n                    this.newBestBadge.classList.remove('hidden');\r\n                } else {\r\n                    this.newBestBadge.classList.add('hidden');\r\n                }\r\n                this.gameOverScreen.classList.remove('hidden');\r\n            }, 800);\r\n        }\r\n\r\n        showCombo(count) {\r\n            this.comboIndicator.classList.add('active');\r\n            this.comboText.textContent = `\ud83d\udd25 COMBO x${count}`;\r\n            this.comboText.style.color = count >= 5 ? '#ff6b81' : '#78c8ff';\r\n            \r\n            setTimeout(() => {\r\n                this.comboIndicator.classList.remove('active');\r\n            }, 1200);\r\n        }\r\n\r\n        showPerfectText(x, y) {\r\n            const el = document.createElement('div');\r\n            el.className = 'perfect-text-float';\r\n            el.textContent = this.combo >= 3 ? `\u2726 PERFECT x${this.combo}! \u2726` : '\u2726 PERFECT! \u2726';\r\n            el.style.left = x + 'px';\r\n            el.style.top = y + 'px';\r\n            this.wrapper.appendChild(el);\r\n            setTimeout(() => el.remove(), 1000);\r\n        }\r\n\r\n        triggerPerfectFlash() {\r\n            this.perfectFlash.classList.remove('active');\r\n            void this.perfectFlash.offsetWidth;\r\n            this.perfectFlash.classList.add('active');\r\n        }\r\n\r\n        spawnPerfectParticles(x, y) {\r\n            for (let i = 0; i < 20; i++) {\r\n                const angle = (Math.PI * 2 * i) \/ 20;\r\n                const speed = Math.random() * 4 + 2;\r\n                this.particles.push({\r\n                    x, y,\r\n                    vx: Math.cos(angle) * speed,\r\n                    vy: Math.sin(angle) * speed - 2,\r\n                    life: 1,\r\n                    decay: 0.015 + Math.random() * 0.01,\r\n                    size: Math.random() * 4 + 2,\r\n                    color: `hsl(${190 + Math.random() * 40}, 80%, 70%)`\r\n                });\r\n            }\r\n        }\r\n\r\n        spawnFailParticles(x, y) {\r\n            for (let i = 0; i < 30; i++) {\r\n                const angle = Math.random() * Math.PI * 2;\r\n                const speed = Math.random() * 6 + 1;\r\n                this.particles.push({\r\n                    x, y,\r\n                    vx: Math.cos(angle) * speed,\r\n                    vy: Math.sin(angle) * speed - 3,\r\n                    life: 1,\r\n                    decay: 0.01 + Math.random() * 0.008,\r\n                    size: Math.random() * 5 + 2,\r\n                    color: `hsl(${0 + Math.random() * 30}, 80%, 60%)`\r\n                });\r\n            }\r\n        }\r\n\r\n        updateParticles() {\r\n            this.pCtx.clearRect(0, 0, this.W, this.H);\r\n            \r\n            for (let i = this.particles.length - 1; i >= 0; i--) {\r\n                const p = this.particles[i];\r\n                p.x += p.vx;\r\n                p.y += p.vy;\r\n                p.vy += 0.08;\r\n                p.life -= p.decay;\r\n                \r\n                if (p.life <= 0) {\r\n                    this.particles.splice(i, 1);\r\n                    continue;\r\n                }\r\n                \r\n                this.pCtx.globalAlpha = p.life;\r\n                this.pCtx.fillStyle = p.color;\r\n                this.pCtx.beginPath();\r\n                this.pCtx.arc(p.x, p.y, p.size * p.life, 0, Math.PI * 2);\r\n                this.pCtx.fill();\r\n            }\r\n            this.pCtx.globalAlpha = 1;\r\n        }\r\n\r\n        updateFallingPieces() {\r\n            for (let i = this.fallingPieces.length - 1; i >= 0; i--) {\r\n                const p = this.fallingPieces[i];\r\n                p.vy += 0.5;\r\n                p.y -= p.vy;\r\n                p.rotation += p.rotSpeed;\r\n                \r\n                if (p.y < -500) {\r\n                    this.fallingPieces.splice(i, 1);\r\n                }\r\n            }\r\n        }\r\n\r\n        toScreen(x, y, z) {\r\n            return {\r\n                x: this.W \/ 2 + (x - z) * 0.6,\r\n                y: this.H * 0.82 - y - (x + z) * 0.3 + this.viewY\r\n            };\r\n        }\r\n\r\n        drawCube(b, alpha = 1) {\r\n            const { x, y, z, w, d, colorIndex, perfect } = b;\r\n            const h = this.blockH;\r\n            const baseHue = (210 + colorIndex * 12) % 360;\r\n            \r\n            let topL = 52;\r\n            let leftL = 38;\r\n            let rightL = 44;\r\n            \r\n            if (perfect) {\r\n                topL = 65;\r\n                leftL = 50;\r\n                rightL = 58;\r\n            }\r\n            \r\n            const colorTop = `hsla(${baseHue}, 70%, ${topL}%, ${alpha})`;\r\n            const colorLeft = `hsla(${baseHue + 10}, 65%, ${leftL}%, ${alpha})`;\r\n            const colorRight = `hsla(${baseHue - 10}, 65%, ${rightL}%, ${alpha})`;\r\n\r\n            const p1 = this.toScreen(x - w\/2, y, z - d\/2);\r\n            const p2 = this.toScreen(x + w\/2, y, z - d\/2);\r\n            const p3 = this.toScreen(x + w\/2, y, z + d\/2);\r\n            const p4 = this.toScreen(x - w\/2, y, z + d\/2);\r\n            \r\n            const t1 = this.toScreen(x - w\/2, y + h, z - d\/2);\r\n            const t2 = this.toScreen(x + w\/2, y + h, z - d\/2);\r\n            const t3 = this.toScreen(x + w\/2, y + h, z + d\/2);\r\n            const t4 = this.toScreen(x - w\/2, y + h, z + d\/2);\r\n\r\n            \/\/ Shadow\r\n            if (alpha === 1) {\r\n                this.ctx.fillStyle = 'rgba(0,0,0,0.15)';\r\n                this.ctx.beginPath();\r\n                const s1 = this.toScreen(x - w\/2, y - 2, z - d\/2);\r\n                const s2 = this.toScreen(x + w\/2, y - 2, z - d\/2);\r\n                const s3 = this.toScreen(x + w\/2, y - 2, z + d\/2);\r\n                const s4 = this.toScreen(x - w\/2, y - 2, z + d\/2);\r\n                this.ctx.moveTo(s1.x, s1.y);\r\n                this.ctx.lineTo(s2.x, s2.y);\r\n                this.ctx.lineTo(s3.x, s3.y);\r\n                this.ctx.lineTo(s4.x, s4.y);\r\n                this.ctx.fill();\r\n            }\r\n\r\n            \/\/ Left face\r\n            this.ctx.fillStyle = colorLeft;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(p1.x, p1.y);\r\n            this.ctx.lineTo(p4.x, p4.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.lineTo(t1.x, t1.y);\r\n            this.ctx.closePath();\r\n            this.ctx.fill();\r\n\r\n            \/\/ Right face\r\n            this.ctx.fillStyle = colorRight;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(p4.x, p4.y);\r\n            this.ctx.lineTo(p3.x, p3.y);\r\n            this.ctx.lineTo(t3.x, t3.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.closePath();\r\n            this.ctx.fill();\r\n\r\n            \/\/ Top face\r\n            this.ctx.fillStyle = colorTop;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(t1.x, t1.y);\r\n            this.ctx.lineTo(t2.x, t2.y);\r\n            this.ctx.lineTo(t3.x, t3.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.closePath();\r\n            this.ctx.fill();\r\n            \r\n            \/\/ Edge highlight\r\n            if (alpha === 1) {\r\n                this.ctx.strokeStyle = 'rgba(255,255,255,0.12)';\r\n                this.ctx.lineWidth = 1;\r\n                \r\n                this.ctx.beginPath();\r\n                this.ctx.moveTo(t1.x, t1.y);\r\n                this.ctx.lineTo(t2.x, t2.y);\r\n                this.ctx.lineTo(t3.x, t3.y);\r\n                this.ctx.lineTo(t4.x, t4.y);\r\n                this.ctx.closePath();\r\n                this.ctx.stroke();\r\n                \r\n                \/\/ Perfect glow\r\n                if (perfect) {\r\n                    this.ctx.strokeStyle = 'rgba(120,200,255,0.4)';\r\n                    this.ctx.lineWidth = 2;\r\n                    this.ctx.stroke();\r\n                    \r\n                    \/\/ Glow effect\r\n                    this.ctx.shadowColor = 'rgba(120,200,255,0.3)';\r\n                    this.ctx.shadowBlur = 15;\r\n                    this.ctx.stroke();\r\n                    this.ctx.shadowBlur = 0;\r\n                }\r\n            }\r\n        }\r\n\r\n        drawFallingPiece(p) {\r\n            const { x, y, z, w, d, colorIndex, rotation } = p;\r\n            const h = this.blockH;\r\n            const baseHue = (210 + colorIndex * 12) % 360;\r\n            const alpha = Math.max(0, 0.7 - Math.abs(y) * 0.001);\r\n            \r\n            if (alpha <= 0) return;\r\n\r\n            \/\/ Apply rotation offset\r\n            const rx = Math.sin(rotation) * 5;\r\n            const rz = Math.cos(rotation) * 3;\r\n\r\n            const colorLeft = `hsla(${baseHue + 10}, 65%, 38%, ${alpha})`;\r\n            const colorRight = `hsla(${baseHue - 10}, 65%, 44%, ${alpha})`;\r\n            const colorTop = `hsla(${baseHue}, 70%, 52%, ${alpha})`;\r\n\r\n            const p1 = this.toScreen(x - w\/2 + rx, y, z - d\/2 + rz);\r\n            const p4 = this.toScreen(x - w\/2 + rx, y, z + d\/2 + rz);\r\n            const p3 = this.toScreen(x + w\/2 + rx, y, z + d\/2 + rz);\r\n            \r\n            const t1 = this.toScreen(x - w\/2 + rx, y + h, z - d\/2 + rz);\r\n            const t2 = this.toScreen(x + w\/2 + rx, y + h, z - d\/2 + rz);\r\n            const t3 = this.toScreen(x + w\/2 + rx, y + h, z + d\/2 + rz);\r\n            const t4 = this.toScreen(x - w\/2 + rx, y + h, z + d\/2 + rz);\r\n\r\n            this.ctx.fillStyle = colorLeft;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(p1.x, p1.y);\r\n            this.ctx.lineTo(p4.x, p4.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.lineTo(t1.x, t1.y);\r\n            this.ctx.fill();\r\n\r\n            this.ctx.fillStyle = colorRight;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(p4.x, p4.y);\r\n            this.ctx.lineTo(p3.x, p3.y);\r\n            this.ctx.lineTo(t3.x, t3.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.fill();\r\n\r\n            this.ctx.fillStyle = colorTop;\r\n            this.ctx.beginPath();\r\n            this.ctx.moveTo(t1.x, t1.y);\r\n            this.ctx.lineTo(t2.x, t2.y);\r\n            this.ctx.lineTo(t3.x, t3.y);\r\n            this.ctx.lineTo(t4.x, t4.y);\r\n            this.ctx.fill();\r\n        }\r\n\r\n        animate() {\r\n            requestAnimationFrame(() => this.animate());\r\n\r\n            const time = performance.now();\r\n            \r\n            this.ctx.clearRect(0, 0, this.W, this.H);\r\n            \r\n            \/\/ Background gradient\r\n            const bgGrad = this.ctx.createLinearGradient(0, 0, 0, this.H);\r\n            bgGrad.addColorStop(0, '#0d1225');\r\n            bgGrad.addColorStop(1, '#05060a');\r\n            this.ctx.fillStyle = bgGrad;\r\n            this.ctx.fillRect(0, 0, this.W, this.H);\r\n            \r\n            \/\/ Stars\r\n            this.drawStars(time);\r\n            \r\n            \/\/ Smooth camera\r\n            this.viewY += (this.targetViewY - this.viewY) * 0.08;\r\n            \r\n            \/\/ Screen shake\r\n            let shakeX = 0, shakeY = 0;\r\n            if (this.shakeAmount > 0.5) {\r\n                shakeX = (Math.random() - 0.5) * this.shakeAmount;\r\n                shakeY = (Math.random() - 0.5) * this.shakeAmount;\r\n                this.shakeAmount *= this.shakeDecay;\r\n            }\r\n            \r\n            this.ctx.save();\r\n            this.ctx.translate(shakeX, shakeY);\r\n\r\n            \/\/ Update moving block\r\n            if (this.running) {\r\n                const current = this.blocks[this.blocks.length - 1];\r\n                if (current && current.moving) {\r\n                    current[current.axis] += current.speed * current.dir;\r\n                    if (current[current.axis] > 280) current.dir = -1;\r\n                    if (current[current.axis] < -280) current.dir = 1;\r\n                }\r\n            }\r\n\r\n            \/\/ Update falling pieces\r\n            this.updateFallingPieces();\r\n            \r\n            \/\/ Draw blocks (last 25 for performance)\r\n            const start = Math.max(0, this.blocks.length - 25);\r\n            for (let i = start; i < this.blocks.length; i++) {\r\n                this.drawCube(this.blocks[i]);\r\n            }\r\n            \r\n            \/\/ Draw falling pieces\r\n            for (const p of this.fallingPieces) {\r\n                this.drawFallingPiece(p);\r\n            }\r\n\r\n            this.ctx.restore();\r\n            \r\n            \/\/ Update particles\r\n            this.updateParticles();\r\n        }\r\n    }\r\n\r\n    \/\/ Initialize game\r\n    const game = new TowerGame();\r\n    <\/script>\r\n<\/body>\r\n<\/html>\r\n\r\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>BanTV \u2014 Tower Block BANTV.RU BEST: 0 SCORE 0 \u041d\u0430\u0436\u043c\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0431\u043b\u043e\u043a \ud83c\udfd7\ufe0f TOWERBLOCK \u041a\u043b\u0438\u043a\u0430\u0439 \u0432 \u043d\u0443\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442,\u0447\u0442\u043e\u0431\u044b \u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0431\u0430\u0448\u043d\u044e \u0418\u0413\u0420\u0410\u0422\u042c \ud83d\udca5 \u0411\u0410\u0428\u041d\u042f\u0423\u041f\u0410\u041b\u0410! \u0420\u0415\u0417\u0423\u041b\u042c\u0422\u0410\u0422 0 \ud83d\udd25 \u041d\u041e\u0412\u042b\u0419 \u0420\u0415\u041a\u041e\u0420\u0414! \u0415\u0429\u0401 \u0420\u0410\u0417<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"inspiro_hide_title":false,"footnotes":""},"class_list":["post-1282","page","type-page","status-publish","hentry"],"featured_media_urls":[],"_links":{"self":[{"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/pages\/1282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bantv.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1282"}],"version-history":[{"count":16,"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/pages\/1282\/revisions"}],"predecessor-version":[{"id":1410,"href":"https:\/\/bantv.ru\/index.php?rest_route=\/wp\/v2\/pages\/1282\/revisions\/1410"}],"wp:attachment":[{"href":"https:\/\/bantv.ru\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}