Games & More

Pasted Sep 25, 2026, 2:12 PM1 file4,242 lines

Scan to open on your phone

Point a camera at the QR code - it opens this exact paste, no app needed.

https://codepastes.com/zzgtasgzeded8n52
Fun
<script>
/* =========================================================
   SHARED ARCADE COINS
========================================================= */

let arcadeCoins = Number(localStorage.getItem("arcadeCoins") || 0);

function saveCoins(){
    localStorage.setItem("arcadeCoins",Math.floor(arcadeCoins));
}

function addCoins(amount){
    arcadeCoins += Math.max(0,Math.floor(amount));
    saveCoins();
    updateArcadeCoins();
}

function spendCoins(amount){
    amount=Math.floor(amount);

    if(arcadeCoins<amount)return false;

    arcadeCoins-=amount;
    saveCoins();
    updateArcadeCoins();
    return true;
}

function updateArcadeCoins(){
    let el=document.getElementById("arcadeCoinDisplay");

    if(!el){
        el=document.createElement("div");
        el.id="arcadeCoinDisplay";
        el.style.cssText=`
            position:fixed;
            top:15px;
            right:15px;
            z-index:999;
            padding:10px 16px;
            border-radius:14px;
            background:linear-gradient(135deg,#151d37,#0b1022);
            border:1px solid #ffd84d66;
            color:#fff;
            font-weight:bold;
            box-shadow:0 8px 30px #0008;
        `;
        document.body.appendChild(el);
    }

    el.textContent="๐Ÿช™ "+Math.floor(arcadeCoins);
}

function coinReward(amount){
    addCoins(amount);

    const el=document.createElement("div");

    el.textContent=`+${Math.floor(amount)} ๐Ÿช™`;

    el.style.cssText=`
        position:fixed;
        left:50%;
        top:80px;
        transform:translateX(-50%);
        z-index:1000;
        color:#ffd84d;
        font-size:22px;
        font-weight:bold;
        pointer-events:none;
        text-shadow:0 0 15px #ffd84d;
        animation:coinFloat 1s forwards;
    `;

    document.body.appendChild(el);

    setTimeout(()=>el.remove(),1000);
}

if(!document.getElementById("coinAnimationStyle")){
    const style=document.createElement("style");
    style.id="coinAnimationStyle";

    style.textContent=`
        @keyframes coinFloat{
            0%{
                opacity:0;
                transform:translate(-50%,20px) scale(.7);
            }
            20%{
                opacity:1;
            }
            100%{
                opacity:0;
                transform:translate(-50%,-60px) scale(1.2);
            }
        }
    `;

    document.head.appendChild(style);
}

updateArcadeCoins();


/* =========================================================
   CORE
========================================================= */

const modal=document.getElementById("modal");
const gameTitle=document.getElementById("gameTitle");
const gameContent=document.getElementById("gameContent");

const titles={
    clicker:"โšก Power Clicker",
    runner:"๐Ÿƒ Neon Runner",
    snake:"๐Ÿ Snake Evolution",
    target:"๐ŸŽฏ Target Rush",
    memory:"๐Ÿง  Memory Master",
    dodge:"โ˜„๏ธ Asteroid Dodge",
    brick:"๐Ÿงฑ Brick Breaker",
    racer:"๐ŸŽ๏ธ Highway Racer"
};

let currentGame=null;
let cleanupGame=null;

document.querySelectorAll(".game-card").forEach(card=>{
    card.addEventListener("click",()=>openGame(card.dataset.game));
});

function openGame(name){

    if(cleanupGame)cleanupGame();

    currentGame=name;

    gameTitle.textContent=titles[name];

    modal.classList.add("show");

    gameContent.innerHTML="";

    const games={
        clicker:powerClicker,
        runner:neonRunner,
        snake:snakeEvolution,
        target:targetRush,
        memory:memoryMaster,
        dodge:asteroidDodge,
        brick:brickBreaker,
        racer:highwayRacer
    };

    cleanupGame=games[name]();
}

function closeGame(){

    if(cleanupGame)cleanupGame();

    cleanupGame=null;
    currentGame=null;

    modal.classList.remove("show");

    updateArcadeCoins();
}

modal.addEventListener("click",e=>{
    if(e.target===modal)closeGame();
});

function money(n){
    return Math.floor(n).toLocaleString();
}

function save(key,value){
    localStorage.setItem(key,JSON.stringify(value));
}

function load(key,def){
    try{
        const v=JSON.parse(localStorage.getItem(key));
        return v??def;
    }catch{
        return def;
    }
}


/* =========================================================
   POWER CLICKER
========================================================= */

