top of page

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AD ATTACK!: David Cabezas</title>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');

        body {
            background-color: #000000;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: #FF4D4D; /* Light Red */
            font-family: 'Press Start 2P', cursive;
            height: 100vh;
            margin: 0;
            overflow: hidden;
        }
        canvas {
            border: 4px solid #FF4D4D;
            background-color: #000000;
            box-shadow: 0 0 40px rgba(255, 77, 77, 0.3);
            cursor: none;
            image-rendering: pixelated;
        }
        #ui { margin-bottom: 15px; text-align: center; }
        
        /* Interactive Link Button */
        #portfolio-btn {
            display: none;
            position: absolute;
            top: 82%;
            background: #FF4D4D;
            color: #000;
            padding: 15px 25px;
            text-decoration: none;
            font-size: 12px;
            border: 4px solid #fff;
            cursor: pointer;
            z-index: 10;
        }
        #portfolio-btn:hover { background: #fff; }
    </style>
</head>
<body>

    <div id="ui">
        <h1>AD ATTACK!</h1>
        <p>DODGE IDEA KILLERS FOR <span style="color:#fff">6 SECONDS</span></p>
    </div>

    <canvas id="gameCanvas" width="700" height="450"></canvas>
    
    <a id="portfolio-btn" href="https://www.davidcabezaswrites.com" target="_blank">VIEW PORTFOLIO</a>

<script>
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const btn = document.getElementById('portfolio-btn');

let gameState = 'START'; 
let startTime = 0;
let timeLeft = 6.00;
let mouseX = canvas.width / 2;
let mouseY = canvas.height / 2;

// --- GAME SETTINGS ---
const ERASER_SPEED = 4.5; 
const ERASER_COUNT = 10;

canvas.addEventListener('mousemove', (e) => {
    const rect = canvas.getBoundingClientRect();
    mouseX = e.clientX - rect.left;
    mouseY = e.clientY - rect.top;
});

canvas.addEventListener('mousedown', () => {
    if (gameState === 'START' || gameState === 'GAMEOVER') startGame();
});

function drawPencil(x, y) {
    ctx.save();
    ctx.translate(x, y);
    ctx.rotate(Math.PI / 4); 
    ctx.fillStyle = '#FF4D4D';
    ctx.fillRect(-15, -5, 30, 10);
    ctx.fillStyle = '#FFFFFF';
    ctx.beginPath();
    ctx.moveTo(15, -5); ctx.lineTo(25, 0); ctx.lineTo(15, 5);
    ctx.fill();
    ctx.fillStyle = '#000000';
    ctx.fillRect(22, -1, 3, 2);
    ctx.restore();
}

let erasers = [];
class Eraser {
    constructor() {
        this.size = 35;
        this.reset();
    }
    reset() {
        const side = Math.floor(Math.random() * 4);
        if(side === 0) { this.x = -this.size; this.y = Math.random() * canvas.height; }
        else if(side === 1) { this.x = canvas.width; this.y = Math.random() * canvas.height; }
        else if(side === 2) { this.x = Math.random() * canvas.width; this.y = -this.size; }
        else { this.x = Math.random() * canvas.width; this.y = canvas.height; }

        const angle = Math.atan2(mouseY - this.y, mouseX - this.x); 
        this.dx = Math.cos(angle) * ERASER_SPEED;
        this.dy = Math.sin(angle) * ERASER_SPEED;
    }
    update() {
        this.x += this.dx;
        this.y += this.dy;
        if (this.x < -100 || this.x > canvas.width + 100 || this.y < -100 || this.y > canvas.height + 100) {
            this.reset();
        }
    }
    draw() {
        ctx.fillStyle = 'rgba(255, 77, 77, 0.2)';
        ctx.fillRect(this.x, this.y, this.size, this.size);
        ctx.strokeStyle = '#FF4D4D';
        ctx.lineWidth = 2;
        ctx.strokeRect(this.x, this.y, this.size, this.size);
        ctx.fillStyle = '#FF4D4D';
        ctx.fillRect(this.x + 8, this.y + 10, 5, 5);
        ctx.fillRect(this.x + 22, this.y + 10, 5, 5);
    }
}

function startGame() {
    gameState = 'PLAYING';
    btn.style.display = 'none';
    startTime = Date.now();
    timeLeft = 6.00;
    erasers = [];
    for (let i = 0; i < ERASER_COUNT; i++) erasers.push(new Eraser());
    canvas.style.cursor = 'none';
    requestAnimationFrame(gameLoop);
}

