🎮ArcadeLab

Clash Royale - Deluxe Edition

by NovaGalaxy88
623 lines24.1 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Clash Royale - Deluxe Edition</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #2c3e50;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            font-family: 'Arial', sans-serif;
            color: white;
            overflow: hidden;
            user-select: none;
        }
        canvas {
            background-color: #228B22;
            border: 4px solid #34495e;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            display: block;
            cursor: pointer;
        }
    </style>
</head>
<body>

<canvas id="gameCanvas" width="450" height="700"></canvas>

<script>
const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");

// --- GLOBALE STATS (bleiben permanent erhalten) ---
const FPS = 60;
const SCREEN_WIDTH = 450;
const SCREEN_HEIGHT = 700;

let currentLevel = 1;
const MAX_LEVEL = 25;
let trophies = 1000;
let gold = 500;

// Menü-Navigation: "menu", "game", "shop", "quests", "emotes"
let currentScreen = "menu"; 

// --- ENTITÄTEN-KLASSEN (KAMPF) ---
class Tower {
    constructor(x, y, team, isKing = false, aiLevel = 1) {
        this.x = x;
        this.y = y;
        this.team = team;
        this.isKing = isKing;
        this.radius = isKing ? 25 : 18;
        
        // Level 1 viel leichter machen für die KI (0.6x HP)
        let hpMultiplier = 1;
        let dmgMultiplier = 1;
        if (team === "red") {
            if (aiLevel === 1) {
                hpMultiplier = 0.6;
                dmgMultiplier = 0.6;
            } else {
                hpMultiplier = 1 + (aiLevel - 1) * 0.1;
                dmgMultiplier = 1 + (aiLevel - 1) * 0.05;
            }
        }

        this.maxHp = (isKing ? 3000 : 1500) * hpMultiplier;
        this.hp = this.maxHp;
        this.range = 120;
        this.damage = (isKing ? 50 : 35) * dmgMultiplier;
        this.attackCooldown = 0;
        this.laserTarget = null;
        this.laserTimer = 0;
    }

    draw() {
        if (this.hp <= 0) return;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = this.team === "blue" ? "#0000FF" : "#FF0000";
        ctx.fill();
        ctx.lineWidth = 3;
        ctx.strokeStyle = "#323232";
        ctx.stroke();

        const barWidth = this.radius * 2;
        const hpPct = Math.max(0, this.hp / this.maxHp);
        ctx.fillStyle = "#FF0000";
        ctx.fillRect(this.x - this.radius, this.y - this.radius - 10, barWidth, 5);
        ctx.fillStyle = "#00FF00";
        ctx.fillRect(this.x - this.radius, this.y - this.radius - 10, barWidth * hpPct, 5);

        if (this.laserTimer > 0 && this.laserTarget && this.laserTarget.hp > 0) {
            ctx.beginPath();
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(this.laserTarget.x, this.laserTarget.y);
            ctx.strokeStyle = "#FFD700";
            ctx.lineWidth = 3;
            ctx.stroke();
            this.laserTimer--;
        }
    }

    update(enemies) {
        if (this.hp <= 0) return;
        if (this.attackCooldown > 0) this.attackCooldown--;

        if (this.attackCooldown === 0) {
            let target = null;
            let minDist = this.range;
            for (let enemy of enemies) {
                let dist = Math.hypot(enemy.x - this.x, enemy.y - this.y);
                if (dist < minDist) { minDist = dist; target = enemy; }
            }
            if (target) {
                target.hp -= this.damage;
                this.attackCooldown = 50;
                this.laserTarget = target;
                this.laserTimer = 8;
            }
        }
    }
}