function powerClicker(){

    let energy=load("gm_energy",0);
    let clickLevel=load("gm_clickLevel",0);
    let generator=load("gm_generator",0);
    let crit=load("gm_crit",0);
    let comboLevel=load("gm_combo",0);
    let prestige=load("gm_prestige",0);

    let combo=0;
    let lastClick=0;
    let timer;

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">ENERGY<strong id="pcEnergy">0</strong></div>
            <div class="game-stat">PER CLICK<strong id="pcClick">1</strong></div>
            <div class="game-stat">PER SECOND<strong id="pcGen">0</strong></div>
            <div class="game-stat">COMBO<strong id="pcCombo">0x</strong></div>
            <div class="game-stat">PRESTIGE<strong id="pcPrestige">0</strong></div>
        </div>

        <div class="orb-wrap">
            <div class="energy-orb" id="energyOrb">โšก</div>
        </div>

        <div class="notice" id="pcNotice">
            Click the energy core!
        </div>

        <div class="controls">
            <button class="btn primary" id="prestigeBtn">
                ๐ŸŒŸ Prestige
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>๐Ÿงค Plasma Gloves</h3>
                <p>Increase energy earned per click.</p>
                <button class="btn" id="pcClickUp"></button>
            </div>

            <div class="upgrade">
                <h3>โš™๏ธ Auto Generator</h3>
                <p>Creates energy every second.</p>
                <button class="btn" id="pcGenUp"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ’ฅ Critical Reactor</h3>
                <p>Chance for a massive energy hit.</p>
                <button class="btn" id="pcCritUp"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ”ฅ Combo Core</h3>
                <p>Rapid clicking increases your multiplier.</p>
                <button class="btn" id="pcComboUp"></button>
            </div>

        </div>
    `;

    const $=id=>document.getElementById(id);

    function clickPower(){
        return Math.max(
            1,
            1+clickLevel*2+prestige*3
        );
    }

    function update(){

        $("pcEnergy").textContent=money(energy);

        $("pcClick").textContent=
            money(clickPower());

        $("pcGen").textContent=
            money(generator*(prestige+1));

        $("pcCombo").textContent=
            combo.toFixed(1)+"x";

        $("pcPrestige").textContent=prestige;

        $("pcClickUp").textContent=
            `Upgrade โ€ข ${50+clickLevel*100} โšก`;

        $("pcGenUp").textContent=
            `Upgrade โ€ข ${75+generator*175} โšก`;

        $("pcCritUp").textContent=
            `Upgrade โ€ข ${250+crit*400} โšก`;

        $("pcComboUp").textContent=
            `Upgrade โ€ข ${150+comboLevel*250} โšก`;

        save("gm_energy",energy);
        save("gm_clickLevel",clickLevel);
        save("gm_generator",generator);
        save("gm_crit",crit);
        save("gm_combo",comboLevel);
        save("gm_prestige",prestige);
    }

    function floating(text){

        const el=document.createElement("div");

        el.className="float-text";
        el.textContent=text;

        const r=$("energyOrb").getBoundingClientRect();

        el.style.left=
            (r.left+r.width/2)+"px";

        el.style.top=
            (r.top+30)+"px";

        document.body.appendChild(el);

        setTimeout(()=>el.remove(),900);
    }

    $("energyOrb").onclick=()=>{

        const now=Date.now();

        if(now-lastClick<900){
            combo=Math.min(
                10,
                combo+.25+comboLevel*.03
            );
        }else{
            combo=1;
        }

        lastClick=now;

        let amount=
            clickPower()*combo;

        if(Math.random()<crit*.03){

            amount*=5;

            floating(
                "CRITICAL! +"+
                Math.floor(amount)
            );

            /* clicking gives arcade coins too */
            addCoins(1);

        }else{

            floating(
                "+"+
                Math.floor(amount)
            );

        }

        energy+=amount;

        update();
    };

    $("pcClickUp").onclick=()=>{

        const cost=
            50+clickLevel*100;

        if(energy>=cost){

            energy-=cost;
            clickLevel++;

            update();
        }
    };

    $("pcGenUp").onclick=()=>{

        const cost=
            75+generator*175;

        if(energy>=cost){

            energy-=cost;
            generator++;

            update();
        }
    };

    $("pcCritUp").onclick=()=>{

        const cost=
            250+crit*400;

        if(
            energy>=cost &&
            crit<15
        ){

            energy-=cost;
            crit++;

            update();
        }
    };

    $("pcComboUp").onclick=()=>{

        const cost=
            150+comboLevel*250;

        if(energy>=cost){

            energy-=cost;
            comboLevel++;

            update();
        }
    };

    $("prestigeBtn").onclick=()=>{

        const needed=
            100000*(prestige+1);

        if(energy>=needed){

            energy=0;
            clickLevel=0;
            generator=0;
            crit=0;
            comboLevel=0;

            prestige++;

            /* Prestige reward */
            coinReward(50*prestige);

            $("pcNotice").textContent=
                "๐ŸŒŸ PRESTIGE COMPLETE! +"+
                (50*prestige)+
                " arcade coins!";

            update();

        }else{

            $("pcNotice").textContent=
                `Need ${money(needed)} energy.`;
        }
    };

    timer=setInterval(()=>{

        if(generator>0){

            energy+=
                generator*(prestige+1);

            /* passive gameplay reward */
            if(Math.random()<.15){
                addCoins(1);
            }

            update();
        }

        if(
            Date.now()-lastClick>1200 &&
            combo>0
        ){

            combo=Math.max(
                0,
                combo-.5
            );

            update();
        }

    },1000);

    update();

    return()=>{
        clearInterval(timer);
    };
}


/* =========================================================
   NEON RUNNER
========================================================= */

function neonRunner(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">SCORE<strong id="runScore">0</strong></div>
            <div class="game-stat">COINS<strong id="runCoins">0</strong></div>
            <div class="game-stat">LIVES<strong id="runLives">3</strong></div>
            <div class="game-stat">BEST<strong id="runBest">0</strong></div>
        </div>

        <canvas id="runnerCanvas"
                width="900"
                height="420"></canvas>

        <div class="touch-controls">
            <button id="runLeft">โ—€</button>
            <button id="runJump">โ–ฒ</button>
            <button id="runRight">โ–ถ</button>
        </div>

        <div class="controls">
            <button class="btn primary"
                    id="runStart">
                โ–ถ Start / Restart
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>๐Ÿ‘Ÿ Speed Shoes</h3>
                <p>Improve your running speed.</p>
                <button class="btn"
                        id="runSpeedUp">
                </button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿชฝ Double Jump</h3>
                <p>Unlock a second jump.</p>
                <button class="btn"
                        id="runJumpUp">
                </button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ›ก๏ธ Shield</h3>
                <p>Protect yourself from obstacles.</p>
                <button class="btn"
                        id="runShieldUp">
                </button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿงฒ Coin Magnet</h3>
                <p>Pull nearby coins toward you.</p>
                <button class="btn"
                        id="runMagnetUp">
                </button>
            </div>

        </div>

        <div class="notice"
             id="runNotice">
            Press Start!
        </div>
    `;

    const canvas=
        document.getElementById("runnerCanvas");

    const ctx=
        canvas.getContext("2d");

    let speedLevel=
        load("run_speed",0);

    let jumpLevel=
        load("run_jump",0);

    let shieldLevel=
        load("run_shield",0);

    let magnetLevel=
        load("run_magnet",0);

    let best=
        load("run_best",0);

    let score=0;
    let coins=0;
    let lives=3;
    let running=false;
    let frame=0;
    let raf;

    let player;
    let obstacles;
    let coinList;

    function reset(){

        score=0;
        coins=0;

        lives=
            3+shieldLevel;

        frame=0;

        player={
            x:120,
            y:310,
            w:35,
            h:55,
            vy:0,
            onGround:true,
            jumps:0,
            shield:shieldLevel
        };

        obstacles=[];
        coinList=[];

        running=true;

        loop();
    }

    function jump(){

        if(!running)return;

        if(player.onGround){

            player.vy=-14;
            player.onGround=false;
            player.jumps=1;

        }else if(
            jumpLevel>0 &&
            player.jumps<2
        ){

            player.vy=-13;
            player.jumps++;
        }
    }

    function update(){

        frame++;

        score+=.1;

        const worldSpeed=
            6+
            speedLevel*.5+
            Math.min(8,score/500);

        player.vy+=.7;
        player.y+=player.vy;

        if(player.y>=310){

            player.y=310;
            player.vy=0;
            player.onGround=true;
            player.jumps=0;
        }

        if(
            frame%
            Math.max(
                35,
                85-Math.floor(score/120)
            )===0
        ){

            const h=
                35+
                Math.random()*55;

            obstacles.push({
                x:920,
                y:365-h,
                w:25+
                   Math.random()*30,
                h
            });
        }

        if(frame%50===0){

            coinList.push({
                x:920,
                y:230+
                   Math.random()*100,
                r:9
            });
        }

        obstacles.forEach(
            o=>o.x-=worldSpeed
        );

        coinList.forEach(c=>{

            c.x-=worldSpeed;

            if(magnetLevel>0){

                const dx=
                    player.x-c.x;

                const dy=
                    player.y+25-c.y;

                const dist=
                    Math.hypot(dx,dy);

                if(
                    dist<
                    100+
                    magnetLevel*30
                ){

                    c.x+=dx*.08;
                    c.y+=dy*.08;
                }
            }
        });

        obstacles=obstacles.filter(o=>{

            if(o.x+o.w<0)
                return false;

            if(
                player.x<o.x+o.w &&
                player.x+player.w>o.x &&
                player.y<o.y+o.h &&
                player.y+player.h>o.y
            ){

                if(player.shield>0){

                    player.shield--;

                    coinReward(2);

                    return false;
                }

                lives--;

                if(lives<=0){

                    running=false;

                    best=Math.max(
                        best,
                        Math.floor(score)
                    );

                    save("run_best",best);

                    coinReward(
                        Math.floor(score/100)
                    );

                    document.getElementById(
                        "runNotice"
                    ).textContent=
                        `GAME OVER โ€ข ${Math.floor(score)} points`;
                }

                return false;
            }

            return true;
        });

        coinList=coinList.filter(c=>{

            if(
                Math.hypot(
                    player.x+18-c.x,
                    player.y+25-c.y
                )<25
            ){

                coins++;

                /* REAL ARCADE COINS */
                coinReward(1);

                return false;
            }

            return c.x>-20;
        });

        document.getElementById(
            "runScore"
        ).textContent=
            Math.floor(score);

        document.getElementById(
            "runCoins"
        ).textContent=
            coins;

        document.getElementById(
            "runLives"
        ).textContent=
            lives;

        document.getElementById(
            "runBest"
        ).textContent=
            best;
    }

    function draw(){

        ctx.fillStyle="#070b20";
        ctx.fillRect(
            0,0,900,420
        );

        /* stars */
        for(let i=0;i<60;i++){

            const x=
                (i*173-frame*.3)%900;

            const y=
                (i*71)%280;

            ctx.fillStyle="#ffffff88";

            ctx.fillRect(
                (x+900)%900,
                y,
                2,
                2
            );
        }

        /* buildings */
        ctx.fillStyle="#11182e";

        for(
            let x=0;
            x<900;
            x+=60
        ){

            const h=
                60+
                (x*7%110);

            ctx.fillRect(
                x,
                365-h,
                45,
                h
            );
        }

        ctx.fillStyle="#111";
        ctx.fillRect(
            0,365,900,55
        );

        ctx.strokeStyle="#35e7ff55";
        ctx.setLineDash(
            [25,20]
        );

        ctx.beginPath();
        ctx.moveTo(0,393);
        ctx.lineTo(900,393);
        ctx.stroke();

        ctx.setLineDash([]);

        /* player */
        ctx.fillStyle="#35e7ff";
        ctx.shadowBlur=20;
        ctx.shadowColor="#35e7ff";

        ctx.fillRect(
            player.x,
            player.y,
            player.w,
            player.h
        );

        ctx.shadowBlur=0;

        obstacles.forEach(o=>{

            ctx.fillStyle="#ff4fd8";

            ctx.fillRect(
                o.x,
                o.y,
                o.w,
                o.h
            );
        });

        coinList.forEach(c=>{

            ctx.fillStyle="#ffd84d";

            ctx.shadowBlur=15;
            ctx.shadowColor="#ffd84d";

            ctx.beginPath();

            ctx.arc(
                c.x,
                c.y,
                c.r,
                0,
                Math.PI*2
            );

            ctx.fill();

            ctx.shadowBlur=0;
        });
    }

    function loop(){

        if(!running){

            draw();
            return;
        }

        update();
        draw();

        raf=
            requestAnimationFrame(loop);
    }

    function moveLeft(){

        if(running)
            player.x=
                Math.max(
                    20,
                    player.x-30
                );
    }

    function moveRight(){

        if(running)
            player.x=
                Math.min(
                    845,
                    player.x+30
                );
    }

    const key=e=>{

        if(
            ["ArrowUp"," ","w","W"]
            .includes(e.key)
        ){

            e.preventDefault();

            jump();
        }

        if(
            ["ArrowLeft","a","A"]
            .includes(e.key)
        )
            moveLeft();

        if(
            ["ArrowRight","d","D"]
            .includes(e.key)
        )
            moveRight();
    };

    document.addEventListener(
        "keydown",
        key
    );

    document.getElementById(
        "runStart"
    ).onclick=reset;

    document.getElementById(
        "runJump"
    ).onclick=jump;

    document.getElementById(
        "runLeft"
    ).onclick=moveLeft;

    document.getElementById(
        "runRight"
    ).onclick=moveRight;

    /* Upgrade buttons now use REAL arcade coins */

    const upgrades=[
        ["runSpeedUp","run_speed","Speed Shoes",10],
        ["runJumpUp","run_jump","Double Jump",25],
        ["runShieldUp","run_shield","Shield",30],
        ["runMagnetUp","run_magnet","Coin Magnet",35]
    ];

    upgrades.forEach(
        ([id,key,name,base])=>{

            const button=
                document.getElementById(id);

            function refresh(){

                const level=
                    load(key,0);

                const cost=
                    base+
                    level*base;

                button.textContent=
                    `${name} โ€ข ${cost} ๐Ÿช™`;
            }

            button.onclick=()=>{

                const level=
                    load(key,0);

                const cost=
                    base+
                    level*base;

                if(spendCoins(cost)){

                    save(
                        key,
                        level+1
                    );

                    if(key==="run_speed")
                        speedLevel++;

                    if(key==="run_jump")
                        jumpLevel++;

                    if(key==="run_shield")
                        shieldLevel++;

                    if(key==="run_magnet")
                        magnetLevel++;

                    refresh();
                }
            };

            refresh();
        }
    );

    reset();

    return()=>{
        cancelAnimationFrame(raf);
        document.removeEventListener(
            "keydown",
            key
        );
    };
}


