Naruto: Shinobi Rising
by NovaGalaxy881030 lines45.6 KB
<!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.