class Unit {
    constructor(x, y, team, unitType, aiLevel = 1) {
        this.x = x;
        this.y = y;
        this.team = team;
        this.type = unitType;
        this.laserTarget = null;
        this.laserTimer = 0;
        this.attackCooldown = 0;

        let baseHp = 0, baseDmg = 0;
        if (unitType === "Knight") { baseHp = 600; baseDmg = 40; this.range = 20; this.speed = 1.2; this.radius = 10; }
        else if (unitType === "Archer") { baseHp = 250; baseDmg = 25; this.range = 90; this.speed = 1.5; this.radius = 7; }
        else if (unitType === "Giant") { baseHp = 2000; baseDmg = 50; this.range = 20; this.speed = 0.7; this.radius = 14; }
        else if (unitType === "Goblins") { baseHp = 120; baseDmg = 30; this.range = 20; this.speed = 2.2; this.radius = 6; }

        let hpMultiplier = 1;
        let dmgMultiplier = 1;
        if (team === "red") {
            if (aiLevel === 1) {
                hpMultiplier = 0.5; // Level 1 Einheiten der KI haben nur halbes Leben
                dmgMultiplier = 0.6;
            } else {
                hpMultiplier = 1 + (aiLevel - 1) * 0.08;
                dmgMultiplier = 1 + (aiLevel - 1) * 0.05;
            }
        }

        this.hp = this.maxHp = baseHp * hpMultiplier;
        this.damage = baseDmg * dmgMultiplier;
    }

    draw() {
        if (this.hp <= 0) return;
        ctx.beginPath();
        ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        ctx.fillStyle = this.team === "blue" ? "#0000FF" : "#FF0000";
        ctx.fill();

        if (this.type === "Archer") {
            ctx.beginPath(); ctx.arc(this.x, this.y, this.radius - 3, 0, Math.PI * 2);
            ctx.fillStyle = "#FFFFFF"; ctx.fill();
        } else if (this.type === "Giant") {
            ctx.beginPath(); ctx.arc(this.x, this.y, this.radius - 4, 0, Math.PI * 2);
            ctx.fillStyle = "#FFD700"; ctx.fill();
        } else if (this.type === "Goblins") {
            ctx.beginPath(); ctx.arc(this.x, this.y, this.radius - 2, 0, Math.PI * 2);
            ctx.fillStyle = "#32C832"; ctx.fill();
        }

        const hpPct = Math.max(0, this.hp / this.maxHp);
        ctx.fillStyle = "#FF0000";
        ctx.fillRect(this.x - 10, this.y - this.radius - 6, 20, 3);
        ctx.fillStyle = "#00FF00";
        ctx.fillRect(this.x - 10, this.y - this.radius - 6, 20 * hpPct, 3);

        if (this.laserTimer > 0 && this.laserTarget && this.laserTarget.hp > 0) {
            ctx.beginPath(); ctx.moveTo(this.x, this.y); ctx.lineTo(this.laserTarget.x, this.laserTarget.y);
            ctx.strokeStyle = "#FFFFFF"; ctx.lineWidth = 2; ctx.stroke();
            this.laserTimer--;
        }
    }

    moveAndAttack(enemies, enemyTowers) {
        if (this.hp <= 0) return;
        if (this.attackCooldown > 0) this.attackCooldown--;

        let target = null;
        let targetDist = 999990;

        if (this.type === "Giant") {
            for (let tower of enemyTowers) {
                if (tower.hp > 0) {
                    let dist = Math.hypot(tower.x - this.x, tower.y - this.y);
                    if (dist < targetDist) { targetDist = dist; target = tower; }
                }
            }
        } else {
            for (let enemy of enemies) {
                let dist = Math.hypot(enemy.x - this.x, enemy.y - this.y);
                if (dist < targetDist) { targetDist = dist; target = enemy; }
            }
            if (!target) {
                for (let tower of enemyTowers) {
                    if (tower.hp > 0) {
                        let dist = Math.hypot(tower.x - this.x, tower.y - this.y);
                        if (dist < targetDist) { targetDist = dist; target = tower; }
                    }
                }
            }
        }

        if (!target) return;

        if (targetDist <= this.range) {
            if (this.attackCooldown === 0) {
                target.hp -= this.damage;
                this.attackCooldown = 40;
                if (this.type === "Archer") { this.laserTarget = target; this.laserTimer = 5; }
            }
        } else {
            let targetX = target.x;
            let targetY = target.y;

            if (this.team === "blue" && this.y > 340 && targetY < 340) {
                let bridgeX = this.x < SCREEN_WIDTH / 2 ? 100 : 350;
                if (Math.abs(this.y - 350) > 10) { targetX = bridgeX; targetY = 350; }
            } else if (this.team === "red" && this.y < 340 && targetY > 340) {
                let bridgeX = this.x < SCREEN_WIDTH / 2 ? 100 : 350;
                if (Math.abs(this.y - 350) > 10) { targetX = bridgeX; targetY = 350; }
            }

            let dx = targetX - this.x;
            let dy = targetY - this.y;
            let dist = Math.hypot(dx, dy);
            if (dist > 0) {
                this.x += (dx / dist) * this.speed;
                this.y += (dy / dist) * this.speed;
            }
        }
    }
}