/* =========================================================
   SNAKE
========================================================= */

function snakeEvolution(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">SCORE<strong id="snScore">0</strong></div>
            <div class="game-stat">LEVEL<strong id="snLevel">1</strong></div>
            <div class="game-stat">LENGTH<strong id="snLength">3</strong></div>
            <div class="game-stat">BEST<strong id="snBest">0</strong></div>
        </div>

        <canvas id="snakeCanvas"
                width="500"
                height="500"></canvas>

        <div class="touch-controls">
            <button id="snLeft">โ—€</button>
            <button id="snUp">โ–ฒ</button>
            <button id="snDown">โ–ผ</button>
            <button id="snRight">โ–ถ</button>
        </div>

        <div class="controls">
            <button class="btn primary"
                    id="snStart">
                โ–ถ Start / Restart
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>โšก Speed Coil</h3>
                <p>Increase snake speed.</p>
                <button class="btn"
                        id="snSpeed"></button>
            </div>

            <div class="upgrade">
                <h3>๐ŸŽ Golden Food</h3>
                <p>Increase golden food chance.</p>
                <button class="btn"
                        id="snGold"></button>
            </div>

            <div class="upgrade">
                <h3>โค๏ธ Extra Life</h3>
                <p>Survive collisions.</p>
                <button class="btn"
                        id="snLife"></button>
            </div>

        </div>

        <div class="notice"
             id="snNotice">
            Eat the food!
        </div>
    `;

    const canvas=
        document.getElementById(
            "snakeCanvas"
        );

    const ctx=
        canvas.getContext("2d");

    const grid=20;
    const cell=25;

    let speedLv=
        load("snake_speed",0);

    let goldLv=
        load("snake_gold",0);

    let lifeLv=
        load("snake_life",0);

    let best=
        load("snake_best",0);

    let snake;
    let food;
    let dir;
    let nextDir;
    let score;
    let level;
    let lives;
    let running;
    let timer;

    function randomFood(){

        let p;

        do{

            p={
                x:Math.floor(
                    Math.random()*grid
                ),

                y:Math.floor(
                    Math.random()*grid
                ),

                gold:
                    Math.random()<
                    goldLv*.08
            };

        }while(
            snake.some(
                s=>
                    s.x===p.x &&
                    s.y===p.y
            )
        );

        return p;
    }

    function start(){

        snake=[
            {x:10,y:10},
            {x:9,y:10},
            {x:8,y:10}
        ];

        dir={x:1,y:0};
        nextDir={x:1,y:0};

        score=0;
        level=1;
        lives=lifeLv;

        food=randomFood();

        running=true;

        schedule();
        draw();
    }

    function schedule(){

        clearTimeout(timer);

        if(running){

            timer=setTimeout(
                tick,
                Math.max(
                    65,
                    145-
                    speedLv*8-
                    level*4
                )
            );
        }
    }

    function tick(){

        dir=nextDir;

        const head={
            x:snake[0].x+dir.x,
            y:snake[0].y+dir.y
        };

        const collision=
            head.x<0 ||
            head.x>=grid ||
            head.y<0 ||
            head.y>=grid ||
            snake.some(
                s=>
                    s.x===head.x &&
                    s.y===head.y
            );

        if(collision){

            if(lives>0){

                lives--;

                snake=[
                    {x:10,y:10},
                    {x:9,y:10},
                    {x:8,y:10}
                ];

                dir={x:1,y:0};
                nextDir={x:1,y:0};

            }else{

                running=false;

                best=Math.max(
                    best,
                    score
                );

                save(
                    "snake_best",
                    best
                );

                coinReward(
                    Math.floor(score/50)
                );

                document.getElementById(
                    "snNotice"
                ).textContent=
                    `GAME OVER โ€ข ${score}`;

                draw();

                return;
            }

        }else{

            snake.unshift(head);

            if(
                head.x===food.x &&
                head.y===food.y
            ){

                const points=
                    food.gold?50:10;

                score+=points;

                /* FOOD NOW EARNS ARCADE COINS */

                if(food.gold){

                    coinReward(5);

                }else{

                    coinReward(1);
                }

                food=randomFood();

                level=
                    1+
                    Math.floor(
                        score/100
                    );

            }else{

                snake.pop();
            }
        }

        updateStats();
        draw();
        schedule();
    }

    function updateStats(){

        document.getElementById(
            "snScore"
        ).textContent=score;

        document.getElementById(
            "snLevel"
        ).textContent=level;

        document.getElementById(
            "snLength"
        ).textContent=snake.length;

        document.getElementById(
            "snBest"
        ).textContent=best;
    }

    function draw(){

        ctx.fillStyle="#050914";

        ctx.fillRect(
            0,0,500,500
        );

        for(
            let x=0;
            x<grid;
            x++
        ){

            for(
                let y=0;
                y<grid;
                y++
            ){

                ctx.fillStyle=
                    (x+y)%2
                    ?" #0a1221".trim()
                    :"#0c1628";

                ctx.fillRect(
                    x*cell,
                    y*cell,
                    cell,
                    cell
                );
            }
        }

        ctx.fillStyle=
            food.gold
            ?" #ffd84d".trim()
            :"#ff4f9a";

        ctx.beginPath();

        ctx.arc(
            food.x*cell+cell/2,
            food.y*cell+cell/2,
            food.gold?9:7,
            0,
            Math.PI*2
        );

        ctx.fill();

        snake.forEach(
            (s,i)=>{

                ctx.fillStyle=
                    i===0
                    ?" #54f59a".trim()
                    :"#25b875";

                ctx.fillRect(
                    s.x*cell+2,
                    s.y*cell+2,
                    cell-4,
                    cell-4
                );
            }
        );

        updateStats();
    }

    function setDir(x,y){

        if(
            dir.x===-x &&
            dir.y===-y
        )
            return;

        nextDir={x,y};
    }

    const key=e=>{

        const map={

            ArrowUp:[0,-1],
            w:[0,-1],
            W:[0,-1],

            ArrowDown:[0,1],
            s:[0,1],
            S:[0,1],

            ArrowLeft:[-1,0],
            a:[-1,0],
            A:[-1,0],

            ArrowRight:[1,0],
            d:[1,0],
            D:[1,0]
        };

        if(map[e.key]){

            e.preventDefault();

            setDir(
                ...map[e.key]
            );
        }
    };

    document.addEventListener(
        "keydown",
        key
    );

    document.getElementById(
        "snStart"
    ).onclick=start;

    document.getElementById(
        "snUp"
    ).onclick=()=>setDir(0,-1);

    document.getElementById(
        "snDown"
    ).onclick=()=>setDir(0,1);

    document.getElementById(
        "snLeft"
    ).onclick=()=>setDir(-1,0);

    document.getElementById(
        "snRight"
    ).onclick=()=>setDir(1,0);


    /* REAL COIN UPGRADES */

    function upgrade(
        id,
        key,
        base
    ){

        const button=
            document.getElementById(id);

        function refresh(){

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            button.textContent=
                `Upgrade โ€ข ${cost} ๐Ÿช™`;
        }

        button.onclick=()=>{

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            if(spendCoins(cost)){

                save(
                    key,
                    level+1
                );

                refresh();
            }
        };

        refresh();
    }

    upgrade(
        "snSpeed",
        "snake_speed",
        10
    );

    upgrade(
        "snGold",
        "snake_gold",
        15
    );

    upgrade(
        "snLife",
        "snake_life",
        25
    );

    start();

    return()=>{

        clearTimeout(timer);

        document.removeEventListener(
            "keydown",
            key
        );
    };
}


/* =========================================================
   TARGET RUSH
========================================================= */

function targetRush(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">SCORE<strong id="tarScore">0</strong></div>
            <div class="game-stat">TIME<strong id="tarTime">30</strong></div>
            <div class="game-stat">COMBO<strong id="tarCombo">0x</strong></div>
            <div class="game-stat">BEST<strong id="tarBest">0</strong></div>
        </div>

        <div class="target-area"
             id="targetArea"></div>

        <div class="controls">
            <button class="btn primary"
                    id="tarStart">
                โ–ถ Start Round
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>โฑ๏ธ Time Warp</h3>
                <p>Add more time.</p>
                <button class="btn"
                        id="tarTimeUp"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ”ฅ Combo Master</h3>
                <p>Increase combo score.</p>
                <button class="btn"
                        id="tarComboUp"></button>
            </div>

            <div class="upgrade">
                <h3>๐ŸŒ Slow Motion</h3>
                <p>Targets remain longer.</p>
                <button class="btn"
                        id="tarSlowUp"></button>
            </div>

        </div>

        <div class="notice"
             id="tarNotice">
            Hit as many targets as possible!
        </div>
    `;

    const area=
        document.getElementById(
            "targetArea"
        );

    let timeLv=
        load("target_time",0);

    let comboLv=
        load("target_combo",0);

    let slowLv=
        load("target_slow",0);

    let best=
        load("target_best",0);

    let score=0;
    let combo=0;
    let time=30;
    let running=false;

    let interval;
    let targetTimer;

    function spawn(){

        area.innerHTML="";

        const t=
            document.createElement(
                "div"
            );

        const roll=
            Math.random();

        t.className=
            "target "+
            (
                roll<.12
                ?"bomb":
                roll<.28
                ?"gold":
                "normal"
            );

        t.textContent=
            t.classList.contains("bomb")
            ?"ร—":
            t.classList.contains("gold")
            ?"โ˜…":
            "+";

        t.style.left=
            (Math.random()*82+4)+"%";

        t.style.top=
            (Math.random()*78+5)+"%";

        t.onclick=()=>{

            if(!running)return;

            if(
                t.classList.contains(
                    "bomb"
                )
            ){

                score=
                    Math.max(
                        0,
                        score-25
                    );

                combo=0;

            }else{

                combo++;

                const points=
                    t.classList.contains(
                        "gold"
                    )
                    ?30
                    :10;

                score+=Math.floor(
                    points*
                    (1+combo*.08)*
                    (1+comboLv*.15)
                );

                /* TARGETS GIVE COINS */

                if(
                    t.classList.contains(
                        "gold"
                    )
                ){

                    coinReward(3);

                }else{

                    coinReward(1);
                }
            }

            update();

            spawn();
        };

        area.appendChild(t);

        clearTimeout(
            targetTimer
        );

        targetTimer=setTimeout(
            ()=>{
                if(running)
                    spawn();
            },
            Math.max(
                400,
                1200-slowLv*100
            )
        );
    }

    function update(){

        document.getElementById(
            "tarScore"
        ).textContent=score;

        document.getElementById(
            "tarTime"
        ).textContent=
            Math.max(
                0,
                time
            ).toFixed(1);

        document.getElementById(
            "tarCombo"
        ).textContent=
            combo+"x";

        document.getElementById(
            "tarBest"
        ).textContent=
            best;
    }

    function start(){

        clearInterval(interval);

        score=0;
        combo=0;

        time=
            30+
            timeLv*5;

        running=true;

        spawn();
        update();

        interval=setInterval(()=>{

            time-=.1;

            if(time<=0){

                time=0;
                running=false;

                clearInterval(
                    interval
                );

                area.innerHTML="";

                best=Math.max(
                    best,
                    score
                );

                save(
                    "target_best",
                    best
                );

                /* END OF ROUND REWARD */

                const reward=
                    Math.floor(
                        score/25
                    );

                coinReward(
                    reward
                );

                document.getElementById(
                    "tarNotice"
                ).textContent=
                    `ROUND COMPLETE! +${reward} ๐Ÿช™`;
            }

            update();

        },100);
    }

    document.getElementById(
        "tarStart"
    ).onclick=start;


    function upgrade(
        id,
        key,
        base
    ){

        const button=
            document.getElementById(id);

        function refresh(){

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            button.textContent=
                `Upgrade โ€ข ${cost} ๐Ÿช™`;
        }

        button.onclick=()=>{

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            if(spendCoins(cost)){

                save(
                    key,
                    level+1
                );

                refresh();
            }
        };

        refresh();
    }

    upgrade(
        "tarTimeUp",
        "target_time",
        20
    );

    upgrade(
        "tarComboUp",
        "target_combo",
        25
    );

    upgrade(
        "tarSlowUp",
        "target_slow",
        25
    );

    update();

    return()=>{

        clearInterval(interval);

        clearTimeout(targetTimer);
    };
}