function drawWinScreen() {
    ctx.fillStyle = '#000000';
    ctx.fillRect(0, 0, canvas.width, canvas.height);
    
    const pulse = Math.sin(Date.now() / 200) * 3;
    ctx.textAlign = 'center';
    
    // Updated Ethos Text
    ctx.fillStyle = '#FF4D4D';
    ctx.font = '14px "Press Start 2P"';
    ctx.fillText("AD LIFE IS A WILD GAME.", canvas.width / 2, 65 + pulse);
    
    ctx.fillStyle = '#FFFFFF';
    ctx.font = '18px "Press Start 2P"';
    ctx.fillText("LET'S WIN IT.", canvas.width / 2, 105);

    // David Character (Hat & Glasses)
    let cx = canvas.width / 2;
    let cy = 200; 
    let p = 6; 

    ctx.fillStyle = '#FF4D4D'; // Face
    ctx.fillRect(cx - 3*p, cy - 3*p, 6*p, 7*p);
    
    ctx.fillStyle = '#ffffff'; // Emote Sparks
    ctx.fillRect(cx - 8*p, cy - 6*p + pulse, p, p);
    ctx.fillRect(cx + 7*p, cy - 4*p - pulse, p, p);

    ctx.fillStyle = '#000000'; // Hat
    ctx.fillRect(cx - 4*p, cy - 5*p, 8*p, 2*p);
    ctx.fillRect(cx - 3*p, cy - 7*p, 6*p, 2*p);

    ctx.fillStyle = '#FFFFFF'; // Glasses
    ctx.fillRect(cx - 3*p, cy - 1*p, 2*p, 2*p);
    ctx.fillRect(cx + 1*p, cy - 1*p, 2*p, 2*p);
    ctx.fillRect(cx - 1*p, cy, 2*p, 1*p);

    ctx.fillStyle = '#000000'; // Smile
    ctx.fillRect(cx - 2*p, cy + 3*p, 4*p, 1*p);

    // Name & Contact
    ctx.fillStyle = '#FFFFFF';
    ctx.font = '16px "Press Start 2P"';
    ctx.fillText("DAVID CABEZAS", canvas.width / 2, 310);
    ctx.font = '10px "Press Start 2P"';
    ctx.fillStyle = '#FF4D4D';
    ctx.fillText("davidcabezas23@gmail.com", canvas.width / 2, 340);
    
    btn.style.display = 'block';
    canvas.style.cursor = 'default';
}

function gameLoop() {
    if (gameState !== 'PLAYING') {
        if (gameState === 'WIN') drawWinScreen();
        return;
    }
    
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    let elapsed = (Date.now() - startTime) / 1000;
    timeLeft = 6.00 - elapsed;

    if (timeLeft <= 0) {
        gameState = 'WIN';
        drawWinScreen();
        return;
    }

    erasers.forEach(e => {
        e.update();
        e.draw();
        if (mouseX > e.x && mouseX < e.x + e.size && mouseY > e.y && mouseY < e.y + e.size) {
            gameState = 'GAMEOVER';
        }
    });

    drawPencil(mouseX, mouseY);
    ctx.fillStyle = '#FFFFFF';
    ctx.font = '20px "Press Start 2P"';
    ctx.textAlign = 'left';
    ctx.fillText(timeLeft.toFixed(2), 20, 40);

    if (gameState === 'PLAYING') {
        requestAnimationFrame(gameLoop);
    } else if (gameState === 'GAMEOVER') {
        canvas.style.cursor = 'default';
        ctx.fillStyle = 'rgba(255, 0, 0, 0.4)';
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#fff';
        ctx.textAlign = 'center';
        ctx.font = '16px "Press Start 2P"';
        ctx.fillText("ERASED!", canvas.width/2, canvas.height/2);
        ctx.font = '10px "Press Start 2P"';
        ctx.fillText("CLICK TO TRY AGAIN", canvas.width/2, canvas.height/2 + 40);
    }
}

// Initial Screen
ctx.fillStyle = '#FF4D4D';
ctx.textAlign = 'center';
ctx.font = '12px "Press Start 2P"';
ctx.fillText("CLICK TO START", canvas.width/2, canvas.height/2);

// Keep the character animation running on win screen
setInterval(() => {
    if (gameState === 'WIN') drawWinScreen();
}, 100);

</script>
</body>
</html>

bottom of page