// --- GAME STATE ENGINE ---
class Game {
    constructor(level) {
        this.level = level;
        this.towers = [
            new Tower(SCREEN_WIDTH / 2, 590, "blue", true, level),
            new Tower(120, 520, "blue", false, level),
            new Tower(330, 520, "blue", false, level),
            
            new Tower(SCREEN_WIDTH / 2, 70, "red", true, level),
            new Tower(120, 140, "red", false, level),
            new Tower(330, 140, "red", false, level)
        ];

        this.units = [];
        this.blueElixir = 5.0;
        this.redElixir = 5.0;

        this.allCards = ["Knight", "Archer", "Giant", "Goblins"];
        this.cardCosts = {"Knight": 3, "Archer": 2, "Giant": 5, "Goblins": 2};
        this.deck = ["Knight", "Archer", "Giant", "Goblins"];
        this.selectedIndex = 0;

        this.cardButtons = [
            {x: 180, y: 615, w: 55, h: 70},
            {x: 245, y: 615, w: 55, h: 70},
            {x: 310, y: 615, w: 55, h: 70},
            {x: 375, y: 615, w: 55, h: 70}
        ];

        // Emote-System Variablen
        this.emoteMenuOpen = false;
        this.activeEmote = null;
        this.activeEmoteTimer = 0;

        this.spawnCooldown = 0;
        this.timeLeft = 180 * FPS; 
        this.gameOver = false;
        this.endMessage = "";
        this.levelChangedText = "";
    }

    getSelectedCard() { return this.deck[this.selectedIndex]; }
    rotateCard() { let played = this.deck.splice(this.selectedIndex, 1)[0]; this.deck.push(played); }

    spawnUnit(x, y, team, unitType) {
        let cost = this.cardCosts[unitType];
        if (team === "blue") {
            if (y < 340 || y > 600 || this.blueElixir < cost) return false;
            this.blueElixir -= cost;
            this.rotateCard();
        } else {
            if (this.redElixir < cost) return false;
            this.redElixir -= cost;
        }

        if (unitType === "Goblins") {
            for (let i = 0; i < 3; i++) {
                this.units.push(new Unit(x + (Math.random()*30-15), y + (Math.random()*30-15), team, unitType, this.level));
            }
        } else {
            this.units.push(new Unit(x, y, team, unitType, this.level));
        }
        return true;
    }