/* =========================================================
   MEMORY MASTER
========================================================= */

function memoryMaster(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">LEVEL<strong id="memLevel">1</strong></div>
            <div class="game-stat">MOVES<strong id="memMoves">0</strong></div>
            <div class="game-stat">TIME<strong id="memTime">0</strong></div>
            <div class="game-stat">BEST<strong id="memBest">0</strong></div>
        </div>

        <div class="memory-grid"
             id="memoryGrid"></div>

        <div class="controls">
            <button class="btn primary"
                    id="memNew">
                ๐Ÿ”„ New Board
            </button>

            <button class="btn"
                    id="memHard">
                ๐Ÿ”ฅ Hard Mode
            </button>
        </div>

        <div class="notice"
             id="memNotice">
            Find all matching pairs!
        </div>
    `;

    const icons=[
        "๐Ÿš€","๐Ÿ‘พ","โญ","๐Ÿ’Ž",
        "๐Ÿ”ฅ","โšก","๐ŸŽฎ","๐Ÿ›ธ",
        "๐ŸŒ™","๐Ÿฒ","๐Ÿ•","๐Ÿ†"
    ];

    let level=
        load("memory_level",1);

    let best=
        load("memory_best",0);

    let moves=0;
    let seconds=0;

    let first=null;
    let second=null;
    let lock=false;
    let timer;
    let hard=false;

    function newBoard(){

        clearInterval(timer);

        moves=0;
        seconds=0;

        first=null;
        second=null;
        lock=false;

        const pairs=
            hard
            ?8
            :Math.min(
                6,
                3+
                Math.floor(level/2)
            );

        const chosen=
            [...icons]
            .sort(
                ()=>Math.random()-.5
            )
            .slice(0,pairs);

        const cards=
            [...chosen,...chosen]
            .sort(
                ()=>Math.random()-.5
            );

        const grid=
            document.getElementById(
                "memoryGrid"
            );

        grid.innerHTML="";

        cards.forEach(icon=>{

            const card=
                document.createElement(
                    "button"
                );

            card.className=
                "memory-card";

            card.dataset.icon=icon;

            card.textContent=icon;

            card.onclick=
                ()=>flip(card);

            grid.appendChild(card);
        });

        timer=setInterval(()=>{

            seconds++;

            document.getElementById(
                "memTime"
            ).textContent=
                seconds;

        },1000);

        update();
    }

    function flip(card){

        if(
            lock||
            card.classList.contains(
                "flipped"
            )||
            card.classList.contains(
                "matched"
            )
        )
            return;

        card.classList.add(
            "flipped"
        );

        if(!first){

            first=card;
            return;
        }

        second=card;

        moves++;

        lock=true;

        if(
            first.dataset.icon===
            second.dataset.icon
        ){

            first.classList.add(
                "matched"
            );

            second.classList.add(
                "matched"
            );

            first=null;
            second=null;
            lock=false;

            coinReward(2);

            if(
                document.querySelectorAll(
                    ".memory-card.matched"
                ).length===
                document.querySelectorAll(
                    ".memory-card"
                ).length
            ){

                clearInterval(timer);

                const points=
                    Math.max(
                        10,
                        500-
                        seconds*5-
                        moves*10
                    );

                best=Math.max(
                    best,
                    points
                );

                save(
                    "memory_best",
                    best
                );

                level++;

                save(
                    "memory_level",
                    level
                );

                coinReward(
                    Math.floor(
                        points/25
                    )
                );

                document.getElementById(
                    "memNotice"
                ).textContent=
                    `๐Ÿ† LEVEL COMPLETE!`;
            }

        }else{

            setTimeout(()=>{

                first.classList.remove(
                    "flipped"
                );

                second.classList.remove(
                    "flipped"
                );

                first=null;
                second=null;

                lock=false;

            },650);
        }

        update();
    }

    function update(){

        document.getElementById(
            "memLevel"
        ).textContent=level;

        document.getElementById(
            "memMoves"
        ).textContent=moves;

        document.getElementById(
            "memTime"
        ).textContent=seconds;

        document.getElementById(
            "memBest"
        ).textContent=best;
    }

    document.getElementById(
        "memNew"
    ).onclick=newBoard;

    document.getElementById(
        "memHard"
    ).onclick=()=>{

        hard=!hard;

        document.getElementById(
            "memHard"
        ).textContent=
            hard
            ?"โ„๏ธ Normal Mode"
            :"๐Ÿ”ฅ Hard Mode";

        newBoard();
    };

    newBoard();

    return()=>{
        clearInterval(timer);
    };
}


/* =========================================================
   ASTEROID DODGE
========================================================= */

function asteroidDodge(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">SCORE<strong id="dodScore">0</strong></div>
            <div class="game-stat">CREDITS<strong id="dodCredits">0</strong></div>
            <div class="game-stat">SHIELD<strong id="dodShield">0</strong></div>
            <div class="game-stat">BEST<strong id="dodBest">0</strong></div>
        </div>

        <canvas id="dodgeCanvas"
                width="900"
                height="450"></canvas>

        <div class="touch-controls">
            <button id="dodLeft">โ—€</button>
            <button id="dodFire">๐Ÿ”ฅ</button>
            <button id="dodRight">โ–ถ</button>
        </div>

        <div class="controls">
            <button class="btn primary"
                    id="dodStart">
                โ–ถ Launch Ship
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>๐Ÿš€ Engine</h3>
                <p>Move faster.</p>
                <button class="btn"
                        id="dodEngine"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ›ก๏ธ Shield</h3>
                <p>Absorb more hits.</p>
                <button class="btn"
                        id="dodShield"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ”ฅ Plasma Cannon</h3>
                <p>Destroy asteroids.</p>
                <button class="btn"
                        id="dodGun"></button>
            </div>

        </div>

        <div class="notice"
             id="dodNotice">
            Avoid the asteroid field!
        </div>
    `;

    const canvas=
        document.getElementById(
            "dodgeCanvas"
        );

    const ctx=
        canvas.getContext("2d");

    let engine=
        load("dod_engine",0);

    let shield=
        load("dod_shield",0);

    let gun=
        load("dod_gun",0);

    let best=
        load("dod_best",0);

    let score=0;
    let credits=0;
    let running=false;
    let frame=0;
    let raf;

    let player;
    let asteroids;
    let bullets;

    function start(){

        score=0;
        credits=0;
        frame=0;

        player={
            x:450,
            y:370,
            hp:shield+1
        };

        asteroids=[];
        bullets=[];

        running=true;

        loop();
    }

    function fire(){

        if(!running)return;

        bullets.push({
            x:player.x,
            y:player.y-20,
            speed:10+gun
        });
    }

    function update(){

        frame++;

        score++;

        const spawnRate=
            Math.max(
                12,
                38-
                Math.floor(score/300)
            );

        if(
            frame%spawnRate===0
        ){

            asteroids.push({
                x:Math.random()*870+15,
                y:-40,
                r:12+
                   Math.random()*22,
                speed:
                    3+
                    Math.random()*3+
                    score/1000
            });
        }

        asteroids.forEach(
            a=>a.y+=a.speed
        );

        bullets.forEach(
            b=>b.y-=b.speed
        );

        asteroids=
            asteroids.filter(
                a=>a.y<500
            );

        bullets=
            bullets.filter(
                b=>b.y>-20
            );

        for(
            let i=asteroids.length-1;
            i>=0;
            i--
        ){

            const a=
                asteroids[i];

            if(
                Math.hypot(
                    a.x-player.x,
                    a.y-player.y
                )<
                a.r+18
            ){

                player.hp--;

                asteroids.splice(
                    i,
                    1
                );

                if(player.hp<=0){

                    running=false;

                    best=Math.max(
                        best,
                        score
                    );

                    save(
                        "dod_best",
                        best
                    );

                    coinReward(
                        Math.floor(
                            score/100
                        )
                    );

                    document.getElementById(
                        "dodNotice"
                    ).textContent=
                        `SHIP DESTROYED โ€ข ${score}`;

                    draw();

                    return;
                }
            }
        }

        for(
            let i=asteroids.length-1;
            i>=0;
            i--
        ){

            for(
                let j=bullets.length-1;
                j>=0;
                j--
            ){

                if(
                    Math.hypot(
                        asteroids[i].x-
                        bullets[j].x,

                        asteroids[i].y-
                        bullets[j].y
                    )<
                    asteroids[i].r+5
                ){

                    credits+=5;

                    /* Destroying asteroids earns coins */

                    coinReward(1);

                    asteroids.splice(
                        i,
                        1
                    );

                    bullets.splice(
                        j,
                        1
                    );

                    break;
                }
            }
        }

        document.getElementById(
            "dodScore"
        ).textContent=score;

        document.getElementById(
            "dodCredits"
        ).textContent=credits;

        document.getElementById(
            "dodShield"
        ).textContent=player.hp;

        document.getElementById(
            "dodBest"
        ).textContent=best;
    }

    function draw(){

        ctx.fillStyle="#03050c";

        ctx.fillRect(
            0,0,900,450
        );

        /* stars */

        for(let i=0;i<80;i++){

            const x=
                (i*137)%900;

            const y=
                (i*73+frame*.4)%450;

            ctx.fillStyle="#ffffff99";

            ctx.fillRect(
                x,
                y,
                2,
                2
            );
        }

        /* ship */

        ctx.fillStyle="#35e7ff";

        ctx.shadowBlur=25;
        ctx.shadowColor="#35e7ff";

        ctx.beginPath();

        ctx.moveTo(
            player.x,
            player.y-25
        );

        ctx.lineTo(
            player.x-18,
            player.y+20
        );

        ctx.lineTo(
            player.x,
            player.y+12
        );

        ctx.lineTo(
            player.x+18,
            player.y+20
        );

        ctx.closePath();

        ctx.fill();

        ctx.shadowBlur=0;

        /* asteroids */

        asteroids.forEach(a=>{

            ctx.fillStyle="#7b8399";

            ctx.beginPath();

            for(
                let i=0;
                i<8;
                i++
            ){

                const angle=
                    i*Math.PI/4;

                const rr=
                    a.r*
                    (.75+
                    Math.sin(i*4)*.1);

                const x=
                    a.x+
                    Math.cos(angle)*rr;

                const y=
                    a.y+
                    Math.sin(angle)*rr;

                if(i===0)
                    ctx.moveTo(x,y);
                else
                    ctx.lineTo(x,y);
            }

            ctx.closePath();
            ctx.fill();
        });

        ctx.fillStyle="#ffd84d";

        bullets.forEach(
            b=>{
                ctx.fillRect(
                    b.x-2,
                    b.y-10,
                    4,
                    12
                );
            }
        );
    }

    function loop(){

        if(!running){

            draw();
            return;
        }

        update();
        draw();

        raf=
            requestAnimationFrame(
                loop
            );
    }

    function left(){

        if(running)
            player.x=
                Math.max(
                    20,
                    player.x-(8+engine)
                );
    }

    function right(){

        if(running)
            player.x=
                Math.min(
                    880,
                    player.x+(8+engine)
                );
    }

    const key=e=>{

        if(
            ["ArrowLeft","a","A"]
            .includes(e.key)
        )
            left();

        if(
            ["ArrowRight","d","D"]
            .includes(e.key)
        )
            right();

        if(e.key===" ")
            fire();
    };

    document.addEventListener(
        "keydown",
        key
    );

    document.getElementById(
        "dodStart"
    ).onclick=start;

    document.getElementById(
        "dodLeft"
    ).onclick=left;

    document.getElementById(
        "dodRight"
    ).onclick=right;

    document.getElementById(
        "dodFire"
    ).onclick=fire;


    function upgrade(
        id,
        key,
        base
    ){

        const button=
            document.getElementById(id);

        function refresh(){

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            button.textContent=
                `Upgrade โ€ข ${cost} ๐Ÿช™`;
        }

        button.onclick=()=>{

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            if(spendCoins(cost)){

                save(
                    key,
                    level+1
                );

                refresh();
            }
        };

        refresh();
    }

    upgrade(
        "dodEngine",
        "dod_engine",
        20
    );

    upgrade(
        "dodShield",
        "dod_shield",
        30
    );

    upgrade(
        "dodGun",
        "dod_gun",
        40
    );

    start();

    return()=>{

        cancelAnimationFrame(raf);

        document.removeEventListener(
            "keydown",
            key
        );
    };
}


