PageSourceSearch

https://dragon-punks-hero.netlify.app/

html dragon-punks-hero.netlify.app collected 2026-10-03 10:39:50 UTC 47,124 bytes, 1,162 lines download raw bytes

1<!DOCTYPE html>
2<html lang="en">
3<head>
4    <meta charset="UTF-8">
5<!-- This site is hosted on Netlify. Anyone can build and deploy a site
6     like this one for free: https://netlify.new/?utm_campaign=loops&utm_source=ai-legible&utm_medium=owned&utm_content=comment&utm_id=6c2f0266-a311-41bd-8eaa-291f32a2a992
7     Netlify hosting facts for this site: static/SSR served via Netlify Edge. -->
8    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
9    <title>Dragon Punks Hero</title>
10    <style>
11        body {
12            margin: 0;
13            overflow: hidden;
14            background-color: #222;
15            text-align: center;
16            touch-action: none;
17        }
18        #gameContainer {
19            position: relative;
20            margin: auto;
21            max-width: 768px;
22            width: 100%;
23            height: 100vh;
24        }
25        #backgroundImage {
26            position: absolute;
27            top: 0;
28            left: 0;
29            width: 100%;
30            height: 100%;
31            object-fit: cover;
32            z-index: 0;
33        }
34        canvas {
35            display: block;
36            background: transparent;
37            margin: auto;
38            max-width: 100%;
39            max-height: 100vh;
40            position: relative;
41            z-index: 1;
42        }
43        #playAgain {
44            display: none;
45            position: absolute;
46            top: 60%;
47            left: 50%;
48            transform: translate(-50%, -50%);
49            font-size: 20px;
50            padding: 10px 20px;
51            cursor: pointer;
52            background-color: #000000;
53            color: #00FF00;
54            border: none;
55            border-radius: 8px;
56            z-index: 10;
57            touch-action: manipulation;
58            text-shadow: 0 0 5px #00FF00, 0 0 10px #00FF00;
59            animation: pulseGlow 1.5s infinite alternate;
60        }
61        #playAgain:hover {
62            color: #FFFFFF;
63            text-shadow: 0 0 5px #FFFFFF, 0 0 10px #FFFFFF;
64        }
65        #tapToStart {
66            display: none;
67            position: absolute;
68            top: 70%;
69            left: 50%;
70            transform: translate(-50%, -50%);
71            font-size: 24px;
72            padding: 10px 20px;
73            cursor: pointer;
74            background-color: #000000;
75            color: #00FF00;
76            border: none;
77            border-radius: 8px;
78            z-index: 10;
79            touch-action: manipulation;
80            text-shadow: 0 0 5px #00FF00, 0 0 10px #00FF00;
81            animation: pulseGlow 1.5s infinite alternate;
82        }
83        #tapToStart:hover {
84            color: #FFFFFF;
85            text-shadow: 0 0 5px #FFFFFF, 0 0 10px #FFFFFF;
86        }
87        #showLeaderboard {
88            display: none;
89            position: absolute;
90            top: 80%;
91            left: 50%;
92            transform: translate(-50%, -50%);
93            font-size: 24px;
94            padding: 10px 20px;
95            cursor: pointer;
96            background-color: #000000;
97            color: #00FF00;
98            border: none;
99            border-radius: 8px;
100            z-index: 10;
101            touch-action: manipulation;
102            text-shadow: 0 0 5px #00FF00, 0 0 10px #00FF00;
103            animation: pulseGlow 1.5s infinite alternate;
104        }
105        #showLeaderboard:hover {
106            color: #FFFFFF;
107            text-shadow: 0 0 5px #FFFFFF, 0 0 10px #FFFFFF;
108        }
109        #leaderboard {
110            position: absolute;
111            top: 50%;
112            left: 50%;
113            transform: translate(-50%, -50%);
114            background: rgba(255, 255, 255, 0.9);
115            padding: 10px;
116            border-radius: 5px;
117            font-size: 14px;
118            max-height: 200px;
119            overflow-y: auto;
120            z-index: 10;
121            display: none;
122        }
123        @keyframes pulseGlow {
124            0% {
125                text-shadow: 0 0 5px #00FF00, 0 0 10px #00FF00, 0 0 15px #00FF00;
126            }
127            100% {
128                text-shadow: 0 0 10px #00FF00, 0 0 20px #00FF00, 0 0 30px #00FF00;
129            }
130        }
131    </style>
132</head>
133<body>
134    <div id="gameContainer">
135        <img id="backgroundImage" src="bg.jpg" alt="Background">
136        <canvas id="gameCanvas"></canvas>
137        <button id="playAgain" onclick="restartGame()">Play Again</button>
138        <button id="tapToStart" onclick="startGame()">TAP TO START</button>
139        <!-- Temporarily disabled Leaderboard button -->
140        <!-- <button id="showLeaderboard" onclick="toggleLeaderboard()">LEADERBOARD</button> -->
141        <div id="leaderboard"></div>
142    </div>
143    <audio id="bgMusic" loop preload="auto">
144        <source src="cyberpunk_8bit_soundtrack.wav" type="audio/wav">
145    </audio>
146    <audio id="hitSound" preload="auto">
147        <source src="oof.mp3" type="audio/mpeg">
148    </audio>
149    <audio id="coinSound" preload="auto">
150        <source src="coin.mp3" type="audio/mpeg">
151    </audio>
152    <audio id="powerUpSound" preload="auto">
153        <source src="yes.mp3" type="audio/mpeg">
154    </audio>
155    <audio id="evilLaughSound" preload="auto">
156        <source src="evil-laugh.mp3" type="audio/mpeg">
157    </audio>
158    <!-- Temporarily disabled ethers.js script -->
159    <!-- 
159<script src="https://cdn.ethers.io/lib/ethers-5.7.2.umd.min.js" type="text/javascript"></script>
159 -->
160    
160<script>
161        const canvas = document.getElementById("gameCanvas");
162        const ctx = canvas.getContext("2d");
163        const bgMusic = document.getElementById("bgMusic");
164        const hitSound = document.getElementById("hitSound");
165        const coinSound = document.getElementById("coinSound");
166        const powerUpSound = document.getElementById("powerUpSound");
167        const evilLaughSound = document.getElementById("evilLaughSound");
168        const tapToStartButton = document.getElementById("tapToStart");
169
170        // Temporarily disabled Web3 variables
171        /*
172        let provider;
173        let signer;
174        let walletAddress = null;
175
176        // Abstract Testnet configuration
177        const abstractTestnet = {
178            chainId: "0x2B8C", // 11124 in hex
179            chainName: "Abstract Testnet",
180            rpcUrls: ["https://api.testnet.abs.xyz"],
181            nativeCurrency: {
182                name: "Ether",
183                symbol: "ETH",
184                decimals: 18
185            },
186            blockExplorerUrls: ["https://explorer.testnet.abs.xyz"]
187        };
188
189        // Replace with your deployed contract address on Abstract Testnet
190        const contractAddress = "YOUR_CONTRACT_ADDRESS"; // e.g., "0x1234..."
191        const abi = [
192            {
193                "inputs": [{"internalType": "uint256", "name": "_score", "type": "uint256"}],
194                "name": "submitScore",
195                "outputs": [],
196                "stateMutability": "nonpayable",
197                "type": "function"
198            },
199            {
200                "inputs": [],
201                "name": "getLeaderboard",
202                "outputs": [
203                    {
204                        "components": [
205                            {"internalType": "address", "name": "player", "type": "address"},
206                            {"internalType": "uint256", "name": "score", "type": "uint256"}
207                        ],
208                        "internalType": "struct Leaderboard.PlayerScore[]",
209                        "name": "",
210                        "type": "tuple[]"
211                    }
212                ],
213                "stateMutability": "view",
214                "type": "function"
215            }
216        ];
217        let contract;
218        */
219
220        function resizeCanvas() {
221            canvas.width = window.innerWidth;
222            canvas.height = window.innerHeight;
223            if (window.innerWidth > 768) {
224                canvas.width = Math.min(768, window.innerWidth);
225                canvas.style.margin = "0 auto";
226            }
227            document.getElementById("gameContainer").style.width = canvas.width + "px";
228            document.getElementById("gameContainer").style.height = canvas.height + "px";
229        }
230        resizeCanvas();
231        window.addEventListener('resize', resizeCanvas);
232
233        const dragonImg = new Image();
234        const rockImg = new Image();
235        const coinImg = new Image();
236        const speedBoostImg = new Image();
237        const invincibilityImg = new Image();
238        const torchBombImg = new Image();
239        const titleLogoImg = new Image();
240        let imagesLoaded = 0;
241        let audiosLoaded = 0;
242        const totalImages = 7;
243        const totalAudios = 5;
244
245        let frameTime = 0;
246        let gameState = "loading";
247        let fallSpeedMultiplier = 1.0;
248        let touchHeld = false;
249
250        const loadingTimeout = setTimeout(() => {
251            if (gameState === "loading") {
252                console.warn(`Timeout triggered. Not all assets loaded (Images: ${imagesLoaded}/${totalImages}, Audios: ${audiosLoaded}/${totalAudios}). Forcing transition to title screen.`);
253                gameState = "title";
254                tapToStartButton.style.display = "block";
255                // document.getElementById("showLeaderboard").style.display = "block"; // Disabled
256            }
257        }, 10000);
258
259        function drawLoadingMessage() {
260            ctx.clearRect(0, 0, canvas.width, canvas.height);
261            ctx.fillStyle = "white";
262            ctx.font = "40px Arial";
263            ctx.textAlign = "center";
264            ctx.fillText("Loading...", canvas.width / 2, canvas.height / 2);
265
266            const dotSpacing = 20;
267            const dotRadius = 5;
268            const dotY = canvas.height / 2 + 30;
269            const dotStartX = canvas.width / 2 - dotSpacing * 2;
270
271            for (let i = 0; i < 5; i++) {
272                const alpha = Math.sin((frameTime * 0.1 + i * Math.PI / 2.5)) * 0.5 + 0.5;
273                ctx.fillStyle = `rgba(255, 255, 255, ${alpha})`;
274                ctx.beginPath();
275                ctx.arc(dotStartX + i * dotSpacing, dotY, dotRadius, 0, Math.PI * 2);
276                ctx.fill();
277                ctx.closePath();
278            }
279            frameTime++;
280        }
281
282        function assetLoaded() {
283            if (this instanceof HTMLImageElement) {
284                imagesLoaded++;
285                console.log(`Image loaded: ${this.src}, Total: ${imagesLoaded}/${totalImages}`);
286            } else if (this instanceof HTMLAudioElement) {
287                audiosLoaded++;
288                console.log(`Audio loaded: ${this.id}, Total: ${audiosLoaded}/${totalAudios}`);
289            }
290            if (imagesLoaded === totalImages && audiosLoaded === totalAudios) {
291                console.log("All assets loaded, transitioning to title screen");
292                gameState = "title";
293                tapToStartButton.style.display = "block";
294                // document.getElementById("showLeaderboard").style.display = "block"; // Disabled
295            }
296        }
297
298        dragonImg.onerror = () => console.error("Failed to load dragon.png");
299        rockImg.onerror = () => console.error("Failed to load rock.png");
300        coinImg.onerror = () => console.error("Failed to load coin.png");
301        speedBoostImg.onerror = () => console.error("Failed to load speedBoost.png");
302        invincibilityImg.onerror = () => console.error("Failed to load invincibility.png");
303        torchBombImg.onerror = () => console.error("Failed to load torchBomb.png");
304        titleLogoImg.onerror = () => console.error("Failed to load dp-hero-title.png");
305        bgMusic.onerror = () => console.error("Failed to load bgMusic");
306        hitSound.onerror = () => console.error("Failed to load oof.mp3");
307        coinSound.onerror = () => console.error("Failed to load coin.mp3");
308        powerUpSound.onerror = () => console.error("Failed to load yes.mp3");
309        evilLaughSound.onerror = () => console.error("Failed to load evil-laugh.mp3");
310
311        dragonImg.onload = assetLoaded;
312        rockImg.onload = assetLoaded;
313        coinImg.onload = assetLoaded;
314        speedBoostImg.onload = assetLoaded;
315        invincibilityImg.onload = assetLoaded;
316        torchBombImg.onload = assetLoaded;
317        titleLogoImg.onload = assetLoaded;
318        bgMusic.oncanplay = assetLoaded;
319        hitSound.oncanplay = assetLoaded;
320        coinSound.oncanplay = assetLoaded;
321        powerUpSound.oncanplay = assetLoaded;
322        evilLaughSound.oncanplay = assetLoaded;
323
324        dragonImg.src = "dragon.png";
325        rockImg.src = "rock.png";
326        coinImg.src = "coin.png";
327        speedBoostImg.src = "speedBoost.png";
328        invincibilityImg.src = "invincibility.png";
329        torchBombImg.src = "torchBomb.png";
330        titleLogoImg.src = "dp-hero-title.png";
331
332        class Particle {
333            constructor(x, y, radius, color, velocity) {
334                this.x = x;
335                this.y = y;
336                this.radius = radius;
337                this.color = color;
338                this.velocity = velocity;
339                this.life = 1;
340                this.lifeDecay = 0.03;
341            }
342            update() {
343                this.x += this.velocity.x;
344                this.y += this.velocity.y;
345                this.life -= this.lifeDecay;
346            }
347            draw() {
348                ctx.save();
349                ctx.globalAlpha = this.life;
350                ctx.beginPath();
351                ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
352                ctx.fillStyle = this.color;
353                ctx.fill();
354                ctx.closePath();
355                ctx.restore();
356            }
357        }
358
359        class PowerUp {
360            constructor(x, y, type) {
361                this.x = x;
362                this.y = y;
363                this.width = 40;
364                this.height = 40;
365                this.type = type;
366                this.image = type === "speed" ? speedBoostImg : (type === "invincibility" ? invincibilityImg : torchBombImg);
367                this.rotation = 0;
368                this.rotationSpeed = Math.random() * 0.1 - 0.05;
369            }
370            update() {
371                this.y += 1.5 * fallSpeedMultiplier;
372                this.rotation += this.rotationSpeed;
373            }
374            draw() {
375                ctx.save();
376                ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
377                ctx.rotate(this.rotation);
378                ctx.drawImage(this.image, -this.width / 2, -this.height / 2, this.width, this.height);
379                ctx.restore();
380            }
381        }
382
383        class Laser {
384            constructor(x, y) {
385                this.x = x;
386                this.y = y;
387                this.width = 7;
388                this.height = 40;
389                this.speed = -10;
390            }
391            update() {
392                this.y += this.speed;
393            }
394            draw() {
395                ctx.fillStyle = "#FF0000";
396                ctx.fillRect(this.x, this.y, this.width, this.height);
397            }
398        }
399
400        class Rock {
401            constructor(x, y, size, health) {
402                this.x = x;
403                this.y = y;
404                this.width = size;
405                this.height = size;
406                this.rotation = 0;
407                this.rotationSpeed = Math.random() * 0.1 - 0.05;
408                this.health = health;
409            }
410            update() {
411                this.y += 1.5 * fallSpeedMultiplier;
412                this.rotation += this.rotationSpeed;
413            }
414            draw() {
415                ctx.save();
416                ctx.translate(this.x + this.width / 2, this.y + this.height / 2);
417                const glowRadius = this.width * 0.8;
418                const glowAlpha = 0.3 + Math.sin(frameTime * 0.05) * 0.2;
419                const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, glowRadius);
420                gradient.addColorStop(0, `rgba(255, 165, 0, ${glowAlpha})`);
421                gradient.addColorStop(1, "rgba(255, 165, 0, 0)");
422                ctx.fillStyle = gradient;
423                ctx.beginPath();
424                ctx.arc(0, 0, glowRadius, 0, Math.PI * 2, false);
425                ctx.fill();
426                ctx.closePath();
427                ctx.rotate(this.rotation);
428                ctx.drawImage(rockImg, -this.width / 2, -this.height / 2, this.width, this.height);
429                ctx.restore();
430            }
431        }
432
433        class MagmaShockwave {
434            constructor() {
435                this.x = 0;
436                this.y = -50;
437                this.width = canvas.width;
438                this.height = 50;
439                this.speed = 5;
440            }
441            update() {
442                this.y += this.speed;
443            }
444            draw() {
445                ctx.fillStyle = "#FF4500";
446                ctx.fillRect(this.x, this.y, this.width, this.height);
447            }
448        }
449
450        const dragon = {
451            x: canvas.width / 2 - 50,
452            y: canvas.height / 2,
453            width: 100,
454            height: 100,
455            velocityY: 0,
456            gravity: 0.2,
457            speedX: 0,
458            flashing: 0,
459            flying: false,
460            targetScaleX: 1,
461            currentScaleX: 1,
462            speedBoost: false,
463            speedBoostTimer: 0,
464            invincible: false,
465            invincibleTimer: 0,
466            isFlyingInputActive: false,
467            laserCooldowns: [0],
468            invincibilityCount: 0,
469            torchBombTimer: 0,
470            flyScoreTimer: 0,
471            torchBombFlyBonusTimer: 0
472        };
473
474        let rocks = [];
475        let coins = [];
476        let powerUps = [];
477        let particles = [];
478        let lasers = [];
479        let magmaShockwaves = [];
480        let score = 0;
481        let lastLifeScore = 0;
482        let rockSpawnRate = 0.0025;
483        let lives = 3;
484        let musicPlayed = false;
485        let evilLaughTimer = 7200;
486        let lastTapTime = 0;
487        let lastSpacePress = 0;
488        const doubleTapThreshold = 300;
489
490        setInterval(() => {
491            if (gameState === "playing") {
492                fallSpeedMultiplier *= 1.05;
493                console.log(`Fall speed increased to ${fallSpeedMultiplier.toFixed(2)}x`);
494            }
495        }, 33000);
496
497        setInterval(() => { 
498            if (gameState === "playing" && rockSpawnRate < 0.015) {
499                rockSpawnRate += 0.001;
500                console.log("Rock spawn rate increased to: ", rockSpawnRate);
501            }
502        }, 15000);
503
504        function tryPlayMusic() {
505            if (!musicPlayed) {
506                bgMusic.play()
507                    .then(() => {
508                        console.log("Background music started successfully");
509                        musicPlayed = true;
510                    })
511                    .catch(err => console.error("Failed to play background music: ", err));
512            }
513        }
514
515        function playEvilLaugh() {
516            evilLaughSound.currentTime = 0;
517            evilLaughSound.play().catch(err => console.error("Failed to play evil laugh: ", err));
518        }
519
520        function playHitSound() {
521            hitSound.currentTime = 0;
522            hitSound.play()
523                .then(() => console.log("Hit sound (oof.mp3) played successfully"))
524                .catch(err => console.error("Failed to play hit sound (oof.mp3): ", err));
525        }
526
527        canvas.addEventListener('touchstart', (e) => {
528            e.preventDefault();
529            const touch = e.touches[0];
530            const currentTime = Date.now();
531            const tapX = touch.clientX;
532
533            if (gameState === "title") {
534                startGame();
535            } else if (gameState === "playing") {
536                if (currentTime - lastTapTime <= doubleTapThreshold) {
537                    dragon.isFlyingInputActive = true;
538                }
539                lastTapTime = currentTime;
540                const halfScreen = canvas.width / 2;
541                if (tapX < halfScreen) {
542                    dragon.speedX = -2.5;
543                    dragon.targetScaleX = -1;
544                } else {
545                    dragon.speedX = 2.5;
546                    dragon.targetScaleX = 1;
547                }
548            }
549        });
550
551        canvas.addEventListener('touchend', (e) => {
552            e.preventDefault();
553            dragon.speedX = 0;
554            dragon.isFlyingInputActive = false;
555            touchHeld = false;
556        });
557
558        canvas.addEventListener('touchmove', (e) => {
559            e.preventDefault();
560            touchHeld = true;
561        });
562
563        window.addEventListener("keydown", (e) => {
564            if (gameState === "title" && (e.code === "Space" || e.code === "Enter")) {
565                startGame();
566            } else if (gameState === "playing") {
567                if (e.code === "Space") {
568                    const currentTime = Date.now();
569                    if (currentTime - lastSpacePress <= doubleTapThreshold) {
570                        dragon.isFlyingInputActive = true;
571                    }
572                    lastSpacePress = currentTime;
573                }
574                if (e.code === "ArrowLeft") {
575                    dragon.speedX = -2.5;
576                    dragon.targetScaleX = -1;
577                }
578                if (e.code === "ArrowRight") {
579                    dragon.speedX = 2.5;
580                    dragon.targetScaleX = 1;
581                }
582            }
583        });
584
585        window.addEventListener("keyup", (e) => {
586            if (e.code === "ArrowLeft" || e.code === "ArrowRight") {
587                dragon.speedX = 0;
588            }
589            if (e.code === "Space") {
590                dragon.isFlyingInputActive = false;
591            }
592        });
593
594        function drawDragon() {
595            if (dragon.flashing % 2 === 0) {
596                ctx.save();
597                ctx.translate(dragon.x + dragon.width / 2, dragon.y + dragon.height / 2);
598                ctx.scale(dragon.currentScaleX, 1);
599                ctx.drawImage(dragonImg, -dragon.width / 2, -dragon.height / 2, dragon.width, dragon.height);
600                if (dragon.invincible) {
601                    ctx.globalAlpha = 0.5;
602                    ctx.drawImage(dragonImg, -dragon.width / 2, -dragon.height / 2, dragon.width, dragon.height);
603                }
604                ctx.restore();
605            }
606        }
607
608        function drawRocks() {
609            rocks.forEach(rock => rock.draw());
610        }
611
612        function drawCoins() {
613            coins.forEach(coin => {
614                ctx.save();
615                ctx.translate(coin.x + coin.width / 2, coin.y + coin.height / 2);
616                ctx.rotate(coin.rotation);
617                ctx.drawImage(coinImg, -coin.width / 2, -coin.height / 2, coin.width, coin.height);
618                ctx.restore();
619            });
620        }
621
622        function drawPowerUps() {
623            powerUps.forEach(powerUp => powerUp.draw());
624        }
625
626        function drawParticles() {
627            particles.forEach(particle => particle.draw());
628        }
629
630        function drawLasers() {
631            lasers.forEach(laser => laser.draw());
632        }
633
634        function drawMagmaShockwaves() {
635            magmaShockwaves.forEach(shockwave => shockwave.draw());
636        }
637
638        // Temporarily disabled Web3 connect wallet function
639        /*
640        async function connectWallet() {
641            if (typeof window.ethereum !== 'undefined') {
642                try {
643                    await window.ethereum.request({
644                        method: 'wallet_addEthereumChain',
645                        params: [abstractTestnet]
646                    });
647                    provider = new ethers.providers.Web3Provider(window.ethereum);
648                    await provider.send("eth_requestAccounts", []);
649                    signer = provider.getSigner();
650                    walletAddress = await signer.getAddress();
651                    const currentChainId = await window.ethereum.request({ method: 'eth_chainId' });
652                    if (currentChainId !== abstractTestnet.chainId) {
653                        await window.ethereum.request({
654                            method: 'wallet_switchEthereumChain',
655                            params: [{ chainId: abstractTestnet.chainId }]
656                        });
657                    }
658                    contract = new ethers.Contract(contractAddress, abi, signer);
659                    console.log("Connected to wallet on Abstract Testnet:", walletAddress);
660                    fetchLeaderboard();
661                } catch (error) {
662                    console.error("Failed to connect wallet:", error);
663                    if (error.code === 4902) {
664                        console.log("Abstract Testnet not added to MetaMask.");
665                    }
666                }
667            } else {
668                console.log("MetaMask not detected. Please install MetaMask to play with blockchain features.");
669            }
670        }
671        */
672
673        // Temporarily disabled fetchLeaderboard function
674        /*
675        async function fetchLeaderboard() {
676            if (!walletAddress || !contract) {
677                console.log("Wallet not connected. Leaderboard unavailable.");
678                return;
679            }
680            try {
681                const data = await contract.getLeaderboard();
682                const leaderboardData = data.map(entry => ({
683                    address: entry.player,
684                    score: entry.score.toNumber()
685                }));
686                leaderboardData.sort((a, b) => b.score - a.score);
687                const top5 = leaderboardData.slice(0, 5);
688                const leaderboardDiv = document.getElementById('leaderboard');
689                leaderboardDiv.style.display = 'block';
690                leaderboardDiv.innerHTML = '<h3>Leaderboard</h3>' +
691                    top5.map((entry, index) =>
692                        `<div>${index + 1}. ${entry.address.slice(0, 6)}...${entry.address.slice(-4)}: ${entry.score}</div>`
693                    ).join('');
694            } catch (error) {
695                console.error("Error fetching leaderboard:", error);
696            }
697        }
698        */
699
700        // Temporarily disabled submitScore function
701        /*
702        async function submitScore() {
703            if (walletAddress && contract && gameState === "gameOver") {
704                try {
705                    const tx = await contract.submitScore(score);
706                    await tx.wait();
707                    console.log("Score submitted to Abstract Testnet:", score);
708                    fetchLeaderboard();
709                } catch (error) {
710                    console.error("Error submitting score:", error);
711                }
712            }
713        }
714        */
715
716        // Temporarily disabled toggleLeaderboard function
717        /*
718        function toggleLeaderboard() {
719            if (gameState !== "title") return;
720            const leaderboardDiv = document.getElementById("leaderboard");
721            if (leaderboardDiv.style.display === "block") {
722                leaderboardDiv.style.display = "none";
723            } else {
724                fetchLeaderboard();
725            }
726        }
727        */
728
729        function startGame() {
730            console.log("Starting game...");
731            clearTimeout(loadingTimeout);
732            gameState = "playing";
733            tapToStartButton.style.display = "none";
734            // document.getElementById("showLeaderboard").style.display = "none"; // Disabled
735            document.getElementById("leaderboard").style.display = "none";
736            tryPlayMusic();
737        }
738
739        function update() {
740            if (gameState !== "playing") return;
741
742            frameTime++;
743
744            if (dragon.isFlyingInputActive) {
745                dragon.flying = true;
746                dragon.velocityY -= 0.3;
747                if (dragon.velocityY < -3) dragon.velocityY = -3;
748                dragon.flyScoreTimer++; // Increment fly time counter
749                if (dragon.flyScoreTimer >= 30) { // Every 30 frames (0.5 seconds at 60 FPS)
750                    score += 1; // Add 1 point for every half second of fly time
751                    dragon.flyScoreTimer = 0; // Reset timer
752                }
753                if (dragon.torchBombFlyBonusTimer > 0) {
754                    // Existing bonus logic remains
755                    if (dragon.flyScoreTimer >= 60) {
756                        score += 100;
757                        dragon.flyScoreTimer = 0;
758                    }
759                }
760            } else {
761                dragon.flying = false;
762                dragon.velocityY += dragon.gravity;
763                if (dragon.velocityY > 5) dragon.velocityY = 5;
764                dragon.flyScoreTimer = 0; // Reset fly time when not flying
765            }
766
767            dragon.y += dragon.velocityY;
768            dragon.x += dragon.speedX;
769
770            const flipSpeed = 0.1;
771            dragon.currentScaleX += (dragon.targetScaleX - dragon.currentScaleX) * flipSpeed;
772
773            evilLaughTimer--;
774            if (evilLaughTimer === 180) {
775                magmaShockwaves.push(new MagmaShockwave());
776            }
777            if (evilLaughTimer <= 0) {
778                playEvilLaugh();
779                evilLaughTimer = 7200;
780            }
781
782            for (let i = 0; i < dragon.laserCooldowns.length; i++) {
783                dragon.laserCooldowns[i]--;
784                if (dragon.laserCooldowns[i] <= 0) {
785                    const laserXOffset = (i - Math.floor(dragon.laserCooldowns.length / 2)) * 25;
786                    lasers.push(new Laser(dragon.x + dragon.width / 2 - 3.5 + laserXOffset, dragon.y));
787                    dragon.laserCooldowns[i] = dragon.speedBoost ? 24 : 120;
788                }
789            }
790
791            if (dragon.speedBoost) {
792                dragon.speedBoostTimer--;
793                if (dragon.speedBoostTimer <= 0) dragon.speedBoost = false;
794            }
795            if (dragon.invincible) {
796                dragon.invincibleTimer--;
797                if (dragon.invincibleTimer <= 0) dragon.invincible = false;
798            }
799
800            if (dragon.torchBombTimer > 0) {
801                dragon.torchBombTimer--;
802                if (dragon.torchBombTimer === 299) {
803                    dragon.torchBombFlyBonusTimer = 600;
804                }
805                rocks.forEach((rock, rIndex) => {
806                    for (let i = 0; i < 5; i++) {
807                        particles.push(new Particle(
808                            rock.x + rock.width / 2,
809                            rock.y + rock.height / 2,
810                            Math.random() * 5 + 2,
811                            "orange",
812                            { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }
813                        ));
814                    }
815                });
816                if (dragon.torchBombTimer <= 0) {
817                    while (rocks.length > 0) {
818                        const rock = rocks.pop();
819                        coins.push({
820                            x: rock.x,
821                            y: rock.y,
822                            width: 30,
823                            height: 30,
824                            rotation: 0,
825                            rotationSpeed: Math.random() * 0.1 - 0.05
826                        });
827                        score += 5;
828                    }
829                }
830            }
831
832            if (dragon.torchBombFlyBonusTimer > 0) {
833                dragon.torchBombFlyBonusTimer--;
834            }
835
836            if (dragon.y + dragon.height > canvas.height) {
837                dragon.y = canvas.height - dragon.height;
838                dragon.velocityY = 0;
839            }
840            if (dragon.y < 0) {
841                dragon.y = 0;
842                dragon.velocityY = 0;
843            }
844            if (dragon.x < 0) dragon.x = 0;
845            if (dragon.x + dragon.width > canvas.width) dragon.x = canvas.width - dragon.width;
846
847            lasers.forEach((laser, lIndex) => {
848                laser.update();
849                if (lasers.length === 0) return;
850
851                if (laser.y < 0) {
852                    lasers.splice(lIndex, 1);
853                    return;
854                }
855
856                rocks.forEach((rock, rIndex) => {
857                    const collision = (
858                        laser.x < rock.x + rock.width &&
859                        laser.x + laser.width > rock.x &&
860                        laser.y < rock.y + rock.height &&
861                        laser.y + laser.height > rock.y
862                    );
863                    if (collision) {
864                        rock.health--;
865                        lasers.splice(lIndex, 1);
866                        if (rock.health <= 0) {
867                            for (let i = 0; i < 10; i++) {
868                                particles.push(new Particle(
869                                    rock.x + rock.width / 2,
870                                    rock.y + rock.height / 2,
871                                    Math.random() * 5 + 2,
872                                    "orange",
873                                    { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }
874                                ));
875                            }
876                            rocks.splice(rIndex, 1);
877                            score += 5;
878                        } else if (rock.width > 30) {
879                            const newSize = rock.width / 2;
880                            rocks.splice(rIndex, 1);
881                            rocks.push(new Rock(rock.x - newSize / 2, rock.y, newSize, 1));
882                            rocks.push(new Rock(rock.x + newSize / 2, rock.y, newSize, 1));
883                        }
884                    }
885                });
886            });
887
888            rocks.forEach((rock, rIndex) => {
889                rock.update();
890                if (rock.y > canvas.height) {
891                    rocks.splice(rIndex, 1);
892                } else if (dragon.torchBombTimer <= 0) {
893                    const collision = (
894                        dragon.x + dragon.width * 0.15 < rock.x + rock.width &&
895                        dragon.x + dragon.width * 0.85 > rock.x &&
896                        dragon.y + dragon.height * 0.15 < rock.y + rock.height &&
897                        dragon.y + dragon.height * 0.85 > rock.y
898                    );
899                    if (collision && !dragon.invincible) {
900                        console.log("Dragon hit by rock");
901                        lives--;
902                        rocks.splice(rIndex, 1);
903                        playHitSound();
904                        dragon.flashing = 90;
905                        dragon.invincible = true;
906                        dragon.invincibleTimer = 180;
907                        for (let i = 0; i < 10; i++) {
908                            particles.push(new Particle(
909                                dragon.x + dragon.width / 2,
910                                dragon.y + dragon.height / 2,
911                                Math.random() * 5 + 2,
912                                "red",
913                                { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }
914                            ));
915                        }
916                        if (lives < 1) {
917                            gameState = "gameOver";
918                            playEvilLaugh();
919                        }
920                    }
921                }
922            });
923
924            coins.forEach((coin, cIndex) => {
925                coin.y += 1.5 * fallSpeedMultiplier;
926                coin.rotation += coin.rotationSpeed;
927                if (coin.y > canvas.height) {
928                    coins.splice(cIndex, 1);
929                } else {
930                    const coinCollision = (
931                        dragon.x + dragon.width * 0.2 < coin.x + coin.width &&
932                        dragon.x + dragon.width * 0.8 > coin.x &&
933                        dragon.y + dragon.height * 0.2 < coin.y + coin.height &&
934                        dragon.y + dragon.height * 0.8 > coin.y
935                    );
936                    if (coinCollision) {
937                        score += 1;
938                        coinSound.play();
939                        coins.splice(cIndex, 1);
940                        for (let i = 0; i < 8; i++) {
941                            particles.push(new Particle(
942                                coin.x + coin.width / 2,
943                                coin.y + coin.height / 2,
944                                Math.random() * 3 + 1,
945                                "yellow",
946                                { x: (Math.random() - 0.5) * 4, y: (Math.random() - 0.5) * 4 }
947                            ));
948                        }
949                        if (score >= lastLifeScore + 1000) {
950                            lives++;
951                            lastLifeScore = Math.floor(score / 1000) * 1000;
952                        }
953                    }
954                }
955            });
956
957            powerUps.forEach((powerUp, pIndex) => {
958                powerUp.update();
959                if (powerUp.y > canvas.height) {
960                    powerUps.splice(pIndex, 1);
961                } else {
962                    const powerUpCollision = (
963                        dragon.x + dragon.width * 0.2 < powerUp.x + powerUp.width &&
964                        dragon.x + dragon.width * 0.8 > powerUp.x &&
965                        dragon.y + dragon.height * 0.2 < powerUp.y + powerUp.height &&
966                        dragon.y + dragon.height * 0.8 > powerUp.y
967                    );
968                    if (powerUpCollision) {
969                        powerUpSound.play();
970                        if (powerUp.type === "speed") {
971                            dragon.speedBoost = true;
972                            dragon.speedBoostTimer = 780;
973                        } else if (powerUp.type === "invincibility") {
974                            dragon.invincible = true;
975                            dragon.invincibleTimer = 420;
976                            dragon.invincibilityCount++;
977                            if (dragon.invincibilityCount % 3 === 0 && dragon.laserCooldowns.length < 5) {
978                                dragon.laserCooldowns.push(120 - dragon.laserCooldowns.length * 30);
979                            }
980                        } else if (powerUp.type === "torchBomb") {
981                            dragon.torchBombTimer = 300;
982                        }
983                        powerUps.splice(pIndex, 1);
984                        for (let i = 0; i < 10; i++) {
985                            particles.push(new Particle(
986                                powerUp.x + powerUp.width / 2,
987                                powerUp.y + powerUp.height / 2,
988                                Math.random() * 4 + 2,
989                                powerUp.type === "speed" ? "blue" : (powerUp.type === "invincibility" ? "green" : "gold"),
990                                { x: (Math.random() - 0.5) * 5, y: (Math.random() - 0.5) * 5 }
991                            ));
992                        }
993                    }
994                }
995            });
996
997            magmaShockwaves.forEach((shockwave, sIndex) => {
998                shockwave.update();
999                if (shockwave.y > canvas.height) {
1000                    magmaShockwaves.splice(sIndex, 1);
1001                } else {
1002                    const shockwaveCollision = (
1003                        dragon.x + dragon.width * 0.15 < shockwave.x + shockwave.width &&
1004                        dragon.x + dragon.width * 0.85 > shockwave.x &&
1005                        dragon.y + dragon.height * 0.15 < shockwave.y + shockwave.height &&
1006                        dragon.y + dragon.height * 0.85 > shockwave.y
1007                    );
1008                    if (shockwaveCollision && !dragon.invincible) {
1009                        lives--;
1010                        magmaShockwaves.splice(sIndex, 1);
1011                        playHitSound();
1012                        dragon.flashing = 90;
1013                        dragon.invincible = true;
1014                        dragon.invincibleTimer = 180;
1015                        for (let i = 0; i < 10; i++) {
1016                            particles.push(new Particle(
1017                                dragon.x + dragon.width / 2,
1018                                dragon.y + dragon.height / 2,
1019                                Math.random() * 5 + 2,
1020                                "red",
1021                                { x: (Math.random() - 0.5) * 6, y: (Math.random() - 0.5) * 6 }
1022                            ));
1023                        }
1024                        if (lives < 1) {
1025                            gameState = "gameOver";
1026                            playEvilLaugh();
1027                        }
1028                    }
1029                }
1030            });
1031
1032            particles.forEach((particle, pIndex) => {
1033                particle.update();
1034                if (particle.life <= 0) {
1035                    particles.splice(pIndex, 1);
1036                }
1037            });
1038
1039            if (Math.random() < rockSpawnRate) {
1040                const rockSize = Math.random() * (120 - 30) + 30;
1041                rocks.push(new Rock(Math.random() * canvas.width, -50, rockSize, score >= 5000 ? (rockSize > 60 ? 4 : 2) : (rockSize > 60 ? 2 : 1)));
1042            }
1043            if (Math.random() < 0.01) {
1044                coins.push({
1045                    x: Math.random() * canvas.width,
1046                    y: -50,
1047                    width: 30,
1048                    height: 30,
1049                    rotation: 0,
1050                    rotationSpeed: Math.random() * 0.1 - 0.05
1051                });
1052            }
1053            if (Math.random() < 0.0015) {
1054                const powerUpType = Math.random() < 0.33 ? "speed" : (Math.random() < 0.66 ? "invincibility" : "torchBomb");
1055                powerUps.push(new PowerUp(Math.random() * canvas.width, -50, powerUpType));
1056            }
1057
1058            if (dragon.flashing > 0) dragon.flashing--;
1059        }
1060
1061        function gameLoop() {
1062            ctx.clearRect(0, 0, canvas.width, canvas.height);
1063
1064            if (gameState === "loading") {
1065                drawLoadingMessage();
1066            } else if (gameState === "title") {
1067                console.log("Drawing title screen");
1068                const logoWidth = titleLogoImg.width;
1069                const logoHeight = titleLogoImg.height;
1070                ctx.drawImage(titleLogoImg, canvas.width / 2 - logoWidth / 2, canvas.height / 2 - logoHeight / 2 - 50, logoWidth, logoHeight);
1071            } else if (gameState === "playing") {
1072                ctx.fillStyle = "white";
1073                ctx.font = "20px Arial";
1074                ctx.fillText("Score: " + score, 30, 30);
1075                ctx.fillText("Lives: " + lives, 30, 60);
1076                if (dragon.speedBoost) {
1077                    ctx.fillStyle = "blue";
1078                    ctx.fillText(`Rapid Fire: ${Math.ceil(dragon.speedBoostTimer / 60)}s`, 30, 90);
1079                }
1080                if (dragon.invincible) {
1081                    ctx.fillStyle = "green";
1082                    ctx.fillText(`Invincibility: ${Math.ceil(dragon.invincibleTimer / 60)}s`, 30, 120);
1083                }
1084                if (dragon.flying && dragon.torchBombFlyBonusTimer > 0) {
1085                    ctx.fillStyle = "cyan";
1086                    ctx.fillText(`Flying Bonus: ${Math.ceil(dragon.torchBombFlyBonusTimer / 60)}s`, 30, 150);
1087                }
1088                drawDragon();
1089                drawRocks();
1090                drawCoins();
1091                drawPowerUps();
1092                drawParticles();
1093                drawLasers();
1094                drawMagmaShockwaves();
1095                update();
1096                document.getElementById("playAgain").style.display = "none";
1097            } else if (gameState === "gameOver") {
1098                ctx.fillStyle = "rgba(50, 0, 0, 0.5)";
1099                ctx.fillRect(0, 0, canvas.width, canvas.height);
1100                ctx.fillStyle = "white";
1101                ctx.textAlign = "center";
1102                ctx.font = "40px Arial";
1103                ctx.fillText("Game Over!", canvas.width / 2, canvas.height / 2 - 60);
1104                ctx.font = "20px Arial";
1105                ctx.fillText("Score: " + score, canvas.width / 2, canvas.height / 2 - 20);
1106                document.getElementById("playAgain").style.display = "block";
1107                // submitScore(); // Disabled
1108                ctx.textAlign = "left";
1109            }
1110
1111            requestAnimationFrame(gameLoop);
1112        }
1113
1114        function restartGame() {
1115            document.getElementById("playAgain").style.display = "none";
1116            gameState = "playing";
1117            score = 0;
1118            lastLifeScore = 0;
1119            lives = 3;
1120            dragon.x = canvas.width / 2 - 50;
1121            dragon.y = canvas.height / 2;
1122            dragon.velocityY = 0;
1123            dragon.speedX = 0;
1124            dragon.flashing = 0;
1125            dragon.flying = false;
1126            dragon.targetScaleX = 1;
1127            dragon.currentScaleX = 1;
1128            dragon.speedBoost = false;
1129            dragon.speedBoostTimer = 0;
1130            dragon.invincible = false;
1131            dragon.invincibleTimer = 0;
1132            dragon.isFlyingInputActive = false;
1133            dragon.laserCooldowns = [0];
1134            dragon.invincibilityCount = 0;
1135            dragon.torchBombTimer = 0;
1136            dragon.flyScoreTimer = 0;
1137            dragon.torchBombFlyBonusTimer = 0;
1138            rocks = [];
1139            coins = [];
1140            powerUps = [];
1141            particles = [];
1142            lasers = [];
1143            magmaShockwaves = [];
1144            rockSpawnRate = 0.0025;
1145            fallSpeedMultiplier = 1.0;
1146            musicPlayed = false;
1147            lastTapTime = 0;
1148            lastSpacePress = 0;
1149            touchHeld = false;
1150            evilLaughTimer = 7200;
1151            frameTime = 0;
1152            tryPlayMusic();
1153        }
1154
1155        // Temporarily disabled Web3 connect wallet call
1156        // connectWallet();
1157
1158        console.log("Starting game loop...");
1159        gameLoop();
1160    </script>
1160
1161</body>
1162</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.