    update() {
        if (this.gameOver) return;
        if (this.timeLeft > 0) this.timeLeft--;
        else this.checkTiebreaker();

        // Elixier-Generierung (In Level 1 lädt die KI extrem langsam!)
        if (this.blueElixir < 10) this.blueElixir += 0.015;
        
        let aiElixirSpeed = this.level === 1 ? 0.008 : 0.015 + (this.level - 1) * 0.002;
        if (this.redElixir < 10) this.redElixir += aiElixirSpeed;

        let blueUnits = this.units.filter(u => u.team === "blue" && u.hp > 0);
        let redUnits = this.units.filter(u => u.team === "red" && u.hp > 0);
        let blueTowers = this.towers.filter(t => t.team === "blue" && t.hp > 0);
        let redTowers = this.towers.filter(t => t.team === "red" && t.hp > 0);
        this.units = blueUnits.concat(redUnits);

        // Bot Spawn-Verhalten
        this.spawnCooldown++;
        let aiSpawnRate = this.level === 1 ? 160 : Math.max(50, 120 - (this.level * 2));
        if (this.spawnCooldown > aiSpawnRate) {
            if (this.redElixir >= 4) {
                let randCard = this.allCards[Math.floor(Math.random() * this.allCards.length)];
                let spawnX = [120, 330, SCREEN_WIDTH / 2][Math.floor(Math.random() * 3)];
                let spawnY = [100, 120][Math.floor(Math.random() * 2)];
                this.spawnUnit(spawnX, spawnY, "red", randCard);
                this.spawnCooldown = 0;
            }
        }

        for (let tower of this.towers) tower.update(tower.team === "blue" ? redUnits : blueUnits);
        for (let unit of this.units) {
            if (unit.team === "blue") unit.moveAndAttack(redUnits, redTowers);
            else unit.moveAndAttack(blueUnits, blueTowers);
        }

        if (this.activeEmoteTimer > 0) this.activeEmoteTimer--;

        if (this.towers[0].hp <= 0) this.endGame(false);
        else if (this.towers[3].hp <= 0) this.endGame(true);
    }

    checkTiebreaker() {
        let blueAlive = this.towers.filter(t => t.team === "blue" && t.hp > 0).length;
        let redAlive = this.towers.filter(t => t.team === "red" && t.hp > 0).length;
        if (blueAlive > redAlive) this.endGame(true);
        else if (redAlive > blueAlive) this.endGame(false);
        else { this.gameOver = true; this.endMessage = "UNENTSCHIEDEN!"; }
    }

    endGame(playerWon) {
        this.gameOver = true;
        if (playerWon) {
            trophies += 30; gold += 150;
            this.endMessage = "SIEG! (+30 🏆 / +150 💰)";
            if (currentLevel < MAX_LEVEL) { currentLevel++; }
        } else {
            trophies = Math.max(0, trophies - 20);
            this.endMessage = "NIEDERLAGE! (-20 🏆)";
            if (currentLevel > 1) { currentLevel--; }
        }
    }

    drawMap() {
        ctx.fillStyle = "#228B22"; ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
        ctx.fillStyle = "#1E90FF"; ctx.fillRect(0, 340, SCREEN_WIDTH, 20);
        ctx.fillStyle = "#8B4513"; ctx.fillRect(80, 335, 40, 30); ctx.fillRect(330, 335, 40, 30);
    }

