🎮ArcadeLab

Shinobi Rising: Ultimate Ninja Combat

by BlazeKoala99
1047 lines47.2 KB
▶ Play
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>Shinobi Rising: Ultimate Ninja Combat</title>
    <style>
        :root {
            --bg-grad-top: #09090b;
            --bg-grad-mid: #121019;
            --bg-grad-bottom: #1c1924;
            --accent-orange: #ff6b00;
            --accent-cyan: #00f0ff;
            --accent-purple: #a855f7;
            --accent-red: #ef4444;
            --panel-bg: rgba(18, 16, 25, 0.88);
            --text-color: #f4f4f5;
            --border-glow: rgba(255, 107, 0, 0.3);
        }

        body {
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
            overflow: hidden;
            background: #09090b;
            user-select: none;
            -webkit-user-select: none;
            color: var(--text-color);
        }

        body::before {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 100vw;
            height: 100vh;
            transform: translate(-50%, -50%);
            background: radial-gradient(circle, rgba(255, 107, 0, 0.08) 0%, rgba(0, 0, 0, 0) 70%);
            pointer-events: none;
            z-index: 1;
        }

        .screen {
            position: absolute;
            width: 100vw;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            z-index: 100;
            transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
            backdrop-filter: blur(16px);
            -webkit-backdrop-filter: blur(16px);
            background: rgba(9, 9, 11, 0.85);
        }

        .hidden {
            opacity: 0 !important;
            pointer-events: none !important;
            transform: scale(0.95);
            display: none !important;
        }

        .glass-panel {
            background: var(--panel-bg);
            border: 1px solid rgba(255, 255, 255, 0.12);
            border-radius: 28px;
            padding: 24px;
            box-shadow: 0 30px 60px rgba(0, 0, 0, 0.8), 0 0 20px var(--border-glow);
            text-align: center;
            max-width: 500px;
            width: 92%;
            box-sizing: border-box;
            max-height: 90vh;
            overflow-y: auto;
            position: relative;
            z-index: 2;
        }

        h1 {
            font-size: 2.2rem;
            font-weight: 900;
            background: linear-gradient(135deg, #ff6b00, #ffa800);
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            margin-bottom: 2px;
            text-transform: uppercase;
            letter-spacing: 2px;
            margin-top: 0;
            filter: drop-shadow(0 0 12px rgba(255, 107, 0, 0.4));
        }

        .rank-badge {
            font-size: 11px;
            font-weight: 800;
            color: #eab308;
            background: rgba(234, 179, 8, 0.15);
            padding: 4px 12px;
            border-radius: 12px;
            border: 1px solid rgba(234, 179, 8, 0.3);
            display: inline-block;
            margin-bottom: 12px;
            text-transform: uppercase;
            letter-spacing: 1px;
        }

        h2 { font-size: 1.5rem; margin-top: 0; margin-bottom: 15px; font-weight: 800; letter-spacing: 1px; }

        .btn {
            padding: 12px 18px;
            font-size: 13px;
            font-weight: 800;
            border: none;
            border-radius: 14px;
            color: white;
            cursor: pointer;
            transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            text-transform: uppercase;
            letter-spacing: 1px;
            width: 100%;
            box-sizing: border-box;
            margin-bottom: 8px;
            position: relative;
            overflow: hidden;
        }
        .btn:hover { transform: translateY(-2px); filter: brightness(1.15); }
        .btn:active { transform: translateY(1px) scale(0.98); }

        .btn-primary { background: linear-gradient(135deg, #ff6b00, #e65100); box-shadow: 0 8px 25px rgba(255, 107, 0, 0.4); }
        .btn-secondary { background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.12); }
        .btn-secondary:hover { background: rgba(255, 255, 255, 0.12); }
        .btn-gold { background: linear-gradient(135deg, #eab308, #ca8a04); box-shadow: 0 8px 20px rgba(234, 179, 8, 0.3); color: #000; }
        .btn-green { background: linear-gradient(135deg, #10b981, #059669); box-shadow: 0 8px 20px rgba(16, 185, 129, 0.3); }

        #loadingScreen { background: #09090b; z-index: 500; display: flex; }
        .loader-container { width: 80%; max-width: 280px; background: rgba(255, 255, 255, 0.08); height: 10px; border-radius: 20px; overflow: hidden; margin-top: 25px; padding: 2px; }
        .loader-bar { width: 0%; height: 100%; background: linear-gradient(90deg, #ff6b00, #00f0ff); border-radius: 20px; transition: width 0.1s linear; box-shadow: 0 0 12px var(--accent-orange); }

        #gameContainer {
            position: relative;
            width: 100vw;
            height: 100vh;
            background: linear-gradient(to bottom, var(--bg-grad-top), var(--bg-grad-mid), var(--bg-grad-bottom));
        }

        #gameCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 5; }

        #hud {
            position: absolute;
            top: max(15px, env(safe-area-inset-top));
            left: 15px; right: 15px;
            display: flex;
            justify-content: space-between;
            pointer-events: none;
            z-index: 50;
        }

        .hud-card {
            background: rgba(18, 16, 25, 0.75);
            padding: 8px 16px;
            border-radius: 16px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            font-weight: 800;
            font-size: 0.85rem;
            backdrop-filter: blur(10px);
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            letter-spacing: 0.5px;
        }
        .stat-val { color: var(--accent-orange); font-weight: 900; }

        .shop-tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 12px; }
        .tab-btn { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.08); color: #a1a1aa; padding: 8px 2px; border-radius: 10px; cursor: pointer; font-weight: 700; font-size: 10px; text-align: center; transition: 0.2s; }
        .tab-btn.active { background: var(--accent-orange); color: #000; font-weight: 900; border-color: var(--accent-orange); box-shadow: 0 4px 15px rgba(255, 107, 0, 0.3); }

        .shop-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; max-height: 260px; overflow-y: auto; padding-right: 4px; margin-bottom: 12px;}
        .shop-item { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: 16px; padding: 10px; display: flex; flex-direction: column; align-items: center; justify-content: space-between; transition: 0.2s; }
        .shop-item:hover { border-color: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.05); }
        .shop-avatar-placeholder { width: 48px; height: 48px; border-radius: 50%; border: 3px solid #3f3f46; margin-bottom: 6px; background: #27272a; box-shadow: 0 4px 10px rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 20px;}
        .shop-item.equipped { border-color: var(--accent-orange); background: rgba(255, 107, 0, 0.08); box-shadow: 0 0 15px rgba(255, 107, 0, 0.2); }
        .shop-item.locked-skin { opacity: 0.6; }
        .shop-item small { font-size: 10px; margin-bottom: 4px; color: #a1a1aa; }
        .shop-item .item-title { font-weight: 800; font-size: 12px; margin-bottom: 2px; }

        .quest-list { max-height: 280px; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; text-align: left; padding-right: 4px; margin-bottom: 12px; }
        .quest-item { background: rgba(255, 255, 255, 0.03); border: 1px solid rgba(255, 255, 255, 0.08); padding: 10px 14px; border-radius: 14px; display: flex; justify-content: space-between; align-items: center; gap: 8px; }
        .quest-item.ready { border-color: #eab308; background: rgba(234, 179, 8, 0.1); }
        .quest-item.claimed { border-color: #10b981; background: rgba(16, 185, 129, 0.05); opacity: 0.6; }
        .quest-reward-tag { font-size: 11px; color: #eab308; font-weight: 800; white-space: nowrap; background: rgba(234, 179, 8, 0.15); padding: 4px 8px; border-radius: 8px;}
        .quest-info { display: flex; flex-direction: column; }
        .quest-label { font-weight: 700; font-size: 12px; }
        .quest-progress { font-size: 11px; color: #a1a1aa; margin-top: 2px; }

        .char-select-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin: 12px 0; width: 100%; max-height: 220px; overflow-y: auto; padding-right: 2px;}
        .char-card { background: rgba(255, 255, 255, 0.03); border: 2px solid rgba(255, 255, 255, 0.08); border-radius: 16px; padding: 10px 6px; cursor: pointer; transition: all 0.2s ease; text-align: center; position: relative;}
        .char-card:hover { transform: translateY(-2px); border-color: rgba(255, 255, 255, 0.2); }
        .char-card.active { border-color: var(--accent-orange); background: rgba(255, 107, 0, 0.1); box-shadow: 0 0 15px rgba(255, 107, 0, 0.25); }
        .char-card.locked { opacity: 0.4; border-style: dashed; }
        .char-card h3 { margin: 6px 0 2px 0; font-size: 0.85rem; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .char-card p { margin: 0; font-size: 0.65rem; color: #a1a1aa; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .char-avatar { width: 40px; height: 40px; border-radius: 50%; background: #27272a; display: inline-flex; align-items: center; justify-content: center; font-size: 18px; border: 2px solid #52525b; box-shadow: 0 4px 10px rgba(0,0,0,0.4); }
        .char-card.active .char-avatar { border-color: var(--accent-orange); }
        .locked-badge { position: absolute; top: 4px; right: 4px; font-size: 8px; background: #ef4444; color: white; padding: 2px 4px; border-radius: 4px; font-weight: 800; }

        .powerup-box { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08); border-radius: 16px; padding: 10px; margin-bottom: 12px; }
        .powerup-item { text-align: left; display: flex; flex-direction: column; justify-content: space-between; }
        .powerup-item span { font-size: 10px; color: #a1a1aa; font-weight: 700; text-transform: uppercase; }

        #combatOverlay {
            position: absolute;
            bottom: 25px; left: 0; width: 100%;
            display: flex; justify-content: center; align-items: center; gap: 15px;
            z-index: 40; padding: 0 15px; box-sizing: border-box;
        }
        .action-circle-btn {
            width: 70px; height: 70px; border-radius: 50%;
            border: 2px solid rgba(255, 255, 255, 0.2);
            background: rgba(18, 16, 25, 0.85); color: white;
            font-weight: 800; font-size: 10px;
            display: flex; flex-direction: column; justify-content: center; align-items: center;
            cursor: pointer; backdrop-filter: blur(8px);
            box-shadow: 0 8px 20px rgba(0,0,0,0.6);
            text-transform: uppercase; transition: all 0.15s;
            position: relative;
        }
        .action-circle-btn:active { transform: scale(0.9); background: var(--accent-orange); color: #000; border-color: white;}
        #jutsuBtn { border-color: var(--accent-orange); width: 85px; height: 85px; background: rgba(255, 107, 0, 0.15); box-shadow: 0 0 20px rgba(255, 107, 0, 0.3); }

        .cooldown-overlay {
            position: absolute; top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.8); border-radius: 50%;
            display: flex; justify-content: center; align-items: center;
            font-size: 20px; color: #ef4444; font-weight: 900;
            opacity: 0; pointer-events: none; transition: opacity 0.2s;
        }
        .cooldown-overlay.active { opacity: 1; }

        #healthBarsContainer {
            position: absolute;
            top: max(65px, env(safe-area-inset-top) + 40px);
            left: 15px; right: 15px;
            display: flex; justify-content: space-between;
            z-index: 50; pointer-events: none;
        }
        .hp-panel {
            background: rgba(18, 16, 25, 0.8);
            padding: 8px 12px; border-radius: 14px;
            width: 45%; border: 1px solid rgba(255, 255, 255, 0.1);
            backdrop-filter: blur(8px); box-shadow: 0 8px 20px rgba(0,0,0,0.4);
        }
        .hp-name { font-size: 11px; font-weight: 800; margin-bottom: 4px; display: flex; justify-content: space-between; text-transform: uppercase; letter-spacing: 0.5px; }
        .hp-bg { width: 100%; height: 8px; background: #27272a; border-radius: 4px; overflow: hidden; }
        .hp-fill { width: 100%; height: 100%; background: linear-gradient(90deg, #10b981, #34d399); transition: width 0.2s ease-out; box-shadow: 0 0 8px rgba(16, 185, 129, 0.5); }
        .hp-fill.enemy { background: linear-gradient(90deg, #ef4444, #f87171); box-shadow: 0 0 8px rgba(239, 68, 68, 0.5); }

        #endGamePanel {
            position: absolute; bottom: 120px; left: 50%; transform: translateX(-50%);
            width: 80%; max-width: 300px; z-index: 60; display: none;
        }

        #toast-container { position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 200; width: 80%; max-width: 300px; pointer-events: none; }
        .toast { background: rgba(16, 185, 129, 0.95); color: white; padding: 10px 16px; border-radius: 12px; text-align: center; font-weight: 800; box-shadow: 0 10px 30px rgba(0,0,0,0.5); margin-bottom: 6px; backdrop-filter: blur(4px); font-size: 12px; }
        .toast.error { background: rgba(239, 68, 68, 0.95); }
        
        #quest-notifier {
            position: absolute; bottom: 20px; left: 50%;
            transform: translateX(-50%) translateY(100px);
            background: rgba(18, 16, 25, 0.95); border: 1px solid #eab308;
            border-radius: 12px; padding: 8px 16px; display: flex;
            align-items: center; gap: 8px; font-size: 12px; font-weight: 800;
            box-shadow: 0 10px 30px rgba(0,0,0,0.8), 0 0 15px rgba(234, 179, 8, 0.3);
            z-index: 150; pointer-events: none; transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }
        #quest-notifier.show { transform: translateX(-50%) translateY(0); }

        .combo-counter {
            position: absolute;
            top: 30%;
            left: 50%;
            transform: translate(-50%, -50%) scale(0);
            font-size: 2.5rem;
            font-weight: 900;
            color: #ff6b00;
            text-shadow: 0 0 20px #ff6b00, 0 0 40px #eab308;
            pointer-events: none;
            z-index: 45;
            transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
        }
        .combo-counter.show { transform: translate(-50%, -50%) scale(1.2); }
    </style>
</head>
<body>

    <div id="loadingScreen" class="screen">
        <h1>Shinobi Rising</h1>
        <p style="color: #a1a1aa; font-size: 12px; font-weight: 600;">Chakra wird gesammelt...</p>
        <div class="loader-container">
            <div id="loaderBar" class="loader-bar"></div>
        </div>
    </div>

    <div id="mainMenu" class="screen hidden">
        <div class="glass-panel">
            <h1>Shinobi Rising</h1>
            <div id="playerRank" class="rank-badge">Anfänger-Ninja</div>
            
            <div style="display: flex; justify-content: space-around; margin-bottom: 10px;">
                <div class="hud-card">LVL: <span id="menuLevel" class="stat-val">1</span></div>
                <div class="hud-card"><span id="menuTotalCoins" class="stat-val">0</span> 🪙</div>
            </div>

            <div class="powerup-box">
                <div class="powerup-item">
                    <span>HP Bonus: +<span id="statHpBonus">0</span></span>
                    <button class="btn btn-green" style="padding: 6px; font-size: 10px; margin-top: 4px; margin-bottom: 0;" onclick="buyUpgrade('hp')">HP +15 (<span id="costHp">200</span>🪙)</button>
                </div>
                <div class="powerup-item">
                    <span>ATK Bonus: +<span id="statAtkBonus">0</span></span>
                    <button class="btn btn-green" style="padding: 6px; font-size: 10px; margin-top: 4px; margin-bottom: 0;" onclick="buyUpgrade('atk')">ATK +3 (<span id="costAtk">200</span>🪙)</button>
                </div>
            </div>

            <h3 style="margin: 5px 0; font-size: 12px; text-transform: uppercase; color: #a1a1aa;">Wähle deinen Ninja</h3>
            <div id="charSelectContainer" class="char-select-container"></div>

            <button class="btn btn-primary" onclick="startBotFight()">Kampf Starten ⚔️</button>
            <div style="display: flex; gap: 8px;">
                <button class="btn btn-secondary" onclick="openScreen('shopScreen')">Shop 🛒</button>
                <button class="btn btn-secondary" onclick="openScreen('questScreen')">Quests 📜</button>
            </div>
        </div>
    </div>

    <div id="shopScreen" class="screen hidden">
        <div class="glass-panel">
            <h2>Shinobi Shop</h2>
            <p style="margin-top: -8px; color: #a1a1aa; margin-bottom: 12px; font-size: 12px;">Deine Coins: <span id="shopCoins" class="stat-val">0</span> 🪙</p>
            
            <div id="shopTabsContainer" class="shop-tabs"></div>
            <div id="shopGrid" class="shop-grid"></div>
            <button class="btn btn-secondary" onclick="openScreen('mainMenu')">Zurück</button>
        </div>
    </div>

    <div id="questScreen" class="screen hidden">
        <div class="glass-panel">
            <h2>Missions-Zentrale</h2>
            <p style="margin-top: -8px; color: #a1a1aa; margin-bottom: 12px; font-size: 12px;">Erfülle Missionen für fette Belohnungen</p>
            <div id="questList" class="quest-list"></div>
            <button class="btn btn-secondary" onclick="openScreen('mainMenu')">Zurück</button>
        </div>
    </div>

    <div id="gameContainer" style="display: none;">
        <canvas id="gameCanvas"></canvas>

        <div id="hud">
            <div class="hud-card">Gegner: <span id="hudEnemyName" style="color: #ef4444;">Gegner</span></div>
            <div class="hud-card"><span id="hudCoins">0</span> 🪙</div>
        </div>

        <div id="healthBarsContainer">
            <div class="hp-panel">
                <div class="hp-name"><span>Du</span><span id="playerHpTxt">100/100</span></div>
                <div class="hp-bg"><div id="playerHpBar" class="hp-fill"></div></div>
            </div>
            <div class="hp-panel">
                <div class="hp-name"><span id="enemyHpName">Gegner</span><span id="enemyHpTxt">100/100</span></div>
                <div class="hp-bg"><div id="enemyHpBar" class="hp-fill enemy"></div></div>
            </div>
        </div>

        <div id="comboCounter" class="combo-counter">CRITICAL!</div>

        <div id="endGamePanel">
            <button class="btn btn-gold" onclick="goToHome()">🏠 Hauptmenü</button>
        </div>

        <div id="combatOverlay">
            <button id="atkBtn" class="action-circle-btn" onclick="playerAction('attack')">⚔️<br>Angriff</button>
            <button id="jutsuBtn" class="action-circle-btn" onclick="playerAction('jutsu')">
                🔥<br>Jutsu
                <div id="jutsuCDOverlay" class="cooldown-overlay">3</div>
            </button>
            <button id="fleeBtn" class="action-circle-btn" onclick="playerAction('flee')">💨<br>Flucht</button>
        </div>
    </div>

    <div id="toast-container"></div>
    <div id="quest-notifier">🏆 <span id="quest-notifier-text">Quest Abgeschlossen!</span></div>

    <script>
        // State Management
        const state = {
            coins: 100,
            level: 1,
            xp: 0,
            selectedCharacter: 'naruto',
            upgrades: { hp: 0, atk: 0 },
            equippedSkins: {
                naruto: 'skin_naruto_base',
                sasuke: 'skin_sasuke_base',
                kakashi: 'skin_kakashi_base',
                itachi: 'skin_itachi_base',
                madara: 'skin_madara_base',
                pain: 'skin_pain_base',
                minato: 'skin_minato_base',
                gaara: 'skin_gaara_base',
                jiraiya: 'skin_jiraiya_base'
            },
            unlockedSkins: ['skin_naruto_base'],
            quests: [
                { id: 'q1', type: 'bot_kills', label: 'Besiege 3 Gegner', progress: 0, target: 3, reward: 100, claimed: false },
                { id: 'q2', type: 'jutsu_uses', label: 'Setze 5 Spezial-Jutsus ein', progress: 0, target: 5, reward: 75, claimed: false },
                { id: 'q3', type: 'perfect_win', label: 'Gewinne 2 Kämpfe', progress: 0, target: 2, reward: 120, claimed: false },
                { id: 'q4', type: 'earn_coins', label: 'Sammle 200 Coins', progress: 0, target: 200, reward: 150, claimed: false },
                { id: 'q5', type: 'upgrades_made', label: 'Kaufe 3 Upgrades', progress: 0, target: 3, reward: 200, claimed: false },
                { id: 'q6', type: 'boss_fight', label: 'Besiege einen Boss', progress: 0, target: 1, reward: 300, claimed: false }
            ]
        };

        const skinsData = {
            naruto: [
                { id: 'skin_naruto_base', name: 'Naruto (Genin)', cost: 0, color: '#ff6b00', secondary: '#00f0ff', jutsu: 'Rasengan', icon: '🍥', power: 1.0 },
                { id: 'skin_naruto_shippuden', name: 'Naruto (Shippuden)', cost: 800, color: '#ea580c', secondary: '#18181b', jutsu: 'Oodama Rasengan', icon: '🌀', power: 1.4 },
                { id: 'skin_naruto_hokage', name: 'Hokage Naruto', cost: 2500, color: '#fef08a', secondary: '#ffffff', jutsu: 'Rasen-Shuriken', icon: '🦊', power: 2.0 }
            ],
            sasuke: [
                { id: 'skin_sasuke_base', name: 'Sasuke (Genin)', cost: 500, color: '#312e81', secondary: '#818cf8', jutsu: 'Chidori', icon: '⚡', power: 1.2 },
                { id: 'skin_sasuke_hebi', name: 'Sasuke (Hebi)', cost: 1500, color: '#1e1b4b', secondary: '#a855f7', jutsu: 'Kirin', icon: '🐉', power: 1.6 }
            ],
            kakashi: [
                { id: 'skin_kakashi_base', name: 'Kakashi Hatake', cost: 1200, color: '#64748b', secondary: '#00f0ff', jutsu: 'Raikiri', icon: '⚡', power: 1.5 },
                { id: 'skin_kakashi_hokage', name: 'Hokage Kakashi', cost: 3000, color: '#334155', secondary: '#eab308', jutsu: 'Kamui Lightning', icon: '👁️', power: 2.2 }
            ],
            itachi: [
                { id: 'skin_itachi_base', name: 'Itachi Uchiha', cost: 2000, color: '#ef4444', secondary: '#000000', jutsu: 'Tsukuyomi', icon: '👁️', power: 1.8 },
                { id: 'skin_itachi_susanoo', name: 'Itachi (Susanoo)', cost: 4500, color: '#991b1b', secondary: '#f87171', jutsu: 'Amaterasu & Susanoo', icon: '🔥', power: 2.5 }
            ],
            madara: [
                { id: 'skin_madara_base', name: 'Madara Uchiha', cost: 3500, color: '#7f1d1d', secondary: '#a855f7', jutsu: 'Tengai Shinsei', icon: '☄️', power: 2.3 }
            ],
            pain: [
                { id: 'skin_pain_base', name: 'Pain (Tendo)', cost: 2800, color: '#f97316', secondary: '#3f3f46', jutsu: 'Shinra Tensei', icon: '💥', power: 2.1 }
            ],
            minato: [
                { id: 'skin_minato_base', name: 'Minato Namikaze', cost: 2200, color: '#eab308', secondary: '#ffffff', jutsu: 'Fliegender Raijin', icon: '⚡', power: 1.9 }
            ],
            gaara: [
                { id: 'skin_gaara_base', name: 'Gaara', cost: 1000, color: '#b45309', secondary: '#ef4444', jutsu: 'Sand-Sarg', icon: '⏳', power: 1.4 }
            ],
            jiraiya: [
                { id: 'skin_jiraiya_base', name: 'Jiraiya (Eremit)', cost: 1800, color: '#15803d', secondary: '#ffffff', jutsu: 'Kröten-Sumpf', icon: '🐸', power: 1.7 }
            ]
        };

        const botsData = [
            { name: 'Übungs-Klon', hp: 70, atk: 5, color: '#94a3b8', xp: 30, coins: 15, isBoss: false },
            { name: 'Sound Ninja', hp: 100, atk: 8, color: '#10b981', xp: 50, coins: 25, isBoss: false },
            { name: 'Akatsuki Spion', hp: 140, atk: 12, color: '#ef4444', xp: 80, coins: 45, isBoss: false },
            { name: 'Orochimaru', hp: 200, atk: 16, color: '#8b5cf6', xp: 140, coins: 80, isBoss: true },
            { name: 'Madara Uchiha (Boss)', hp: 300, atk: 22, color: '#dc2626', xp: 250, coins: 150, isBoss: true }
        ];

        let canvas, ctx;
        let currentScreen = 'mainMenu';
        let activeShopTab = 'naruto';

        let combat = { 
            active: false, 
            playerHP: 100, playerMaxHP: 100, 
            enemyHP: 100, enemyMaxHP: 100, 
            enemyData: null, 
            jutsuCD: 0, 
            particles: [], 
            visualEffects: [], 
            shake: 0, 
            timer: 0,
            playerAnim: { x: 0, y: 0, attack: 0 },
            enemyAnim: { x: 0, y: 0, attack: 0 }
        };

        window.addEventListener('DOMContentLoaded', () => {
            canvas = document.getElementById('gameCanvas');
            ctx = canvas.getContext('2d');
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);
            loadSavedData();
            updateUI();

            let prog = 0;
            const loader = setInterval(() => {
                prog += 15;
                document.getElementById('loaderBar').style.width = prog + '%';
                if(prog >= 100) {
                    clearInterval(loader);
                    document.getElementById('loadingScreen').classList.add('hidden');
                    document.getElementById('mainMenu').classList.remove('hidden');
                    requestAnimationFrame(gameLoop);
                }
            }, 40);
        });

        function resizeCanvas() { 
            if(canvas) {
                canvas.width = window.innerWidth; 
                canvas.height = window.innerHeight; 
            }
        }

        function saveData() { localStorage.setItem('shinobi_save', JSON.stringify(state)); }
        function loadSavedData() {
            const saved = localStorage.getItem('shinobi_save');
            if(saved) {
                try { Object.assign(state, JSON.parse(saved)); } catch(e){}
            }
        }

        function getRankName(lvl) {
            if(lvl < 3) return "Anfänger-Ninja (Genin)";
            if(lvl < 7) return "Missions-Chunin";
            if(lvl < 12) return "Anbu-Spezialeinheit";
            if(lvl < 20) return "S-Rang Jounin";
            return "Legendärer Hokage";
        }

        function updateUI() {
            document.getElementById('menuLevel').innerText = state.level;
            document.getElementById('menuTotalCoins').innerText = state.coins;
            document.getElementById('shopCoins').innerText = state.coins;
            document.getElementById('hudCoins').innerText = state.coins;
            document.getElementById('playerRank').innerText = getRankName(state.level);

            document.getElementById('statHpBonus').innerText = state.upgrades.hp * 15;
            document.getElementById('statAtkBonus').innerText = state.upgrades.atk * 3;
            document.getElementById('costHp').innerText = (state.upgrades.hp + 1) * 200;
            document.getElementById('costAtk').innerText = (state.upgrades.atk + 1) * 200;

            renderCharSelect();
        }

        function renderCharSelect() {
            const container = document.getElementById('charSelectContainer');
            container.innerHTML = '';

            Object.keys(skinsData).forEach(charKey => {
                const baseSkin = skinsData[charKey][0];
                const isUnlocked = state.unlockedSkins.includes(baseSkin.id) || charKey === 'naruto';
                const card = document.createElement('div');
                card.className = `char-card ${state.selectedCharacter === charKey ? 'active' : ''} ${!isUnlocked ? 'locked' : ''}`;
                card.onclick = () => selectCharacter(charKey);

                card.innerHTML = `
                    ${!isUnlocked ? '<div class="locked-badge">🔒 Shop</div>' : ''}
                    <div class="char-avatar" style="border-color: ${baseSkin.color}; color: white;">${baseSkin.icon}</div>
                    <h3>${charKey.toUpperCase()}</h3>
                    <p>${baseSkin.jutsu}</p>
                `;
                container.appendChild(card);
            });
        }

        function buyUpgrade(type) {
            const cost = (state.upgrades[type] + 1) * 200;
            if (state.coins >= cost) {
                state.coins -= cost;
                state.upgrades[type]++;
                showToast("Erfolgreich geupgradet!");
                triggerQuestProgress('upgrades_made', 1);
                updateUI();
                saveData();
            } else {
                showToast("Nicht genug Coins!", true);
            }
        }

        function openScreen(screenId) {
            document.getElementById('mainMenu').classList.add('hidden');
            document.getElementById('shopScreen').classList.add('hidden');
            document.getElementById('questScreen').classList.add('hidden');
            document.getElementById('gameContainer').style.display = 'none';
            
            if (screenId === 'game') {
                document.getElementById('gameContainer').style.display = 'block';
                currentScreen = 'game';
            } else {
                document.getElementById(screenId).classList.remove('hidden');
                currentScreen = screenId;
            }

            if(screenId === 'shopScreen') {
                renderShopTabs();
                renderShop();
            }
            if(screenId === 'questScreen') renderQuests();
            if(screenId === 'mainMenu') updateUI();
        }

        function selectCharacter(char) {
            const baseSkinId = skinsData[char][0].id;
            if (char !== 'naruto' && !state.unlockedSkins.includes(baseSkinId)) {
                showToast("Schalte diesen Ninja im Shop frei!", true);
                return;
            }
            state.selectedCharacter = char;
            renderCharSelect();
            saveData();
        }

        function renderShopTabs() {
            const container = document.getElementById('shopTabsContainer');
            container.innerHTML = '';
            Object.keys(skinsData).forEach(charKey => {
                const btn = document.createElement('button');
                btn.className = `tab-btn ${activeShopTab === charKey ? 'active' : ''}`;
                btn.innerText = charKey.toUpperCase();
                btn.onclick = () => switchShopTab(charKey);
                container.appendChild(btn);
            });
        }

        function switchShopTab(tab) {
            activeShopTab = tab;
            renderShopTabs();
            renderShop();
        }

        function getCurrentSkin(charKey) {
            const equippedId = state.equippedSkins[charKey];
            const list = skinsData[charKey] || [];
            return list.find(s => s.id === equippedId) || list[0];
        }

        function renderShop() {
            const grid = document.getElementById('shopGrid');
            grid.innerHTML = '';

            const skins = skinsData[activeShopTab] || [];
            skins.forEach(skin => {
                const isUnlocked = state.unlockedSkins.includes(skin.id) || skin.cost === 0;
                const isEquipped = state.equippedSkins[activeShopTab] === skin.id;

                const card = document.createElement('div');
                card.className = `shop-item ${isEquipped ? 'equipped' : ''} ${!isUnlocked ? 'locked-skin' : ''}`;

                let btnHtml = '';
                if (isEquipped) {
                    btnHtml = `<button class="btn btn-gold" disabled style="padding: 6px; font-size: 10px; margin: 0;">Ausrüstet</button>`;
                } else if (isUnlocked) {
                    btnHtml = `<button class="btn btn-green" onclick="equipSkin('${activeShopTab}', '${skin.id}')" style="padding: 6px; font-size: 10px; margin: 0;">Ausrüsten</button>`;
                } else {
                    btnHtml = `<button class="btn btn-primary" onclick="buySkin('${skin.id}', ${skin.cost})" style="padding: 6px; font-size: 10px; margin: 0;">Kaufen (${skin.cost}🪙)</button>`;
                }

                card.innerHTML = `
                    <div class="shop-avatar-placeholder" style="border-color: ${skin.color};">${skin.icon}</div>
                    <div class="item-title">${skin.name}</div>
                    <small>Power: x${skin.power.toFixed(1)} | ${skin.jutsu}</small>
                    ${btnHtml}
                `;
                grid.appendChild(card);
            });
        }

        function buySkin(skinId, cost) {
            if (state.coins >= cost) {
                state.coins -= cost;
                state.unlockedSkins.push(skinId);
                showToast("Skin freigeschaltet!");
                
                // Automatisches Ausrüsten beim Kauf
                state.equippedSkins[activeShopTab] = skinId;
                if (!state.unlockedSkins.includes(skinsData[activeShopTab][0].id)) {
                    state.unlockedSkins.push(skinsData[activeShopTab][0].id);
                }
                
                updateUI();
                renderShop();
                saveData();
            } else {
                showToast("Nicht genug Coins!", true);
            }
        }

        function equipSkin(charKey, skinId) {
            state.equippedSkins[charKey] = skinId;
            showToast("Skin ausgerüstet!");
            renderShop();
            saveData();
        }

        function renderQuests() {
            const list = document.getElementById('questList');
            list.innerHTML = '';

            state.quests.forEach(q => {
                const item = document.createElement('div');
                const isReady = q.progress >= q.target && !q.claimed;
                item.className = `quest-item ${isReady ? 'ready' : ''} ${q.claimed ? 'claimed' : ''}`;

                let btn = '';
                if (q.claimed) {
                    btn = `<span style="font-size: 10px; color: #10b981; font-weight: 800;">Abgeholt ✓</span>`;
                } else if (isReady) {
                    btn = `<button class="btn btn-gold" onclick="claimQuest('${q.id}')" style="padding: 6px 12px; font-size: 10px; margin: 0; width: auto;">Einlösen</button>`;
                } else {
                    btn = `<span class="quest-reward-tag">+${q.reward} 🪙</span>`;
                }

                item.innerHTML = `
                    <div class="quest-info">
                        <span class="quest-label">${q.label}</span>
                        <span class="quest-progress">${Math.min(q.progress, q.target)} / ${q.target}</span>
                    </div>
                    ${btn}
                `;
                list.appendChild(item);
            });
        }

        function triggerQuestProgress(type, amount = 1) {
            state.quests.forEach(q => {
                if (q.type === type && !q.claimed) {
                    q.progress += amount;
                    if (q.progress >= q.target) {
                        showQuestNotification(q.label);
                    }
                }
            });
            saveData();
        }

        function claimQuest(id) {
            const q = state.quests.find(x => x.id === id);
            if (q && q.progress >= q.target && !q.claimed) {
                q.claimed = true;
                state.coins += q.reward;
                showToast(`+${q.reward} Coins erhalten!`);
                updateUI();
                renderQuests();
                saveData();
            }
        }

        function showQuestNotification(text) {
            const notif = document.getElementById('quest-notifier');
            document.getElementById('quest-notifier-text').innerText = text;
            notif.classList.add('show');
            setTimeout(() => notif.classList.remove('show'), 3000);
        }

        function showToast(text, isError = false) {
            const container = document.getElementById('toast-container');
            const toast = document.createElement('div');
            toast.className = `toast ${isError ? 'error' : ''}`;
            toast.innerText = text;
            container.appendChild(toast);
            setTimeout(() => toast.remove(), 2500);
        }

        // KAMPF SYSTEM
        function startBotFight() {
            const currentSkin = getCurrentSkin(state.selectedCharacter);
            
            // Zufälliger Gegner passend zum Level
            let botIndex = Math.min(Math.floor(Math.random() * (state.level + 1)), botsData.length - 1);
            let bot = { ...botsData[botIndex] };

            combat.active = true;
            combat.playerMaxHP = 100 + (state.upgrades.hp * 15);
            combat.playerHP = combat.playerMaxHP;
            combat.enemyData = bot;
            combat.enemyMaxHP = bot.hp;
            combat.enemyHP = bot.hp;
            combat.jutsuCD = 0;
            combat.particles = [];
            combat.visualEffects = [];

            document.getElementById('hudEnemyName').innerText = bot.name;
            document.getElementById('enemyHpName').innerText = bot.name;
            document.getElementById('endGamePanel').style.display = 'none';
            document.getElementById('combatOverlay').style.display = 'flex';

            updateCombatHP();
            openScreen('game');
        }

        function updateCombatHP() {
            const pPct = Math.max(0, (combat.playerHP / combat.playerMaxHP) * 100);
            const ePct = Math.max(0, (combat.enemyHP / combat.enemyMaxHP) * 100);

            document.getElementById('playerHpBar').style.width = pPct + '%';
            document.getElementById('enemyHpBar').style.width = ePct + '%';
            document.getElementById('playerHpTxt').innerText = `${Math.ceil(combat.playerHP)}/${combat.playerMaxHP}`;
            document.getElementById('enemyHpTxt').innerText = `${Math.ceil(combat.enemyHP)}/${combat.enemyMaxHP}`;
        }

        function playerAction(type) {
            if (!combat.active || combat.playerHP <= 0 || combat.enemyHP <= 0) return;

            const skin = getCurrentSkin(state.selectedCharacter);
            let baseAtk = 12 + (state.upgrades.atk * 3);

            if (type === 'attack') {
                let damage = Math.round((baseAtk * skin.power) * (0.8 + Math.random() * 0.4));
                combat.enemyHP -= damage;
                combat.playerAnim.attack = 10;
                combat.shake = 5;
                spawnParticles(canvas.width * 0.7, canvas.height * 0.5, skin.color, 15);
                spawnDamageText(canvas.width * 0.7, canvas.height * 0.4, damage, '#ffffff');

                if (combat.jutsuCD > 0) combat.jutsuCD--;
            } 
            else if (type === 'jutsu') {
                if (combat.jutsuCD > 0) {
                    showToast("Jutsu lädt noch auf!", true);
                    return;
                }
                let damage = Math.round((baseAtk * skin.power * 2.2) * (0.9 + Math.random() * 0.3));
                combat.enemyHP -= damage;
                combat.jutsuCD = 3;
                combat.playerAnim.attack = 20;
                combat.shake = 15;
                
                spawnParticles(canvas.width * 0.7, canvas.height * 0.5, skin.secondary, 35);
                spawnDamageText(canvas.width * 0.7, canvas.height * 0.35, `${skin.jutsu}! -${damage}`, skin.secondary);
                triggerQuestProgress('jutsu_uses', 1);
            }
            else if (type === 'flee') {
                showToast("Geflohen!");
                goToHome();
                return;
            }

            // Cooldown Overlay Update
            const cdOverlay = document.getElementById('jutsuCDOverlay');
            if (combat.jutsuCD > 0) {
                cdOverlay.classList.add('active');
                cdOverlay.innerText = combat.jutsuCD;
            } else {
                cdOverlay.classList.remove('active');
            }

            updateCombatHP();

            // Prüfen ob Gegner besiegt
            if (combat.enemyHP <= 0) {
                winBattle();
                return;
            }

            // Gegner schlägt zurück
            setTimeout(enemyTurn, 600);
        }

        function enemyTurn() {
            if (!combat.active || combat.enemyHP <= 0) return;

            let damage = Math.round(combat.enemyData.atk * (0.8 + Math.random() * 0.4));
            combat.playerHP -= damage;
            combat.enemyAnim.attack = 10;
            combat.shake = 8;

            spawnParticles(canvas.width * 0.3, canvas.height * 0.5, combat.enemyData.color, 15);
            spawnDamageText(canvas.width * 0.3, canvas.height * 0.4, damage, '#ef4444');
            updateCombatHP();

            if (combat.playerHP <= 0) {
                loseBattle();
            }
        }

        function winBattle() {
            combat.active = false;
            const rewardCoins = combat.enemyData.coins;
            const rewardXp = combat.enemyData.xp;

            state.coins += rewardCoins;
            state.xp += rewardXp;

            // Level Up Berechnung
            if (state.xp >= state.level * 100) {
                state.level++;
                showToast(`LEVEL UP! Du bist jetzt Level ${state.level}!`);
            }

            triggerQuestProgress('bot_kills', 1);
            triggerQuestProgress('earn_coins', rewardCoins);
            triggerQuestProgress('perfect_win', 1);
            if (combat.enemyData.isBoss) triggerQuestProgress('boss_fight', 1);

            showToast(`Sieg! +${rewardCoins} Coins | +${rewardXp} XP`);
            document.getElementById('endGamePanel').style.display = 'block';
            document.getElementById('combatOverlay').style.display = 'none';
            saveData();
        }

        function loseBattle() {
            combat.active = false;
            showToast("Niederlage! Trainiere weiter.", true);
            document.getElementById('endGamePanel').style.display = 'block';
            document.getElementById('combatOverlay').style.display = 'none';
        }

        function goToHome() {
            combat.active = false;
            openScreen('mainMenu');
        }

        function spawnParticles(x, y, color, amount) {
            for (let i = 0; i < amount; i++) {
                combat.particles.push({
                    x: x, y: y,
                    vx: (Math.random() - 0.5) * 12,
                    vy: (Math.random() - 0.5) * 12,
                    size: Math.random() * 6 + 2,
                    color: color,
                    life: 1.0
                });
            }
        }

        function spawnDamageText(x, y, text, color) {
            combat.visualEffects.push({
                x: x, y: y,
                text: text,
                color: color,
                life: 1.0,
                vy: -2
            });
        }

        // MAIN GAME LOOP (RENDER & ANIMATIONS)
        function gameLoop() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            if (currentScreen === 'game') {
                combat.timer += 0.05;

                // Screen Shake Anwenden
                ctx.save();
                if (combat.shake > 0) {
                    let rx = (Math.random() - 0.5) * combat.shake;
                    let ry = (Math.random() - 0.5) * combat.shake;
                    ctx.translate(rx, ry);
                    combat.shake *= 0.85;
                    if (combat.shake < 0.5) combat.shake = 0;
                }

                // Kampf-Arena Hintergrund zeichnen
                let grad = ctx.createLinearGradient(0, 0, 0, canvas.height);
                grad.addColorStop(0, '#09090b');
                grad.addColorStop(0.7, '#1a102f');
                grad.addColorStop(1, '#050508');
                ctx.fillStyle = grad;
                ctx.fillRect(0, 0, canvas.width, canvas.height);

                // Arena-Boden
                ctx.fillStyle = 'rgba(255,255,255,0.03)';
                ctx.beginPath();
                ctx.ellipse(canvas.width/2, canvas.height * 0.7, canvas.width * 0.4, 80, 0, 0, Math.PI * 2);
                ctx.fill();

                // ANIMATION POSITIONEN
                let pSkin = getCurrentSkin(state.selectedCharacter);
                let px = canvas.width * 0.25 + (combat.playerAnim.attack > 0 ? 30 : 0);
                let py = canvas.height * 0.65 + Math.sin(combat.timer * 2) * 5;

                let ex = canvas.width * 0.75 - (combat.enemyAnim.attack > 0 ? 30 : 0);
                let ey = canvas.height * 0.65 + Math.sin(combat.timer * 2 + 1) * 5;

                if (combat.playerAnim.attack > 0) combat.playerAnim.attack--;
                if (combat.enemyAnim.attack > 0) combat.enemyAnim.attack--;

                // SPIELER ZEICHNEN
                ctx.shadowBlur = 20;
                ctx.shadowColor = pSkin.color;
                ctx.fillStyle = pSkin.color;
                ctx.beginPath();
                ctx.arc(px, py, 35, 0, Math.PI * 2);
                ctx.fill();

                // Aura / Innenschicht
                ctx.fillStyle = pSkin.secondary;
                ctx.beginPath();
                ctx.arc(px, py, 15, 0, Math.PI * 2);
                ctx.fill();

                // GEGNER ZEICHNEN
                if (combat.enemyData) {
                    ctx.shadowColor = combat.enemyData.color;
                    ctx.fillStyle = combat.enemyData.color;
                    ctx.beginPath();
                    ctx.arc(ex, ey, 35, 0, Math.PI * 2);
                    ctx.fill();
                }
                ctx.shadowBlur = 0;

                // PARTIKEL RENDERN
                for (let i = combat.particles.length - 1; i >= 0; i--) {
                    let p = combat.particles[i];
                    p.x += p.vx;
                    p.y += p.vy;
                    p.life -= 0.03;

                    if (p.life <= 0) {
                        combat.particles.splice(i, 1);
                        continue;
                    }

                    ctx.fillStyle = p.color;
                    ctx.globalAlpha = p.life;
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.globalAlpha = 1.0;
                }

                // SCHADENS-TEXTE RENDERN
                for (let i = combat.visualEffects.length - 1; i >= 0; i--) {
                    let fx = combat.visualEffects[i];
                    fx.y += fx.vy;
                    fx.life -= 0.02;

                    if (fx.life <= 0) {
                        combat.visualEffects.splice(i, 1);
                        continue;
                    }

                    ctx.font = '900 20px Segoe UI, sans-serif';
                    ctx.fillStyle = fx.color;
                    ctx.globalAlpha = fx.life;
                    ctx.textAlign = 'center';
                    ctx.fillText(fx.text, fx.x, fx.y);
                    ctx.globalAlpha = 1.0;
                }

                ctx.restore();
            }

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

Game Source: Shinobi Rising: Ultimate Ninja Combat

Creator: BlazeKoala99

Libraries: none

Complexity: complex (1047 lines, 47.2 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: naruto-shinobi-rising-blazekoala99" to link back to the original. Then publish at arcadelab.ai/publish.