function updateRewards(){ const btn=document.getElementById('rewardsBtn'); const hasNew=REWARD_DEFS.some((r,i)=>!GS.rewardsClaimed.includes(i)&&GS.trophies>=getTrophyFloor()+r.offset); if(hasNew)btn.classList.add('has-new');else btn.classList.remove('has-new'); } function savePlayerName(){ const v=document.getElementById('playerNameInput').value.trim(); if(v){GS.playerName=v;saveGS();toast('השם נשמר!');} } // ==================== DECK ==================== let tempDeck=[]; function goDeck(){tempDeck=[...GS.deck];renderDeck();showScreen('deckScreen');} function renderDeck(){ document.getElementById('dCount').textContent=tempDeck.length; document.getElementById('dNext').disabled=tempDeck.length!==10; const selRow=document.getElementById('selRow'); selRow.innerHTML=''; for(let i=0;i<10;i++){ const d=document.createElement('div');d.className='sel-slot '+(ix.id===tempDeck[i]);d.textContent=c.icon;} selRow.appendChild(d); } const grid=document.getElementById('charGrid'); grid.innerHTML=''; CHARS.forEach(c=>{ const el=document.createElement('div'); const picked=tempDeck.includes(c.id); const locked=!isUnl(c.id); el.className='char-cell '+(picked?'picked':'')+(locked?' locked':''); el.innerHTML=`
Lv.${getLvl(c.id)}
${locked?'
נעול
':''}
${c.icon}
${c.name}
❤️${c.hp} ⚔️${c.dmg}
💧${c.cost} ${c.special}
`; if(!locked)el.onclick=()=>toggleChar(c.id); grid.appendChild(el); }); } function toggleChar(id){ if(tempDeck.includes(id)){tempDeck=tempDeck.filter(x=>x!==id);} else if(tempDeck.length<10){tempDeck.push(id);} renderDeck(); } function goMaps(){if(tempDeck.length!==10)return;GS.deck=[...tempDeck];saveGS();renderMaps();showScreen('mapScreen');} // ==================== MAPS ==================== let selMap=0; function renderMaps(){ const g=document.getElementById('mapGrid'); g.innerHTML=''; MAPS.forEach(m=>{ const el=document.createElement('div'); el.className='map-cell '+(selMap===m.id?'chosen':''); el.style.background=m.bg; el.innerHTML=`
${m.emoji}
${m.name}
`; el.onclick=()=>{selMap=m.id;renderMaps();document.getElementById('mStart').disabled=false;}; g.appendChild(el); }); } // ==================== GAME ==================== let active=false,paused=false,tmr,loop,manaTmr,botTmr; let units=[],projectiles=[]; let elixir=5,botElixir=5; let timeLeft=180; let hand=[],botHand=[],deckQueue=[],botQueue=[]; let botLvl=1,botDeck=[]; let mapData=null; let trainMode=false,trainDiff='medium'; let towers={}; function startGame(){trainMode=false;initGame();} function startTrain(diff){trainMode=true;trainDiff=diff;initGame();} function initGame(){ active=true;paused=false;units=[];projectiles=[];elixir=5;botElixir=5;timeLeft=180; mapData=MAPS[selMap]; botLvl=trainMode?(diffToBotLvl(trainDiff)):getBotLvl(); botDeck=trainMode?buildBotDeck(botLvl):buildBotDeck(botLvl); deckQueue=shuffle([...GS.deck]);botQueue=shuffle([...botDeck]); hand=drawHand(deckQueue,4);botHand=drawHand(botQueue,4); towers={p:[2400,2400,4000],b:[2400,2400,4000],pActive:[true,true,false],bActive:[true,true,false]}; updateTowers(); document.getElementById('arena').style.background=mapData.bg; renderScenery(); showScreen('gameScreen'); renderHand(); updateHUD(); if(tmr)clearInterval(tmr);tmr=setInterval(()=>{if(!active||paused)return;timeLeft--;if(timeLeft<=0)endGame();updateHUD();},1000); if(loop)clearInterval(loop);loop=setInterval(gameTick,50); if(manaTmr)clearInterval(manaTmr);manaTmr=setInterval(()=>{if(!active||paused)return;elixir=Math.min(10,elixir+0.15);botElixir=Math.min(10,botElixir+getBotElixirRate());updateElixir();},200); if(botTmr)clearInterval(botTmr);botTmr=setInterval(botTick,1200); document.getElementById('arena').onclick=arenaClick; } function diffToBotLvl(d){return d==='easy'?1:d==='medium'?3:d==='hard'?6:10;} function buildBotDeck(lvl){ const pool=CHARS.filter((_,i)=>i<10+(lvl*2)).map(c=>c.id); const d=[];while(d.length<10){const r=pool[Math.floor(Math.random()*pool.length)];if(!d.includes(r))d.push(r);} return d; } function shuffle(a){const n=[...a];for(let i=n.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[n[i],n[j]]=[n[j],n[i]];}return n;} function drawHand(queue,n){const h=[];while(h.length0)h.push(queue.shift());return h;} function renderScenery(){ const arena=document.getElementById('arena'); arena.querySelectorAll('.scenery').forEach(e=>e.remove()); if(mapData.scenery)mapData.scenery.forEach(s=>{ const e=document.createElement('div');e.className='scenery';e.textContent=s.icon; e.style.left=s.x+'%';e.style.top=s.y+'%';e.style.fontSize=s.s+'rem';e.style.opacity=s.o; arena.appendChild(e); }); } function updateHUD(){ document.getElementById('hTimer').textContent=fmtTime(timeLeft); document.getElementById('hPlayer').textContent=towers.pActive.filter(Boolean).length; document.getElementById('hBot').textContent=towers.bActive.filter(Boolean).length; } function fmtTime(s){const m=Math.floor(s/60);const r=s%60;return m+':'+(r<10?'0':'')+r;} function updateElixir(){ document.getElementById('eText').textContent='אליקסיר: '+Math.floor(elixir)+'/10'; document.getElementById('eFill').style.width=(elixir*10)+'%'; renderHand(); } function renderHand(){ const bar=document.getElementById('handBar'); bar.innerHTML=''; hand.forEach((id,idx)=>{ const c=CHARS.find(x=>x.id===id); const el=document.createElement('div'); const can=elixir>=c.cost; el.className='hand-card '+(selCard===idx?'selected ':'')+(can?'':'disabled'); el.innerHTML=`
${c.cost}
${c.icon}
${c.name}
`; el.onclick=(e)=>{e.stopPropagation();selectCard(idx);}; bar.appendChild(el); }); } let selCard=-1; function selectCard(idx){ if(idx>=hand.length)return; const c=CHARS.find(x=>x.id===hand[idx]); if(elixir=0){ghost.style.display='block';}else{ghost.style.display='none';} } function arenaClick(e){ if(selCard<0||!active||paused)return; const c=CHARS.find(x=>x.id===hand[selCard]); if(elixirrect.height*0.55||y0?deckQueue.shift():hand[selCard]; selCard=-1;document.getElementById('ghost').style.display='none'; updateElixir();renderHand(); } function spawnUnit(charId,side,x,y){ const s=getStats(charId); const arena=document.getElementById('arena'); const el=document.createElement('div'); el.className='unit '+side; el.style.left=(x-21)+'px';el.style.top=(y-21)+'px'; el.innerHTML=s.icon+`
`; arena.appendChild(el); const unit={id:Date.now()+Math.random(),charId,char:s,side,el,x,y,hp:s.hp,maxHp:s.hp,lastAtk:0,state:'walk',target:null}; units.push(unit); } function gameTick(){ if(!active||paused)return; const arena=document.getElementById('arena'); const w=arena.offsetWidth,h=arena.offsetHeight; // Update units units.forEach(u=>{ if(u.hp<=0)return; // Find target let nearest=null,nd=Infinity; // Enemy units units.forEach(o=>{ if(o.hp<=0||o.side===u.side)return; const d=dist(u,o); if(d{ const isBot=u.side==='player'; const tSide=isBot?'b':'p'; if(!towers[tSide+'Active'][ti])return; const tx=getTowerX(ti,w);const ty=isBot?h*0.11:h*0.89; const d=Math.hypot(u.x-tx,u.y-ty); if(d1000/u.char.speed){ u.lastAtk=Date.now(); if(nearest.tower){ dmgTower(nearest.side,nearest.index,u.char.dmg); }else{ dmgUnit(nearest,u.char.dmg); } } }else{ u.state='walk'; const ty=u.side==='player'?0:h; const tx=w/2; const angle=Math.atan2(ty-u.y,tx-u.x); const spd=u.char.speed*1.5; u.x+=Math.cos(angle)*spd;u.y+=Math.sin(angle)*spd; u.el.style.left=(u.x-21)+'px';u.el.style.top=(u.y-21)+'px'; } }); // Tower shooting [0,1,2].forEach(ti=>{ ['p','b'].forEach(ts=>{ if(!towers[ts+'Active'][ti])return; const isBot=ts==='b'; const tx=getTowerX(ti,w);const ty=isBot?h*0.11:h*0.89; let nearest=null,nd=TOWER_RANGE; units.forEach(u=>{ if(u.hp<=0||u.side===(isBot?'bot':'player'))return; const d=Math.hypot(u.x-tx,u.y-ty); if(dTOWER_ATK_SPEED){ towers[key]=Date.now(); fireShot(tx,ty,nearest,TOWER_DMG); } } }); }); // Projectiles projectiles.forEach(p=>{ if(p.done)return; const dx=p.target.x-p.x;const dy=p.target.y-p.y; const d=Math.hypot(dx,dy); if(d<10){ p.done=true;p.el.remove(); if(p.target.tower){ dmgTower(p.target.side,p.target.index,p.dmg); }else if(units.includes(p.target)){ dmgUnit(p.target,p.dmg); } }else{ p.x+=dx/d*12;p.y+=dy/d*12; p.el.style.left=(p.x-5)+'px';p.el.style.top=(p.y-5)+'px'; } }); projectiles=projectiles.filter(p=>!p.done); // Cleanup dead units.forEach(u=>{ if(u.hp<=0&&u.el.parentNode){u.el.remove();} }); units=units.filter(u=>u.hp>0); // Check end if(towers.pActive[2]===false||towers.bActive[2]===false)endGame(); } function getTowerX(i,w){return i===0?w*0.05:i===1?w*0.95:w*0.5;} function dist(a,b){return Math.hypot(a.x-b.x,a.y-b.y);} function dmgUnit(u,dmg){ u.hp-=dmg; const pct=Math.max(0,u.hp/u.maxHp*100); u.el.querySelector('.unit-hp-fg').style.width=pct+'%'; showDmg(u.x,u.y-25,dmg); if(u.hp<=0){u.el.style.opacity='0';setTimeout(()=>u.el&&u.el.remove(),300);} } function dmgTower(side,idx,dmg){ const key=side==='b'?'b':'p'; towers[key][idx]-=dmg; const el=document.getElementById('hp'+(side==='b'?'B':'P')+idx); if(el)el.textContent=Math.max(0,Math.floor(towers[key][idx])); showDmg(getTowerX(idx,document.getElementById('arena').offsetWidth),side==='b'?40:document.getElementById('arena').offsetHeight-40,dmg); if(towers[key][idx]<=0){ towers[key][idx]=0; towers[key+'Active'][idx]=false; const tel=document.getElementById('t'+(side==='b'?'B':'P')+idx); if(tel){tel.classList.add('destroyed');if(idx===2)tel.classList.remove('inactive');} if(idx<2){ const king=document.getElementById('t'+(side==='b'?'B':'P')+2); if(king)king.classList.remove('inactive'); towers[key+'Active'][2]=true; } } } function showDmg(x,y,dmg){ const el=document.createElement('div');el.className='dmg-pop';el.textContent='-'+Math.floor(dmg); el.style.left=(x-15)+'px';el.style.top=y+'px'; document.getElementById('arena').appendChild(el); setTimeout(()=>el.remove(),800); } function fireShot(x,y,target,dmg){ const arena=document.getElementById('arena'); const el=document.createElement('div');el.className='tower-shot'; el.style.left=(x-5)+'px';el.style.top=(y-5)+'px'; el.style.background=target.side==='b'?'#4CAF50':'#f44336'; arena.appendChild(el); projectiles.push({el,x,y,target,dmg,done:false}); } function getBotElixirRate(){ if(trainMode){ return trainDiff==='easy'?0.05:trainDiff==='medium'?0.12:trainDiff==='hard'?0.2:0.35; } return 0.1+Math.min(0.15,(botLvl-1)*0.02); } function botTick(){ if(!active||paused)return; const arena=document.getElementById('arena'); const w=arena.offsetWidth,h=arena.offsetHeight; // Simple AI: play random affordable card in random position const affordable=botHand.map((id,idx)=>({id,idx,c:CHARS.find(x=>x.id===id)})).filter(o=>botElixir>=o.c.cost); if(affordable.length===0)return; const pick=affordable[Math.floor(Math.random()*affordable.length)]; botElixir-=pick.c.cost; const x=w*0.2+Math.random()*w*0.6; const y=h*0.08+Math.random()*h*0.15; spawnUnit(pick.id,'bot',x,y); botHand[pick.idx]=botQueue.length>0?botQueue.shift():botHand[pick.idx]; } function updateTowers(){ [0,1,2].forEach(i=>{ ['P','B'].forEach(s=>{ const el=document.getElementById('t'+s+i); const side=s==='B'?'b':'p'; if(towers[side+'Active'][i])el.classList.remove('inactive','destroyed'); else if(towers[side][i]<=0)el.classList.add('destroyed'); }); }); } function endGame(){ if(!active)return;active=false; clearInterval(tmr);clearInterval(loop);clearInterval(manaTmr);clearInterval(botTmr); const pCount=towers.pActive.filter(Boolean).length; const bCount=towers.bActive.filter(Boolean).length; let result,title,coins=0,trophies=0; if(pCount>bCount||towers.bActive[2]===false){result='win';title='ניצחון! 🎉';coins=30+Math.floor(Math.random()*20);trophies=30;} else if(bCount>pCount||towers.pActive[2]===false){result='lose';title='הפסד...';coins=5;trophies=-getLossAmount();} else{result='draw';title='תיקו 🤝';coins=10;trophies=0;} if(result==='win')GS.wins++;else if(result==='lose')GS.losses++; GS.coins=Math.max(0,GS.coins+coins); GS.trophies=Math.max(0,GS.trophies+trophies); updateLeaderboard();saveGS(); document.getElementById('rTitle').textContent=title; document.getElementById('rTitle').className='rtitle '+result; document.getElementById('rRewards').innerHTML=`
💰 +${coins} מטבעות
`+(trophies!==0?`
🏆 ${trophies>0?'+':''}${trophies} גביעים
`:''); document.getElementById('resultOverlay').classList.add('on'); } // ==================== COLLECTION ==================== function goCollection(){renderUpgrades();showScreen('collectionScreen');} function renderUpgrades(){ document.getElementById('cCoins').textContent=GS.coins; const g=document.getElementById('upGrid'); g.innerHTML=''; CHARS.forEach(c=>{ if(!isUnl(c.id))return; const lvl=getLvl(c.id); const max=getMaxLvl(); const el=document.createElement('div');el.className='up-card'; const s=getStats(c.id); const cost=getCost(lvl); const can=lvl=cost; el.innerHTML=`
Lv.${lvl}
${c.icon}
${c.name}
❤️${s.hp} ⚔️${s.dmg}
💰 שדרוג: ${lvl>=max?'מקסימום':cost}
`+(lvl>=max?'
מקסימום ⭐
':``); g.appendChild(el); }); } function doUpgrade(id){ const lvl=getLvl(id); const cost=getCost(lvl); if(GS.coins=getMaxLvl())return; GS.coins-=cost;GS.levels[id]=lvl+1;saveGS();toast('שודרג לרמה '+(lvl+1)+'!');renderUpgrades();updateMenu(); } // ==================== LEADERBOARD ==================== function goLeaderboard(){renderLB();showScreen('leaderboardScreen');} function renderLB(){ const tbody=document.getElementById('lbBody'); tbody.innerHTML=''; const list=getTop5(); list.forEach((p,i)=>{ const tr=document.createElement('tr'); tr.innerHTML=`${i+1}${p.name}${p.trophies}`; tbody.appendChild(tr); }); if(list.length===0){tbody.innerHTML='אין נתונים עדיין';} } // ==================== REWARDS ==================== function goRewards(){renderRewards();showScreen('rewardsScreen');} function renderRewards(){ const g=document.getElementById('rewardsGrid'); g.innerHTML=''; const floor=getTrophyFloor(); REWARD_DEFS.forEach((r,i)=>{ const el=document.createElement('div'); const claimed=GS.rewardsClaimed.includes(i); const available=GS.trophies>=floor+r.offset; el.className='reward-item '+(claimed?'claimed':available?'available':'locked'); el.innerHTML=`
${r.icon}
${r.title}
${r.desc}
נדרש: ${floor+r.offset} גביעים
${claimed?'
':''}`; if(available&&!claimed)el.onclick=()=>claimReward(i); g.appendChild(el); }); } function claimReward(idx){ if(GS.rewardsClaimed.includes(idx))return; const r=REWARD_DEFS[idx]; GS.rewardsClaimed.push(idx); if(r.type==='coins'){GS.coins+=r.amount;toast('קיבלת '+r.amount+' מטבעות!');} else if(r.type==='coinChest'){openCoinChest();} else if(r.type==='charChest'){openCharChestReward();} saveGS();renderRewards();updateMenu(); } // ==================== CHESTS ==================== let pendingCoinReward=0; function openCoinChest(){ pendingCoinReward=50+Math.floor(Math.random()*100); document.getElementById('chestOverlay').classList.add('on'); document.getElementById('chestVisual').classList.remove('opened'); document.getElementById('chestReward').textContent=''; document.getElementById('chestContinue').style.display='none'; } function openChest(){ const v=document.getElementById('chestVisual'); if(v.classList.contains('opened'))return; v.classList.add('opened'); document.getElementById('chestReward').textContent='💰 '+pendingCoinReward+' מטבעות!'; document.getElementById('chestContinue').style.display='inline-block'; GS.coins+=pendingCoinReward;saveGS();updateMenu(); } function closeChest(){document.getElementById('chestOverlay').classList.remove('on');} let pendingCharId=null; function openCharChestReward(){ const locked=CHARS.filter(c=>!isUnl(c.id)&&canUnl(c.id)); if(locked.length===0){toast('אין דמויות חדשות לפתיחה!');GS.coins+=200;return;} pendingCharId=locked[Math.floor(Math.random()*locked.length)].id; document.getElementById('charChestOverlay').classList.add('on'); document.getElementById('charChestVisual').classList.remove('opened'); document.getElementById('charChestReward').textContent=''; document.getElementById('charChestContinue').style.display='none'; } function openCharChest(){ const v=document.getElementById('charChestVisual'); if(v.classList.contains('opened'))return; v.classList.add('opened'); const c=CHARS.find(x=>x.id===pendingCharId); document.getElementById('charChestReward').innerHTML=c.icon+'
'+c.name+''; document.getElementById('charChestContinue').style.display='inline-block'; if(!GS.unlocked.includes(pendingCharId))GS.unlocked.push(pendingCharId); saveGS();updateMenu(); } function closeCharChest(){document.getElementById('charChestOverlay').classList.remove('on');} // Mouse follow ghost document.addEventListener('mousemove',e=>{ const ghost=document.getElementById('ghost'); if(selCard<0||!ghost)return; const arena=document.getElementById('arena'); const rect=arena.getBoundingClientRect(); const x=e.clientX-rect.left;const y=e.clientY-rect.top; if(x>0&&x0&&y