    drawUi() {
        // UI Leiste unten
        ctx.fillStyle = "#2c3e50"; ctx.fillRect(0, 600, SCREEN_WIDTH, 100);

        // Elixier
        ctx.fillStyle = "#323232"; ctx.fillRect(20, 615, 140, 20);
        ctx.fillStyle = "#FF00FF"; ctx.fillRect(20, 615, (this.blueElixir / 10) * 140, 20);
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 12px Arial"; ctx.fillText(`Punkte: ${Math.floor(this.blueElixir)}/10`, 35, 629);

        // Emote Button (☺) unten links
        ctx.fillStyle = "#f39c12"; ctx.fillRect(20, 645, 40, 40);
        ctx.strokeStyle = "#FFFFFF"; ctx.strokeRect(20, 645, 40, 40);
        ctx.fillStyle = "#FFFFFF"; ctx.font = "20px Arial"; ctx.fillText("☺", 33, 672);

        // Deck Boxen
        this.deck.forEach((card, i) => {
            let btn = this.cardButtons[i];
            ctx.fillStyle = "#34495e"; ctx.fillRect(btn.x, btn.y, btn.w, btn.h);
            ctx.strokeStyle = (i === this.selectedIndex) ? "#FFD700" : "#7f8c8d";
            ctx.lineWidth = (i === this.selectedIndex) ? 3 : 1;
            ctx.strokeRect(btn.x, btn.y, btn.w, btn.h);
            ctx.fillStyle = (i === this.selectedIndex) ? "#FFD700" : "#FFFFFF";
            ctx.font = "bold 10px Arial"; ctx.fillText(card, btn.x + 4, btn.y + 25);
            ctx.fillStyle = "#FF00FF"; ctx.beginPath(); ctx.arc(btn.x + btn.w - 12, btn.y + btn.h - 12, 8, 0, Math.PI*2); ctx.fill();
            ctx.fillStyle = "#FFFFFF"; ctx.fillText(this.cardCosts[card], btn.x + btn.w - 15, btn.y + btn.h - 9);
        });

        // Topbar
        let totalSecs = Math.floor(this.timeLeft / FPS), mins = Math.floor(totalSecs / 60), secs = totalSecs % 60;
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 16px Arial"; ctx.fillText(`Zeit: ${mins}:${secs < 10 ? '0' : ''}${secs}`, 20, 25);
        ctx.textAlign = "center"; ctx.fillText(`Level ${this.level} / 25`, SCREEN_WIDTH/2, 25); ctx.textAlign = "left";

        // Aktives Emote auf dem Feld anzeigen
        if (this.activeEmoteTimer > 0 && this.activeEmote) {
            ctx.font = "40px Arial";
            ctx.fillText(this.activeEmote, SCREEN_WIDTH / 2 - 20, 450);
        }

        // Emote Menü Popup
        if (this.emoteMenuOpen) {
            ctx.fillStyle = "rgba(0,0,0,0.8)"; ctx.fillRect(20, 480, 150, 110);
            ctx.fillStyle = "#FFFFFF"; ctx.font = "12px Arial"; ctx.fillText("Wähle ein Emote:", 30, 500);
            ctx.font = "24px Arial";
            ctx.fillText("😂", 35, 535); ctx.fillText("😡", 75, 535); ctx.fillText("👍", 115, 535);
            ctx.fillText("😭", 35, 575); ctx.fillText("👑", 75, 575); ctx.fillText("⚔", 115, 575);
        }

        if (this.gameOver) {
            ctx.fillStyle = "rgba(0, 0, 0, 0.85)"; ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
            ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 26px Arial"; ctx.textAlign = "center";
            ctx.fillText(this.endMessage, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 - 20);
            ctx.fillStyle = "#FFD700"; ctx.font = "14px Arial"; ctx.fillText("Drücke LEERTASTE für das Hauptmenü", SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2 + 30);
            ctx.textAlign = "left";
        }
    }
}

