🎮ArcadeLab

Naruto: Shinobi Rising

by NovaGalaxy88
1030 lines45.6 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>Naruto: Shinobi Rising</title>
    <style>
        :root {
            --bg-grad-top: #0c0a09;
            --bg-grad-mid: #1c1917;
            --bg-grad-bottom: #292524;
            --ground-color: #44403c;
            --ground-border: #f97316;
            --accent-color: #f97316;
            --panel-bg: rgba(28, 25, 23, 0.75);
            --text-color: #fafaf9;
        }

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

        .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 ease-in-out;
            backdrop-filter: blur(12px);
            -webkit-backdrop-filter: blur(12px);
            background: rgba(12, 10, 9, 0.85);
        }

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

        .glass-panel {
            background: var(--panel-bg);
            border: 1px solid rgba(255, 255, 255, 0.08);
            border-radius: 24px;
            padding: 20px;
            box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.7);
            text-align: center;
            max-width: 500px;
            width: 94%;
            box-sizing: border-box;
            max-height: 95vh;
            overflow-y: auto;
        }

        h1 { font-size: 2.1rem; color: var(--accent-color); margin-bottom: 5px; text-transform: uppercase; letter-spacing: 1px; margin-top: 0; text-shadow: 0 0 10px rgba(249, 115, 22, 0.3); }
        h2 { font-size: 1.6rem; margin-top: 0; margin-bottom: 10px;}

        .btn {
            padding: 12px 18px;
            font-size: 14px;
            font-weight: 700;
            border: none;
            border-radius: 14px;
            color: white;
            cursor: pointer;
            transition: transform 0.1s, box-shadow 0.2s;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            width: 100%;
            box-sizing: border-box;
            margin-bottom: 8px;
        }
        .btn:active { transform: scale(0.96); }
        .btn:disabled { background: #57534e !important; color: #a8a29e !important; cursor: not-allowed; box-shadow: none; }

        .btn-primary { background: linear-gradient(135deg, #f97316, #ea580c); box-shadow: 0 4px 14px rgba(249, 115, 22, 0.4); }
        .btn-secondary { background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.08); }
        .btn-danger { background: linear-gradient(135deg, #ef4444, #b91c1c); }
        .btn-gold { background: linear-gradient(135deg, #eab308, #ca8a04); box-shadow: 0 4px 14px rgba(234, 179, 8, 0.4); }
        .btn-green { background: linear-gradient(135deg, #10b981, #059669); box-shadow: 0 4px 14px rgba(16, 185, 129, 0.4); }

        #loadingScreen { background: #0c0a09; z-index: 500; display: flex; }
        .loader-container { width: 80%; max-width: 300px; background: rgba(255, 255, 255, 0.05); height: 8px; border-radius: 4px; overflow: hidden; margin-top: 20px; }
        .loader-bar { width: 0%; height: 100%; background: var(--accent-color); transition: width 0.1s linear; }

        #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(20px, env(safe-area-inset-top));
            left: 20px;
            right: 20px;
            display: flex;
            justify-content: space-between;
            pointer-events: none;
            z-index: 50;
        }

        .hud-card {
            background: rgba(12, 10, 9, 0.7);
            padding: 10px 18px;
            border-radius: 14px;
            border: 1px solid rgba(255, 255, 255, 0.05);
            font-weight: 600;
            font-size: 1rem;
            backdrop-filter: blur(8px);
        }
        .stat-val { color: var(--accent-color); font-weight: bold; }

        .shop-tabs { display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px; margin-bottom: 15px; }
        .tab-btn { background: rgba(255, 255, 255, 0.05); border: none; color: #a8a29e; padding: 10px 2px; border-radius: 10px; cursor: pointer; font-weight: 600; font-size: 11px; text-align: center; }
        .tab-btn.active { background: var(--accent-color); color: #0c0a09; }

        .shop-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; max-height: 280px; overflow-y: auto; padding-right: 4px; margin-bottom: 15px;}
        .shop-item { background: rgba(255, 255, 255, 0.02); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 14px; padding: 12px; display: flex; flex-direction: column; align-items: center; justify-content: space-between; }
        .shop-avatar-placeholder { width: 50px; height: 50px; border-radius: 50%; border: 2px solid #44403c; margin-bottom: 5px; background: #292524;}
        .shop-item.equipped { border-color: var(--accent-color); background: rgba(249, 115, 22, 0.05); }
        .shop-item.locked-skin { opacity: 0.4; }
        .shop-item small { font-size: 11px; margin-bottom: 6px; color: #a8a29e; }
        .shop-item .item-title { font-weight: bold; font-size: 13px; margin-bottom: 2px; }

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

        .btn-quest-claim { padding: 6px 12px; font-size: 11px; border-radius: 8px; font-weight: bold; width: auto; text-transform: none; margin-bottom: 0; }

        #quest-notifier {
            position: absolute;
            bottom: 25px;
            left: 50%;
            transform: translateX(-50%) translateY(100px);
            background: rgba(28, 25, 23, 0.95);
            border: 1px solid #eab308;
            border-radius: 10px;
            padding: 8px 16px;
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 13px;
            font-weight: 600;
            box-shadow: 0 4px 15px rgba(0,0,0,0.6);
            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); }

        #toast-container { position: absolute; top: 40px; 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: 12px 20px; border-radius: 12px; text-align: center; font-weight: 600; box-shadow: 0 10px 25px -5px rgba(0,0,0,0.5); margin-bottom: 8px; }

        .char-select-container { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; margin: 15px 0; width: 100%; }
        .char-card { background: rgba(255, 255, 255, 0.03); border: 2px solid #44403c; border-radius: 16px; padding: 12px 8px; cursor: pointer; transition: all 0.2s; text-align: center; position: relative;}
        .char-card.active { border-color: var(--accent-color); background: rgba(249, 115, 22, 0.08); }
        .char-card.locked { opacity: 0.5; border-style: dashed; }
        .char-card h3 { margin: 5px 0 2px 0; font-size: 1rem; }
        .char-card p { margin: 0; font-size: 0.75rem; color: #a8a29e; }
        .char-avatar { width: 50px; height: 50px; border-radius: 50%; background: #292524; display: inline-block; object-fit: cover; border: 2px solid #57534e; }
        .char-card.active .char-avatar { border-color: var(--accent-color); }
        .locked-badge { position: absolute; top: 5px; right: 5px; font-size: 10px; background: #ef4444; color: white; padding: 1px 4px; border-radius: 4px; font-weight: bold; }

        .powerup-box {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
            background: rgba(255,255,255,0.03);
            border: 1px solid rgba(255,255,255,0.06);
            border-radius: 16px;
            padding: 12px;
            margin-bottom: 15px;
        }
        .powerup-item { text-align: left; display: flex; flex-direction: column; justify-content: space-between; }
        .powerup-item span { font-size: 12px; color: #a8a29e; font-weight: 600; }
        .powerup-item b { font-size: 14px; color: #fafaf9; }

        #combatOverlay {
            position: absolute;
            bottom: 30px;
            left: 0;
            width: 100%;
            display: flex;
            justify-content: center;
            gap: 15px;
            z-index: 40;
            padding: 0 20px;
            box-sizing: border-box;
        }
        .action-circle-btn {
            width: 72px;
            height: 72px;
            border-radius: 50%;
            border: 3px solid rgba(255, 255, 255, 0.2);
            background: rgba(12, 10, 9, 0.6);
            color: white;
            font-weight: bold;
            font-size: 11px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;
            backdrop-filter: blur(5px);
            pointer-events: auto;
            box-shadow: 0 6px 16px rgba(0,0,0,0.4);
            text-transform: uppercase;
            transition: transform 0.1s;
        }
        .action-circle-btn:active { transform: scale(0.92); background: var(--accent-color); color: #0c0a09; border-color: white;}
        #jutsuBtn { border-color: var(--accent-color); position: relative; width: 84px; height: 84px; margin-top: -6px;}
        
        .cooldown-overlay {
            position: absolute;
            top: 0; left: 0; width: 100%; height: 100%;
            background: rgba(0,0,0,0.75);
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 18px;
            color: #ef4444;
            font-weight: bold;
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.2s;
        }
        .cooldown-overlay.active { opacity: 1; }

        .combat-log {
            position: absolute;
            bottom: 220px;
            left: 50%;
            transform: translateX(-50%);
            width: 85%;
            max-width: 400px;
            height: 70px;
            pointer-events: none;
            z-index: 35;
            display: flex;
            flex-direction: column;
            justify-content: flex-end;
            align-items: center;
            gap: 4px;
            overflow: hidden;
        }
        .log-line {
            font-size: 13px;
            font-weight: 600;
            background: rgba(0,0,0,0.5);
            padding: 3px 10px;
            border-radius: 6px;
            animation: fadeUpLog 1.5s forwards;
            text-align: center;
            white-space: nowrap;
            color: #fafaf9;
        }
        @keyframes fadeUpLog {
            0% { opacity: 0; transform: translateY(10px); }
            15% { opacity: 1; transform: translateY(0); }
            75% { opacity: 1; }
            100% { opacity: 0; transform: translateY(-15px); }
        }

        #healthBarsContainer {
            position: absolute;
            top: max(85px, env(safe-area-inset-top) + 55px);
            left: 20px;
            right: 20px;
            display: flex;
            justify-content: space-between;
            z-index: 50;
            pointer-events: none;
        }
        .hp-panel {
            background: rgba(12, 10, 9, 0.65);
            padding: 8px 12px;
            border-radius: 12px;
            width: 42%;
            border: 1px solid rgba(255, 255, 255, 0.05);
            backdrop-filter: blur(4px);
        }
        .hp-name { font-size: 12px; font-weight: bold; margin-bottom: 4px; display: flex; justify-content: space-between;}
        .hp-bg { width: 100%; height: 8px; background: #44403c; border-radius: 4px; overflow: hidden; }
        .hp-fill { width: 100%; height: 100%; background: #10b981; transition: width 0.15s ease-out; }
        .hp-fill.enemy { background: #ef4444; }

        #endGamePanel {
            position: absolute;
            bottom: 120px;
            left: 50%;
            transform: translateX(-50%);
            width: 80%;
            max-width: 320px;
            z-index: 60;
            display: none;
        }
    </style>
</head>
<body>

    <div id="loadingScreen" class="screen">
        <h1>Shinobi Rising</h1>
        <p style="color: #a8a29e; font-size: 14px;">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 style="display: flex; justify-content: space-around; margin-bottom: 10px;">
                <div class="hud-card" style="position: static;">LVL: <span id="menuLevel" class="stat-val">1</span></div>
                <div class="hud-card" style="position: static;"><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: 11px; margin-top: 4px; margin-bottom: 0;" onclick="buyUpgrade('hp')">HP +15 (<span id="costHp">50</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: 11px; margin-top: 4px; margin-bottom: 0;" onclick="buyUpgrade('atk')">ATK +3 (<span id="costAtk">50</span>🪙)</button>
                </div>
            </div>

            <div class="char-select-container">
                <div id="card-naruto" class="char-card active" onclick="selectCharacter('naruto')">
                    <div class="char-avatar" style="background-color: #f97316; border-color: #1d4ed8;"></div>
                    <h3>Naruto</h3>
                    <p id="menuNarutoJutsu">Ablenkung</p>
                </div>
                <div id="card-sasuke" class="char-card locked" onclick="selectCharacter('sasuke')">
                    <div id="lock-sasuke" class="locked-badge">🔒 Shop</div>
                    <div class="char-avatar" style="background-color: #1e1b4b;"></div>
                    <h3>Sasuke</h3>
                    <p>Chidori</p>
                </div>
                <div id="card-hinata" class="char-card locked" onclick="selectCharacter('hinata')">
                    <div id="lock-hinata" class="locked-badge">🔒 Shop</div>
                    <div class="char-avatar" style="background-color: #312e81;"></div>
                    <h3>Hinata</h3>
                    <p>64 Palmen</p>
                </div>
                <div id="card-sakura" class="char-card locked" onclick="selectCharacter('sakura')">
                    <div id="lock-sakura" class="locked-badge">🔒 Shop</div>
                    <div class="char-avatar" style="background-color: #f472b6;"></div>
                    <h3>Sakura</h3>
                    <p>Chakra-Schlag</p>
                </div>
            </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: -5px; color: #a8a29e; margin-bottom: 15px;">Deine Coins: <span id="shopCoins" class="stat-val">0</span> 🪙</p>
            
            <div class="shop-tabs">
                <button id="tab-naruto" class="tab-btn active" onclick="switchShopTab('naruto')">Naruto</button>
                <button id="tab-sasuke" class="tab-btn" onclick="switchShopTab('sasuke')">Sasuke</button>
                <button id="tab-hinata" class="tab-btn" onclick="switchShopTab('hinata')">Hinata</button>
                <button id="tab-sakura" class="tab-btn" onclick="switchShopTab('sakura')">Sakura</button>
            </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: -5px; color: #a8a29e; margin-bottom: 15px;">Erfülle Missionen für 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="combatLog" class="combat-log"></div>

        <div id="endGamePanel">
            <button class="btn btn-gold" onclick="goToHome()">🏠 Zurück zum 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>
        const state = {
            coins: 300,
            level: 1,
            xp: 0,
            selectedCharacter: 'naruto',
            upgrades: { hp: 0, atk: 0 },
            equippedSkins: { naruto: 'skin_naruto_9', sasuke: 'skin_sasuke_std', hinata: 'skin_hinata_std', sakura: 'skin_sakura_std' },
            unlockedSkins: ['skin_naruto_9'],
            quests: [
                { id: 'q1', type: 'bot_kills', label: 'Besiege 3 Gegner', progress: 0, target: 3, reward: 80, claimed: false },
                { id: 'q2', type: 'jutsu_uses', label: 'Setze 5 Spezial-Jutsus ein', progress: 0, target: 5, reward: 100, claimed: false },
                { id: 'q3', type: 'perfect_win', label: 'Gewinne 2 Kämpfe', progress: 0, target: 2, reward: 150, claimed: false },
                { id: 'q4', type: 'earn_coins', label: 'Sammle 100 Kampf-Coins', progress: 0, target: 100, reward: 120, claimed: false },
                { id: 'q5', type: 'upgrades_made', label: 'Kaufe 2 Power-Up Upgrades', progress: 0, target: 2, reward: 200, claimed: false },
                { id: 'q6', type: 'boss_fight', label: 'Besiege Madara oder Orochimaru', progress: 0, target: 1, reward: 300, claimed: false }
            ]
        };

        const skinsData = {
            naruto: [
                { id: 'skin_naruto_9', name: 'Naruto (9 Jahre)', cost: 0, color: '#f97316', secondary: '#1d4ed8', jutsu: 'Doppelgänger-Trick', power: 1.0 }, 
                { id: 'skin_naruto_16', name: 'Naruto (16 Jahre)', cost: 150, color: '#ea580c', secondary: '#000000', jutsu: 'Rasengan', power: 1.5 }, 
                { id: 'skin_naruto_hokage', name: 'Hokage Naruto', cost: 300, color: '#fef08a', secondary: '#ffffff', jutsu: 'Riesen-Rasengan', power: 2.0 } 
            ],
            sasuke: [
                { id: 'skin_sasuke_std', name: 'Charakter: Sasuke', cost: 100, color: '#1e1b4b', secondary: '#312e81', jutsu: 'Chidori', power: 1.4 }, 
                { id: 'skin_sasuke_wanderer', name: 'Wandernder Shinobi', cost: 250, color: '#0f172a', secondary: '#6366f1', jutsu: 'Kirin', power: 1.9 } 
            ],
            hinata: [
                { id: 'skin_hinata_std', name: 'Charakter: Hinata', cost: 120, color: '#312e81', secondary: '#ddd6fe', jutsu: '64 Palmen', power: 1.3 },
                { id: 'skin_hinata_wedding', name: 'Hochzeitstracht', cost: 250, color: '#1e1b4b', secondary: '#f472b6', jutsu: 'Sanfter Schritt', power: 1.8 }
            ],
            sakura: [
                { id: 'skin_sakura_std', name: 'Charakter: Sakura', cost: 120, color: '#f472b6', secondary: '#ef4444', jutsu: 'Chakra-Schlag', power: 1.3 },
                { id: 'skin_sakura_war', name: 'Ninja-Weltkrieg', cost: 250, color: '#b91c1c', secondary: '#22c55e', jutsu: 'Innere Stärke', power: 1.8 }
            ]
        };

        const botsData = [
            { name: 'Übungs-Klon', hp: 80, atk: 6, color: '#94a3b8', xp: 35, coins: 20, isBoss: false },
            { name: 'Akatsuki Spion', hp: 110, atk: 9, color: '#ef4444', xp: 50, coins: 35, isBoss: false },
            { name: 'Orochimaru-Klon', hp: 140, atk: 12, color: '#22c55e', xp: 75, coins: 50, isBoss: true },
            { name: 'Madara-Simulation', hp: 200, atk: 18, color: '#a855f7', xp: 150, coins: 100, isBoss: true }
        ];

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

        let combat = { active: false, playerHP: 100, playerMaxHP: 100, enemyHP: 100, enemyMaxHP: 100, enemyData: null, jutsuCD: 0, particles: [], shakeIntensity: 0, timer: 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 += 10;
                document.getElementById('loaderBar').style.width = prog + '%';
                if(prog >= 100) {
                    clearInterval(loader);
                    document.getElementById('loadingScreen').classList.add('hidden');
                    document.getElementById('mainMenu').classList.remove('hidden');
                }
            }, 50);
        });

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

        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;

            const hpBonus = state.upgrades.hp * 15;
            const atkBonus = state.upgrades.atk * 3;
            const costHp = (state.upgrades.hp + 1) * 50;
            const costAtk = (state.upgrades.atk + 1) * 50;

            document.getElementById('statHpBonus').innerText = hpBonus;
            document.getElementById('statAtkBonus').innerText = atkBonus;
            document.getElementById('costHp').innerText = costHp;
            document.getElementById('costAtk').innerText = costAtk;

            const currentNarutoSkin = skinsData.naruto.find(s => s.id === state.equippedSkins.naruto);
            document.getElementById('menuNarutoJutsu').innerText = currentNarutoSkin.jutsu;

            ['naruto', 'sasuke', 'hinata', 'sakura'].forEach(char => {
                const baseSkinId = skinsData[char][0].id;
                const isUnlocked = state.unlockedSkins.includes(baseSkinId);
                const card = document.getElementById(`card-${char}`);
                const lock = document.getElementById(`lock-${char}`);
                
                if (isUnlocked || char === 'naruto') {
                    card.classList.remove('locked');
                    if (lock) lock.style.display = 'none';
                } else {
                    card.classList.add('locked');
                    if (lock) lock.style.display = 'block';
                }
            });
        }

        function buyUpgrade(type) {
            const currentLvl = state.upgrades[type];
            const cost = (currentLvl + 1) * 50;

            if (state.coins >= cost) {
                state.coins -= cost;
                state.upgrades[type]++;
                showToast("Erfolgreich hochgepowert!");
                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') 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 Charakter zuerst im Shop frei!", true);
                return;
            }
            state.selectedCharacter = char;
            document.querySelectorAll('.char-card').forEach(c => c.classList.remove('active'));
            document.getElementById('card-' + char).classList.add('active');
            saveData();
        }

        function switchShopTab(tab) {
            activeShopTab = tab;
            document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
            document.getElementById('tab-' + tab).classList.add('active');
            renderShop();
        }

        function renderShop() {
            const grid = document.getElementById('shopGrid');
            grid.innerHTML = '';
            const items = skinsData[activeShopTab];
            
            // Prüfen, ob der Basis-Charakter freigeschaltet ist
            const baseSkinId = items[0].id;
            const isBaseUnlocked = state.unlockedSkins.includes(baseSkinId) || activeShopTab === 'naruto';

            items.forEach((skin, index) => {
                const itemEl = document.createElement('div');
                itemEl.className = 'shop-item';
                
                const isUnlocked = state.unlockedSkins.includes(skin.id);
                const isEquipped = state.equippedSkins[activeShopTab] === skin.id;
                
                // Ein Skin ist gesperrt, wenn es nicht der Basis-Skin ist UND der Basis-Skin noch nicht gekauft wurde
                const isSkinLockedByBase = index > 0 && !isBaseUnlocked;

                if(isEquipped) itemEl.classList.add('equipped');
                if(isSkinLockedByBase) itemEl.classList.add('locked-skin');

                let buttonText = isEquipped ? 'Aktiv' : isUnlocked ? 'Nutzen' : 'Freischalten';
                if (isSkinLockedByBase) {
                    buttonText = '🔒 Benötigt Basis';
                }

                itemEl.innerHTML = `
                    <div class="shop-avatar-placeholder" style="background-color: ${skin.color}; border-color: ${skin.secondary}; border-width: 3px;"></div>
                    <div class="item-title">${skin.name}</div>
                    <small style="color: #38bdf8;">Jutsu: ${skin.jutsu}</small>
                    <small>${isUnlocked ? 'Freigeschaltet' : skin.cost + ' Coins'}</small>
                    <button class="btn ${isEquipped ? 'btn-secondary' : isUnlocked ? 'btn-primary' : 'btn-gold'}" 
                            style="padding: 6px 12px; font-size: 12px; margin-bottom: 0;" ${isSkinLockedByBase ? 'disabled' : ''}>
                        ${buttonText}
                    </button>
                `;

                itemEl.querySelector('button').onclick = () => {
                    if (isSkinLockedByBase) return;

                    if (!isUnlocked) {
                        if (state.coins >= skin.cost) {
                            state.coins -= skin.cost;
                            state.unlockedSkins.push(skin.id);
                            showToast(`${skin.name} freigeschaltet!`);
                            renderShop();
                            saveData();
                        } else {
                            showToast("Nicht genug Coins!", true);
                        }
                    } else if (!isEquipped) {
                        state.equippedSkins[activeShopTab] = skin.id;
                        renderShop();
                        saveData();
                    }
                };
                grid.appendChild(itemEl);
            });
            updateUI();
        }

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

            state.quests.forEach(quest => {
                const itemEl = document.createElement('div');
                itemEl.className = 'quest-item';
                const isReady = quest.progress >= quest.target && !quest.claimed;
                if(isReady) itemEl.classList.add('ready');
                if(quest.claimed) itemEl.classList.add('claimed');

                itemEl.innerHTML = `
                    <div class="quest-info">
                        <span class="quest-label">${quest.label}</span>
                        <span class="quest-progress">${quest.progress}/${quest.target}</span>
                    </div>
                    <div style="display: flex; align-items: center; gap: 8px;">
                        <span class="quest-reward-tag">+${quest.reward} Ryo</span>
                        ${isReady ? `<button class="btn btn-gold btn-quest-claim">Einlösen</button>` : ''}
                    </div>
                `;

                if(isReady) {
                    itemEl.querySelector('button').onclick = () => {
                        quest.claimed = true;
                        state.coins += quest.reward;
                        showToast(`+${quest.reward} Coins erhalten!`);
                        renderQuests();
                        saveData();
                    };
                }
                list.appendChild(itemEl);
            });
        }

        function triggerQuestProgress(type, amount = 1) {
            state.quests.forEach(q => {
                if(q.type === type && q.progress < q.target && !q.claimed) {
                    q.progress = Math.min(q.target, q.progress + amount);
                    if(q.progress === q.target) {
                        const notifier = document.getElementById('quest-notifier');
                        document.getElementById('quest-notifier-text').innerText = `Quest fertig: ${q.label}`;
                        notifier.classList.add('show');
                        setTimeout(() => notifier.classList.remove('show'), 3000);
                    }
                }
            });
            saveData();
        }

        function startBotFight() {
            const botIdx = Math.min(Math.floor(Math.random() * botsData.length), state.level - 1 + 1);
            const botTemplate = botsData[botIdx];

            combat.enemyData = botTemplate;
            
            const baseHp = 100 + (state.level * 10);
            const upgradeHpBonus = state.upgrades.hp * 15;
            
            combat.playerMaxHP = baseHp + upgradeHpBonus;
            combat.playerHP = combat.playerMaxHP;
            combat.enemyMaxHP = botTemplate.hp;
            combat.enemyHP = botTemplate.hp;
            combat.jutsuCD = 0;
            combat.particles = [];
            combat.active = true;

            document.getElementById('endGamePanel').style.display = 'none';
            document.getElementById('combatOverlay').style.display = 'flex';
            document.getElementById('hudEnemyName').innerText = botTemplate.name;
            document.getElementById('enemyHpName').innerText = botTemplate.name;
            document.getElementById('combatLog').innerHTML = '';
            
            updateCombatBars();
            updateJutsuButton();
            openScreen('game');
            
            if(!animationFrameId) gameLoop();
            addLog(`Ein wilder ${botTemplate.name} fordert dich heraus!`);
        }

        function updateCombatBars() {
            const pPct = (combat.playerHP / combat.playerMaxHP) * 100;
            const ePct = (combat.enemyHP / combat.enemyMaxHP) * 100;
            document.getElementById('playerHpBar').style.width = Math.max(0, pPct) + '%';
            document.getElementById('enemyHpBar').style.width = Math.max(0, ePct) + '%';
            document.getElementById('playerHpTxt').innerText = `${Math.max(0, combat.playerHP)}/${combat.playerMaxHP}`;
            document.getElementById('enemyHpTxt').innerText = `${Math.max(0, combat.enemyHP)}/${combat.enemyMaxHP}`;
        }

        function updateJutsuButton() {
            const overlay = document.getElementById('jutsuCDOverlay');
            if(combat.jutsuCD > 0) {
                overlay.innerText = combat.jutsuCD;
                overlay.classList.add('active');
            } else { overlay.classList.remove('active'); }
        }

        function addLog(text) {
            const logBox = document.getElementById('combatLog');
            const line = document.createElement('div');
            line.className = 'log-line';
            line.innerText = text;
            logBox.appendChild(line);
            if(logBox.childNodes.length > 3) logBox.removeChild(logBox.firstChild);
        }

        function spawnHitParticles(x, y, color) {
            for(let i=0; i<15; i++) {
                combat.particles.push({
                    x: x, y: y, vx: (Math.random() - 0.5) * 8, vy: (Math.random() - 0.5) * 8,
                    radius: Math.random() * 4 + 2, alpha: 1, color: color
                });
            }
        }

        function playerAction(action) {
            if(!combat.active) return;

            const playerSkinId = state.equippedSkins[state.selectedCharacter];
            const currentSkin = skinsData[state.selectedCharacter].find(s => s.id === playerSkinId);
            const upgradeAtkBonus = state.upgrades.atk * 3;

            if(action === 'flee') {
                addLog("Du bist geflohen!");
                showEndBattleMenu();
                return;
            }

            if(action === 'jutsu' && combat.jutsuCD > 0) {
                showToast("Chakra lädt noch auf!", true);
                return;
            }

            let dmg = 0;
            if(action === 'attack') {
                dmg = Math.floor(Math.random() * 6) + 8 + upgradeAtkBonus;
                combat.enemyHP -= dmg;
                addLog(`Du greifst an und verursachst ${dmg} Schaden.`);
                spawnHitParticles(canvas.width * 0.7, canvas.height * 0.5, currentSkin.color);
            } else if(action === 'jutsu') {
                dmg = Math.floor((Math.random() * 10 + 15 + (upgradeAtkBonus * 1.5)) * currentSkin.power);
                combat.enemyHP -= dmg;
                
                addLog(`Jutsu! ${currentSkin.jutsu} trifft für ${dmg} Schaden!`);
                combat.jutsuCD = 3;
                
                const pColor = (currentSkin.id === 'skin_naruto_9') ? '#fbbf24' : '#38bdf8';
                spawnHitParticles(canvas.width * 0.7, canvas.height * 0.5, pColor);
                combat.shakeIntensity = 12;
                triggerQuestProgress('jutsu_uses', 1);
            }

            combat.enemyHP = Math.max(0, combat.enemyHP);
            updateCombatBars();

            if(combat.enemyHP <= 0) {
                winBattle();
                return;
            }

            setTimeout(() => {
                if(!combat.active) return;
                let botDmg = Math.floor(Math.random() * 5) + combat.enemyData.atk;
                combat.playerHP -= botDmg;
                combat.playerHP = Math.max(0, combat.playerHP);
                updateCombatBars();
                addLog(`${combat.enemyData.name} kontert: ${botDmg} Schaden.`);
                spawnHitParticles(canvas.width * 0.3, canvas.height * 0.5, combat.enemyData.color);
                combat.shakeIntensity = 6;

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

            if(combat.jutsuCD > 0) combat.jutsuCD--;
            updateJutsuButton();
        }

        function winBattle() {
            addLog("Sieg! Du hast gewonnen!");
            state.coins += combat.enemyData.coins;
            state.xp += combat.enemyData.xp;
            
            triggerQuestProgress('bot_kills', 1);
            triggerQuestProgress('perfect_win', 1);
            triggerQuestProgress('earn_coins', combat.enemyData.coins);
            if(combat.enemyData.isBoss) triggerQuestProgress('boss_fight', 1);

            let xpNeeded = state.level * 100;
            if(state.xp >= xpNeeded) {
                state.xp -= xpNeeded;
                state.level++;
                setTimeout(() => showToast(`LEVEL UP! Du bist jetzt Level ${state.level}`), 800);
            }
            saveData();
            showEndBattleMenu();
        }

        function loseBattle() { addLog("Du wurdest besiegt..."); showEndBattleMenu(); }

        function showEndBattleMenu() {
            combat.active = false;
            document.getElementById('combatOverlay').style.display = 'none';
            document.getElementById('endGamePanel').style.display = 'block';
        }

        function goToHome() {
            combat.active = false;
            combat.enemyData = null;
            combat.particles = [];
            if(animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; }
            document.getElementById('endGamePanel').style.display = 'none';
            openScreen('mainMenu');
        }

        function gameLoop() {
            if (!combat.active && currentScreen !== 'game') return;
            animationFrameId = requestAnimationFrame(gameLoop);
            
            let dx = 0, dy = 0;
            if(combat.shakeIntensity > 0) {
                dx = (Math.random() - 0.5) * combat.shakeIntensity;
                dy = (Math.random() - 0.5) * combat.shakeIntensity;
                combat.shakeIntensity *= 0.85;
                if(combat.shakeIntensity < 0.1) combat.shakeIntensity = 0;
            }

            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.save();
            ctx.translate(dx, dy);

            combat.timer += 0.04;

            ctx.fillStyle = '#292524';
            ctx.fillRect(0, canvas.height * 0.6, canvas.width, canvas.height * 0.4);
            ctx.fillStyle = '#f97316';
            ctx.fillRect(0, canvas.height * 0.6, canvas.width, 4);

            if(combat.enemyData) {
                let pX = canvas.width * 0.3;
                let pY = canvas.height * 0.58 + Math.sin(combat.timer * 2.5) * 4;

                ctx.save();
                ctx.translate(pX, pY);

                const skinId = state.equippedSkins[state.selectedCharacter];
                const skin = skinsData[state.selectedCharacter].find(s => s.id === skinId);

                ctx.fillStyle = '#1e293b'; 
                ctx.fillRect(-8, -22, 6, 22);
                ctx.fillRect(2, -22, 6, 22);

                ctx.fillStyle = skin.color;
                ctx.fillRect(-12, -50, 24, 28);

                ctx.fillStyle = skin.secondary;
                
                if(state.selectedCharacter === 'naruto') {
                    if(skinId === 'skin_naruto_9') {
                        ctx.fillRect(-12, -50, 24, 6);
                        ctx.fillRect(-4, -44, 8, 22);
                    } else {
                        ctx.fillRect(-3, -50, 6, 28);
                    }
                }
                if(state.selectedCharacter === 'sakura') ctx.fillRect(-12, -32, 24, 4); 
                if(state.selectedCharacter === 'hinata') ctx.fillRect(-12, -50, 24, 10); 

                ctx.beginPath();
                ctx.arc(0, -62, 11, 0, Math.PI * 2);
                ctx.fillStyle = '#ffedd5';
                ctx.fill();

                if (state.selectedCharacter === 'naruto') {
                    ctx.fillStyle = '#facc15'; 
                    ctx.fillRect(-12, -73, 24, 6);
                    
                    ctx.fillStyle = (skinId === 'skin_naruto_9') ? '#1d4ed8' : '#000000';
                    ctx.fillRect(-10, -66, 20, 4);
                } else if(state.selectedCharacter === 'sasuke') {
                    ctx.fillStyle = '#0f172a'; 
                    ctx.fillRect(-13, -73, 26, 7);
                } else if(state.selectedCharacter === 'hinata') {
                    ctx.fillStyle = '#1e1b4b'; 
                    ctx.fillRect(-12, -71, 24, 6);
                    ctx.fillRect(-12, -65, 4, 25); 
                    ctx.fillRect(8, -65, 4, 25);
                } else if(state.selectedCharacter === 'sakura') {
                    ctx.fillStyle = '#f472b6'; 
                    ctx.fillRect(-12, -72, 24, 6);
                    ctx.fillRect(-12, -66, 3, 12);
                    ctx.fillRect(9, -66, 3, 12);
                }
                ctx.restore();

                let eX = canvas.width * 0.7;
                let eY = canvas.height * 0.58 + Math.cos(combat.timer * 2.5) * 4;

                ctx.save();
                ctx.translate(eX, eY);
                ctx.fillStyle = '#1c1917';
                ctx.fillRect(-9, -22, 6, 22);
                ctx.fillRect(3, -22, 6, 22);
                ctx.fillStyle = combat.enemyData.color;
                ctx.fillRect(-13, -50, 26, 28);
                ctx.beginPath();
                ctx.arc(0, -62, 11, 0, Math.PI * 2);
                ctx.fillStyle = '#cbd5e1';
                ctx.fill();
                ctx.restore();
            }

            for(let i = combat.particles.length - 1; i >= 0; i--) {
                let p = combat.particles[i];
                p.x += p.vx; p.y += p.vy; p.alpha -= 0.025;
                if(p.alpha <= 0) { combat.particles.splice(i, 1); } 
                else {
                    ctx.save(); ctx.globalAlpha = p.alpha; ctx.beginPath();
                    ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
                    ctx.fillStyle = p.color; ctx.fill(); ctx.restore();
                }
            }
            ctx.restore();
        }

        function showToast(msg, isError = false) {
            const container = document.getElementById('toast-container');
            const t = document.createElement('div');
            t.className = 'toast';
            if(isError) t.style.background = '#ef4444';
            t.innerText = msg;
            container.appendChild(t);
            setTimeout(() => t.remove(), 2500);
        }

        function saveData() { localStorage.setItem('shinobi_rising_save_v5', JSON.stringify(state)); }

        function loadSavedData() {
            const data = localStorage.getItem('shinobi_rising_save_v5');
            if(data) {
                try {
                    const parsed = JSON.parse(data);
                    Object.assign(state, parsed);
                    selectCharacter(state.selectedCharacter);
                } catch(e) { console.error("Speicherstand fehlerhaft", e); }
            }
        }
    </script>
</body>
</html>

Game Source: Naruto: Shinobi Rising

Creator: NovaGalaxy88

Libraries: none

Complexity: complex (1030 lines, 45.6 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-novagalaxy88" to link back to the original. Then publish at arcadelab.ai/publish.