/* =========================================================
   BRICK BREAKER
========================================================= */

function brickBreaker(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">SCORE<strong id="bbScore">0</strong></div>
            <div class="game-stat">LEVEL<strong id="bbLevel">1</strong></div>
            <div class="game-stat">LIVES<strong id="bbLives">3</strong></div>
            <div class="game-stat">BEST<strong id="bbBest">0</strong></div>
        </div>

        <canvas id="brickCanvas"
                width="850"
                height="520"></canvas>

        <div class="touch-controls">
            <button id="bbLeft">โ—€</button>
            <button id="bbLaunch">โ—</button>
            <button id="bbRight">โ–ถ</button>
        </div>

        <div class="controls">
            <button class="btn primary"
                    id="bbStart">
                โ–ถ Start / Restart
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>๐Ÿ“ Wide Paddle</h3>
                <p>Increase paddle size.</p>
                <button class="btn"
                        id="bbPaddle"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ”ฅ Power Ball</h3>
                <p>Break strong bricks faster.</p>
                <button class="btn"
                        id="bbPower"></button>
            </div>

            <div class="upgrade">
                <h3>โœจ Multi Ball</h3>
                <p>Chance to create another ball.</p>
                <button class="btn"
                        id="bbMulti"></button>
            </div>

            <div class="upgrade">
                <h3>โค๏ธ Extra Life</h3>
                <p>Start with more lives.</p>
                <button class="btn"
                        id="bbLife"></button>
            </div>

        </div>

        <div class="notice"
             id="bbNotice">
            Break every brick!
        </div>
    `;

    const canvas=
        document.getElementById(
            "brickCanvas"
        );

    const ctx=
        canvas.getContext("2d");

    let paddleLv=
        load("bb_paddle",0);

    let powerLv=
        load("bb_power",0);

    let multiLv=
        load("bb_multi",0);

    let lifeLv=
        load("bb_life",0);

    let best=
        load("bb_best",0);

    let score=0;
    let level=1;
    let lives=3;
    let running=false;
    let raf;

    let paddle;
    let balls;
    let bricks;

    function makeBricks(){

        bricks=[];

        const rows=
            Math.min(
                5+level,
                9
            );

        const cols=10;
        const gap=6;
        const bw=74;
        const bh=22;

        for(
            let r=0;
            r<rows;
            r++
        ){

            for(
                let c=0;
                c<cols;
                c++
            ){

                bricks.push({

                    x:
                        20+
                        c*(bw+gap),

                    y:
                        55+
                        r*(bh+gap),

                    w:bw,
                    h:bh,

                    hp:
                        r%4===0
                        ?2
                        :1
                });
            }
        }
    }

    function reset(){

        score=0;
        level=1;

        lives=
            3+lifeLv;

        paddle={
            x:350,
            y:480,
            w:
                130+
                paddleLv*12,
            h:15
        };

        balls=[
            {
                x:425,
                y:450,
                vx:4,
                vy:-5
            }
        ];

        makeBricks();

        running=true;

        loop();
    }

    function launch(){

        if(!running)return;

        balls.forEach(
            b=>{
                b.vy=
                    -Math.abs(
                        b.vy||5
                    );
            }
        );
    }

    function update(){

        balls.forEach(b=>{

            b.x+=b.vx;
            b.y+=b.vy;

            if(
                b.x<7 ||
                b.x>843
            )
                b.vx*=-1;

            if(b.y<7)
                b.vy*=-1;

            if(
                b.y+8>=paddle.y &&
                b.y-8<=
                    paddle.y+
                    paddle.h &&
                b.x>=paddle.x &&
                b.x<=
                    paddle.x+
                    paddle.w &&
                b.vy>0
            ){

                const hit=
                    (
                        b.x-
                        (
                            paddle.x+
                            paddle.w/2
                        )
                    )/
                    (
                        paddle.w/2
                    );

                b.vx=hit*6;
                b.vy=
                    -Math.abs(b.vy);
            }

            for(
                let i=bricks.length-1;
                i>=0;
                i--
            ){

                const br=
                    bricks[i];

                if(
                    b.x+7>br.x &&
                    b.x-7<
                        br.x+br.w &&
                    b.y+7>br.y &&
                    b.y-7<
                        br.y+br.h
                ){

                    br.hp-=
                        powerLv>=2
                        ?2
                        :1;

                    b.vy*=-1;

                    score+=
                        br.hp<=0
                        ?10
                        :3;

                    if(br.hp<=0){

                        bricks.splice(
                            i,
                            1
                        );

                        /* Brick destroyed = arcade coin */

                        coinReward(1);

                        if(
                            multiLv>0 &&
                            Math.random()<
                            .05*multiLv
                        ){

                            balls.push({

                                x:b.x,
                                y:b.y,
                                vx:-b.vx,
                                vy:b.vy
                            });
                        }
                    }

                    break;
                }
            }
        });

        if(bricks.length===0){

            level++;

            score+=
                100*level;

            coinReward(
                10+level
            );

            makeBricks();

            balls.forEach(
                b=>{
                    b.vx*=1.06;
                    b.vy*=1.06;
                }
            );
        }

        balls=
            balls.filter(
                b=>b.y<540
            );

        if(balls.length===0){

            lives--;

            if(lives<=0){

                running=false;

                best=Math.max(
                    best,
                    score
                );

                save(
                    "bb_best",
                    best
                );

                coinReward(
                    Math.floor(
                        score/50
                    )
                );

                document.getElementById(
                    "bbNotice"
                ).textContent=
                    `GAME OVER โ€ข ${score}`;

            }else{

                balls=[{
                    x:425,
                    y:450,
                    vx:4,
                    vy:-5
                }];
            }
        }

        updateStats();
    }

    function updateStats(){

        document.getElementById(
            "bbScore"
        ).textContent=score;

        document.getElementById(
            "bbLevel"
        ).textContent=level;

        document.getElementById(
            "bbLives"
        ).textContent=lives;

        document.getElementById(
            "bbBest"
        ).textContent=best;
    }

    function draw(){

        ctx.fillStyle="#070c1b";

        ctx.fillRect(
            0,0,850,520
        );

        bricks.forEach(
            br=>{

                ctx.fillStyle=
                    br.hp===2
                    ?" #9b6cff".trim()
                    :"#35e7ff";

                ctx.fillRect(
                    br.x,
                    br.y,
                    br.w,
                    br.h
                );
            }
        );

        ctx.fillStyle="#ff4fd8";

        ctx.fillRect(
            paddle.x,
            paddle.y,
            paddle.w,
            paddle.h
        );

        balls.forEach(
            b=>{

                ctx.fillStyle="#fff";

                ctx.shadowBlur=18;
                ctx.shadowColor="#ffd84d";

                ctx.beginPath();

                ctx.arc(
                    b.x,
                    b.y,
                    8,
                    0,
                    Math.PI*2
                );

                ctx.fill();

                ctx.shadowBlur=0;
            }
        );
    }

    function loop(){

        if(!running){

            draw();
            return;
        }

        update();
        draw();

        raf=
            requestAnimationFrame(
                loop
            );
    }

    function left(){

        paddle.x=
            Math.max(
                0,
                paddle.x-35
            );
    }

    function right(){

        paddle.x=
            Math.min(
                850-paddle.w,
                paddle.x+35
            );
    }

    const key=e=>{

        if(
            ["ArrowLeft","a","A"]
            .includes(e.key)
        )
            left();

        if(
            ["ArrowRight","d","D"]
            .includes(e.key)
        )
            right();

        if(e.key===" ")
            launch();
    };

    document.addEventListener(
        "keydown",
        key
    );

    document.getElementById(
        "bbStart"
    ).onclick=reset;

    document.getElementById(
        "bbLaunch"
    ).onclick=launch;

    document.getElementById(
        "bbLeft"
    ).onclick=left;

    document.getElementById(
        "bbRight"
    ).onclick=right;

    canvas.addEventListener(
        "mousemove",
        e=>{

            const rect=
                canvas.getBoundingClientRect();

            const x=
                (
                    e.clientX-
                    rect.left
                )/
                rect.width*
                850;

            paddle.x=
                Math.max(
                    0,
                    Math.min(
                        850-paddle.w,
                        x-paddle.w/2
                    )
                );
        }
    );


    /* REAL ARCADE COIN UPGRADES */

    function upgrade(
        id,
        key,
        base
    ){

        const button=
            document.getElementById(id);

        function refresh(){

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            button.textContent=
                `${cost} ๐Ÿช™ Upgrade`;
        }

        button.onclick=()=>{

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            if(spendCoins(cost)){

                save(
                    key,
                    level+1
                );

                refresh();
            }
        };

        refresh();
    }

    upgrade(
        "bbPaddle",
        "bb_paddle",
        20
    );

    upgrade(
        "bbPower",
        "bb_power",
        30
    );

    upgrade(
        "bbMulti",
        "bb_multi",
        40
    );

    upgrade(
        "bbLife",
        "bb_life",
        35
    );

    reset();

    return()=>{

        cancelAnimationFrame(
            raf
        );

        document.removeEventListener(
            "keydown",
            key
        );
    };
}


/* =========================================================
   HIGHWAY RACER
========================================================= */

function highwayRacer(){

    gameContent.innerHTML=`
        <div class="game-stats">
            <div class="game-stat">DISTANCE<strong id="raceDistance">0</strong></div>
            <div class="game-stat">COINS<strong id="raceCoins">0</strong></div>
            <div class="game-stat">SPEED<strong id="raceSpeed">0</strong></div>
            <div class="game-stat">NITRO<strong id="raceNitro">100%</strong></div>
            <div class="game-stat">BEST<strong id="raceBest">0</strong></div>
        </div>

        <canvas id="racerCanvas"
                width="900"
                height="450"></canvas>

        <div class="touch-controls">
            <button id="raceLeft">โ—€</button>
            <button id="raceNitroBtn">๐Ÿ”ฅ</button>
            <button id="raceRight">โ–ถ</button>
        </div>

        <div class="controls">
            <button class="btn primary"
                    id="raceStart">
                ๐Ÿ Start Race
            </button>
        </div>

        <div class="upgrades">

            <div class="upgrade">
                <h3>๐Ÿš€ Engine</h3>
                <p>Increase maximum speed.</p>
                <button class="btn"
                        id="raceEngine"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ›ž Handling</h3>
                <p>Turn faster.</p>
                <button class="btn"
                        id="raceHandling"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿงฒ Coin Magnet</h3>
                <p>Collect coins from farther away.</p>
                <button class="btn"
                        id="raceMagnet"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ”ฅ Nitro Tank</h3>
                <p>Increase your nitro supply.</p>
                <button class="btn"
                        id="raceNitro"></button>
            </div>

            <div class="upgrade">
                <h3>๐Ÿ›ก๏ธ Armor</h3>
                <p>Survive more crashes.</p>
                <button class="btn"
                        id="raceArmor"></button>
            </div>

        </div>

        <div class="notice"
             id="raceNotice">
            Use A/D or โ†/โ†’ โ€ข Space for nitro
        </div>
    `;

    const canvas=
        document.getElementById(
            "racerCanvas"
        );

    const ctx=
        canvas.getContext("2d");

    let engine=
        load("race_engine",0);

    let handling=
        load("race_handling",0);

    let magnet=
        load("race_magnet",0);

    let nitroLv=
        load("race_nitro",0);

    let armor=
        load("race_armor",0);

    let best=
        load("race_best",0);

    let distance=0;
    let coins=0;
    let speed=0;
    let nitro=100;

    let running=false;
    let frame=0;
    let raf;
    let roadOffset=0;

    let player;
    let traffic;
    let coinList;

    function start(){

        distance=0;
        coins=0;

        speed=
            3+
            engine*.5;

        nitro=
            100+
            nitroLv*25;

        frame=0;
        roadOffset=0;

        player={
            x:450,
            y:350,
            w:45,
            h:75,
            hp:1+armor
        };

        traffic=[];
        coinList=[];

        running=true;

        loop();
    }

    function spawnTraffic(){

        if(
            frame%
            Math.max(
                18,
                55-
                Math.floor(
                    distance/250
                )
            )===0
        ){

            const lanes=[
                280,
                390,
                500,
                610
            ];

            traffic.push({

                x:
                    lanes[
                        Math.floor(
                            Math.random()*
                            lanes.length
                        )
                    ],

                y:-100,

                w:44,
                h:75,

                speed:
                    2+
                    Math.random()*3
            });
        }

        if(frame%35===0){

            const lanes=[
                280,
                390,
                500,
                610
            ];

            coinList.push({

                x:
                    lanes[
                        Math.floor(
                            Math.random()*
                            lanes.length
                        )
                    ],

                y:-30,

                r:8
            });
        }
    }

    function collide(a,b){

        return(
            a.x-a.w/2<
                b.x+b.w/2 &&

            a.x+a.w/2>
                b.x-b.w/2 &&

            a.y-a.h/2<
                b.y+b.h/2 &&

            a.y+a.h/2>
                b.y-b.h/2
        );
    }

    function update(){

        frame++;

        if(
            speed<
            12+engine
        )
            speed+=.01;

        distance+=
            speed*.04;

        roadOffset=
            (
                roadOffset+
                speed*3
            )%80;

        spawnTraffic();

        traffic.forEach(
            t=>
                t.y+=
                speed+t.speed
        );

        coinList.forEach(
            c=>
                c.y+=speed
        );

        traffic=
            traffic.filter(
                t=>t.y<520
            );

        coinList=
            coinList.filter(
                c=>c.y<500
            );

        for(
            let i=traffic.length-1;
            i>=0;
            i--
        ){

            if(
                collide(
                    player,
                    traffic[i]
                )
            ){

                traffic.splice(
                    i,
                    1
                );

                player.hp--;

                if(player.hp<=0){

                    running=false;

                    best=Math.max(
                        best,
                        Math.floor(
                            distance
                        )
                    );

                    save(
                        "race_best",
                        best
                    );

                    coinReward(
                        Math.floor(
                            distance/50
                        )
                    );

                    document.getElementById(
                        "raceNotice"
                    ).textContent=
                        `CRASH! ${Math.floor(distance)}m`;

                    draw();

                    return;
                }
            }
        }

        for(
            let i=coinList.length-1;
            i>=0;
            i--
        ){

            const c=
                coinList[i];

            const dx=
                player.x-c.x;

            const dy=
                player.y-c.y;

            const d=
                Math.hypot(
                    dx,
                    dy
                );

            if(
                magnet>0 &&
                d<
                80+
                magnet*30
            ){

                c.x+=dx*.1;
                c.y+=dy*.1;
            }

            if(d<35){

                coins++;

                /* REAL ARCADE COIN */

                coinReward(1);

                coinList.splice(
                    i,
                    1
                );
            }
        }

        document.getElementById(
            "raceDistance"
        ).textContent=
            Math.floor(
                distance
            )+"m";

        document.getElementById(
            "raceCoins"
        ).textContent=
            coins;

        document.getElementById(
            "raceSpeed"
        ).textContent=
            Math.floor(
                speed*20
            );

        document.getElementById(
            "raceNitro"
        ).textContent=
            Math.floor(
                nitro
            )+"%";

        document.getElementById(
            "raceBest"
        ).textContent=
            best;
    }

    function draw(){

        ctx.clearRect(
            0,0,900,450
        );

        /* grass */

        ctx.fillStyle="#102719";

        ctx.fillRect(
            0,0,900,450
        );

        /* road */

        ctx.fillStyle="#252932";

        ctx.fillRect(
            180,0,540,450
        );

        /* edges */

        ctx.fillStyle="#ddd";

        ctx.fillRect(
            180,0,6,450
        );

        ctx.fillRect(
            714,0,6,450
        );

        /* lane lines */

        ctx.strokeStyle="#ffffff88";

        ctx.lineWidth=5;

        ctx.setLineDash(
            [35,45]
        );

        ctx.lineDashOffset=
            roadOffset;

        for(
            const x of [
                312,
                445,
                578
            ]
        ){

            ctx.beginPath();

            ctx.moveTo(
                x,
                0
            );

            ctx.lineTo(
                x,
                450
            );

            ctx.stroke();
        }

        ctx.setLineDash([]);

        traffic.forEach(
            t=>
                drawCar(
                    t.x,
                    t.y,
                    t.w,
                    t.h,
                    "#ff475e"
                )
        );

        coinList.forEach(
            c=>{

                ctx.fillStyle=
                    "#ffd84d";

                ctx.shadowBlur=18;
                ctx.shadowColor=
                    "#ffd84d";

                ctx.beginPath();

                ctx.arc(
                    c.x,
                    c.y,
                    c.r,
                    0,
                    Math.PI*2
                );

                ctx.fill();

                ctx.shadowBlur=0;
            }
        );

        drawCar(
            player.x,
            player.y,
            player.w,
            player.h,
            "#35e7ff"
        );

        if(nitro>0){

            ctx.fillStyle="#54f59a";

            ctx.fillRect(
                player.x-7,
                player.y+
                    player.h/2,
                14,
                25+
                    Math.random()*15
            );
        }
    }

    function drawCar(
        x,
        y,
        w,
        h,
        color
    ){

        ctx.fillStyle=color;

        ctx.shadowBlur=18;
        ctx.shadowColor=color;

        ctx.fillRect(
            x-w/2,
            y-h/2,
            w,
            h
        );

        ctx.shadowBlur=0;

        ctx.fillStyle="#10151e";

        ctx.fillRect(
            x-w/2+7,
            y-h/2+10,
            w-14,
            22
        );

        ctx.fillStyle="#fff";

        ctx.fillRect(
            x-w/2+4,
            y-h/2+5,
            7,
            10
        );

        ctx.fillRect(
            x+w/2-11,
            y-h/2+5,
            7,
            10
        );
    }

    function loop(){

        if(!running){

            draw();
            return;
        }

        update();
        draw();

        raf=
            requestAnimationFrame(
                loop
            );
    }

    function left(){

        if(running)

            player.x=
                Math.max(
                    215,
                    player.x-
                    (
                        10+
                        handling*3
                    )
                );
    }

    function right(){

        if(running)

            player.x=
                Math.min(
                    685,
                    player.x+
                    (
                        10+
                        handling*3
                    )
                );
    }

    function useNitro(){

        if(
            running &&
            nitro>0
        ){

            speed=
                Math.min(
                    20+engine,
                    speed+1
                );

            nitro-=3;
        }
    }

    const key=e=>{

        if(
            ["ArrowLeft","a","A"]
            .includes(e.key)
        )
            left();

        if(
            ["ArrowRight","d","D"]
            .includes(e.key)
        )
            right();

        if(e.key===" ")
            useNitro();
    };

    document.addEventListener(
        "keydown",
        key
    );

    document.getElementById(
        "raceStart"
    ).onclick=start;

    document.getElementById(
        "raceLeft"
    ).onclick=left;

    document.getElementById(
        "raceRight"
    ).onclick=right;

    document.getElementById(
        "raceNitroBtn"
    ).onclick=useNitro;


    /* REAL COIN UPGRADES */

    function upgrade(
        id,
        key,
        base
    ){

        const button=
            document.getElementById(id);

        function refresh(){

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            button.textContent=
                `${cost} ๐Ÿช™ Upgrade`;
        }

        button.onclick=()=>{

            const level=
                load(key,0);

            const cost=
                base+
                level*base;

            if(spendCoins(cost)){

                save(
                    key,
                    level+1
                );

                refresh();
            }
        };

        refresh();
    }

    upgrade(
        "raceEngine",
        "race_engine",
        15
    );

    upgrade(
        "raceHandling",
        "race_handling",
        20
    );

    upgrade(
        "raceMagnet",
        "race_magnet",
        25
    );

    upgrade(
        "raceNitro",
        "race_nitro",
        20
    );

    upgrade(
        "raceArmor",
        "race_armor",
        35
    );

    start();

    return()=>{

        cancelAnimationFrame(
            raf
        );

        document.removeEventListener(
            "keydown",
            key
        );
    };
}
</script>

81,275 chars ยท codepastes.com