// --- MENÜ INTERFACES DRAWER ---
function drawMenuScreens() {
    // Oberer Balken (Ressourcen immer sichtbar)
    ctx.fillStyle = "#1a252f"; ctx.fillRect(0, 0, SCREEN_WIDTH, 60);
    ctx.fillStyle = "#FFD700"; ctx.font = "bold 16px Arial"; ctx.fillText(`🏆 ${trophies}`, 30, 38);
    ctx.fillStyle = "#f1c40f"; ctx.fillText(`💰 ${gold} Gold`, SCREEN_WIDTH - 120, 38);

    // Untere Tab-Leiste
    ctx.fillStyle = "#1a252f"; ctx.fillRect(0, 630, SCREEN_WIDTH, 70);
    const tabs = ["Shop", "Quests", "Kampf", "Emotes"];
    tabs.forEach((tab, i) => {
        let x = i * (SCREEN_WIDTH / 4);
        ctx.fillStyle = (currentScreen === tab.toLowerCase() || (currentScreen === "menu" && tab === "Kampf")) ? "#34495e" : "#1a252f";
        ctx.fillRect(x, 630, SCREEN_WIDTH / 4, 70);
        ctx.strokeStyle = "#2c3e50"; ctx.strokeRect(x, 630, SCREEN_WIDTH / 4, 70);
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 14px Arial"; ctx.fillText(tab, x + 20, 670);
    });

    // Content je nach gewähltem Screen
    ctx.fillStyle = "#2c3e50"; ctx.fillRect(0, 60, SCREEN_WIDTH, 570);

    if (currentScreen === "menu") {
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 28px Arial"; ctx.fillText("ARENA STUFE", 125, 150);
        ctx.fillStyle = "#f39c12"; ctx.font = "bold 36px Arial"; ctx.fillText(`LEVEL ${currentLevel}`, 145, 210);

        // Großer Kampfbutton
        ctx.fillStyle = "#e67e22"; ctx.fillRect(125, 300, 200, 80);
        ctx.strokeStyle = "#FFFFFF"; ctx.lineWidth = 3; ctx.strokeRect(125, 300, 200, 80);
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 28px Arial"; ctx.fillText("KAMPF", 175, 350);
    } 
    else if (currentScreen === "shop") {
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 24px Arial"; ctx.fillText("KARTEN SHOP", 30, 110);
        
        // Gratis Truhe
        ctx.fillStyle = "#34495e"; ctx.fillRect(40, 160, 160, 150);
        ctx.fillStyle = "#FFFFFF"; ctx.font = "14px Arial"; ctx.fillText("Gratis Truhe", 50, 190);
        ctx.fillStyle = "#2ecc71"; ctx.fillRect(60, 250, 120, 35);
        ctx.fillStyle = "#FFFFFF"; ctx.fillText("ÖFFNEN (0g)", 75, 272);

        // Magietruhe
        ctx.fillStyle = "#34495e"; ctx.fillRect(240, 160, 160, 150);
        ctx.fillStyle = "#9b59b6"; ctx.fillText("Magietruhe", 250, 190);
        ctx.fillStyle = "#e74c3c"; ctx.fillRect(260, 250, 120, 35);
        ctx.fillStyle = "#FFFFFF"; ctx.fillText("250 Gold", 290, 272);
    } 
    else if (currentScreen === "quests") {
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 24px Arial"; ctx.fillText("DAILY QUESTS", 30, 110);
        ctx.font = "14px Arial";
        ctx.fillText("1. Gewinne 3 Kämpfe in Arena 1   (Belohnung: 200g)", 40, 170);
        ctx.fillText("2. Setze 10 Riesen auf das Feld   (Belohnung: 100g)", 40, 220);
        ctx.fillText("3. Zerstöre einen Königsturm       (Belohnung: 300g)", 40, 270);
    } 
    else if (currentScreen === "emotes") {
        ctx.fillStyle = "#FFFFFF"; ctx.font = "bold 24px Arial"; ctx.fillText("DEINE EMOTES", 30, 110);
        ctx.font = "40px Arial";
        ctx.fillText("😂  😡  👍  😭  👑  ⚔", 40, 180);
        ctx.font = "14px Arial"; ctx.fillText("Diese Emojis kannst du aktiv im Kampf benutzen!", 40, 240);
    }
}

// --- INTERACTION LOGIC ---
let combatInstance = null;

window.addEventListener("keydown", (e) => {
    if (currentScreen === "game" && combatInstance) {
        if (e.key === "1") combatInstance.selectedIndex = 0;
        if (e.key === "2") combatInstance.selectedIndex = 1;
        if (e.key === "3") combatInstance.selectedIndex = 2;
        if (e.key === "4") combatInstance.selectedIndex = 3;
        if (combatInstance.gameOver && e.key === " ") {
            combatInstance = null;
            currentScreen = "menu"; // Zurück zum Menü
        }
    }
});

canvas.addEventListener("mousedown", (e) => {
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;

    // --- KLICK LOGIK FÜR DAS MENÜ ---
    if (currentScreen !== "game") {
        // Tab-Bar Wechsel am unteren Rand
        if (my >= 630) {
            let tabIdx = Math.floor(mx / (SCREEN_WIDTH / 4));
            if (tabIdx === 0) currentScreen = "shop";
            if (tabIdx === 1) currentScreen = "quests";
            if (tabIdx === 2) currentScreen = "menu";
            if (tabIdx === 3) currentScreen = "emotes";
            return;
        }

        // Kampf starten im Hauptmenü
        if (currentScreen === "menu" && mx >= 125 && mx <= 325 && my >= 300 && my <= 380) {
            combatInstance = new Game(currentLevel);
            currentScreen = "game";
            return;
        }

        // Shop Kisten öffnen
        if (currentScreen === "shop") {
            // Gratis Truhe öffnen
            if (mx >= 60 && mx <= 180 && my >= 250 && my <= 285) {
                let loot = Math.floor(Math.random() * 50) + 20; gold += loot;
                alert(`Du hast eine Gratis Truhe geöffnet und ${loot} Gold erhalten!`);
            }
            // Magietruhe öffnen
            if (mx >= 260 && mx <= 380 && my >= 250 && my <= 285) {
                if (gold >= 250) {
                    gold -= 250;
                    alert("Du hast eine Magietruhe geöffnet! Epische Karten freigeschaltet!");
                } else { alert("Nicht genug Gold!"); }
            }
        }
        return;
    }

    // --- KLICK LOGIK FÜR DAS LAUFENDE SPIEL ---
    if (currentScreen === "game" && combatInstance) {
        if (combatInstance.gameOver) return;

        // Wenn das Emote Menü offen ist, Klicks prüfen
        if (combatInstance.emoteMenuOpen) {
            if (mx >= 20 && mx <= 170 && my >= 480 && my <= 590) {
                // Emote auswählen anhand Raster-Klick
                let emX = Math.floor((mx - 20) / 50);
                let emY = Math.floor((my - 510) / 40);
                let emotesArr = [["😂","😡","👍"],["😭","👑","⚔"]];
                if(emotesArr[emY] && emotesArr[emY][emX]) {
                    combatInstance.activeEmote = emotesArr[emY][emX];
                    combatInstance.activeEmoteTimer = 90; // Bleibt 1.5s
                }
                combatInstance.emoteMenuOpen = false;
                return;
            }
        }

        // Emote Button anklicken
        if (mx >= 20 && mx <= 60 && my >= 645 && my <= 685) {
            combatInstance.emoteMenuOpen = !combatInstance.emoteMenuOpen;
            return;
        }

        // Deck-Auswahl per Klick
        for (let i = 0; i < combatInstance.cardButtons.length; i++) {
            let btn = combatInstance.cardButtons[i];
            if (mx >= btn.x && mx <= btn.x + btn.w && my >= btn.y && my <= btn.y + btn.h) {
                combatInstance.selectedIndex = i;
                return;
            }
        }

        // Truppen spawnen auf dem Feld
        let activeCard = combatInstance.getSelectedCard();
        combatInstance.spawnUnit(mx, my, "blue", activeCard);
    }
});

// --- ENGINE MAIN LOOP ---
function gameLoop() {
    if (currentScreen === "game" && combatInstance) {
        combatInstance.update();
        combatInstance.drawMap();
        for (let tower of combatInstance.towers) tower.draw();
        for (let unit of combatInstance.units) unit.draw();
        combatInstance.drawUi();
    } else {
        drawMenuScreens();
    }

    requestAnimationFrame(gameLoop);
}

gameLoop();
</script>
</body>
</html>

Game Source: Clash Royale - Deluxe Edition

Creator: NovaGalaxy88

Libraries: none

Complexity: complex (623 lines, 24.1 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: mini-clash-royale-html5-prototyp-novagalaxy88" to link back to the original. Then publish at arcadelab.ai/publish.