🎮ArcadeLab

function updateRewards(){ const btn=document.getElementByI

by FrozenOtter31
499 lines19.5 KB
▶ Play
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 '+(i<tempDeck.length?'':'empty');
    if(i<tempDeck.length){const c=CHARS.find(x=>x.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=`<div class="c-lvl">Lv.${getLvl(c.id)}</div>${locked?'<div class="c-lock">נעול</div>':''}<div class="c-icon">${c.icon}</div><div class="c-name">${c.name}</div><div class="c-info">❤️${c.hp} ⚔️${c.dmg}<br>💧${c.cost} ${c.special}</div>`;
    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=`<div class="m-emoji">${m.emoji}</div><div class="m-name">${m.name}</div>`;
    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.length<n&&queue.length>0)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=`<div class="hc-cost">${c.cost}</div><div class="hc-icon">${c.icon}</div><div class="hc-name">${c.name}</div>`;
    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<c.cost)return;
  selCard=selCard===idx?-1:idx;
  renderHand();
  const ghost=document.getElementById('ghost');
  if(selCard>=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(elixir<c.cost)return;
  const arena=document.getElementById('arena');
  const rect=arena.getBoundingClientRect();
  const x=e.clientX-rect.left;const y=e.clientY-rect.top;
  if(y>rect.height*0.55||y<rect.height*0.25)return;
  elixir-=c.cost;
  spawnUnit(hand[selCard],'player',x,y);
  hand[selCard]=deckQueue.length>0?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+`<div class="unit-hp-bg"><div class="unit-hp-fg" style="width:100%"></div></div>`;
  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<nd){nd=d;nearest=o;}
    });
    // Towers
    [0,1,2].forEach(ti=>{
      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(d<nd){nd=d;nearest={x:tx,y:ty,tower:true,side:tSide,index:ti};}
    });
    
    if(nearest&&nd<=u.char.range){
      u.state='attack';
      if(Date.now()-u.lastAtk>1000/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(d<nd){nd=d;nearest=u;}
      });
      if(nearest){
        const key=ts+ti;
        if(!towers[key])towers[key]=0;
        if(Date.now()-towers[key]>TOWER_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=`<div class="r-reward">💰 <b>+${coins}</b> מטבעות</div>`+(trophies!==0?`<div class="r-reward">🏆 <b>${trophies>0?'+':''}${trophies}</b> גביעים</div>`:'');
  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<max&&GS.coins>=cost;
    el.innerHTML=`<div class="c-lvl" style="position:static;display:inline-block;margin-bottom:4px;">Lv.${lvl}</div><div class="up-icon">${c.icon}</div><div class="up-name">${c.name}</div><div class="up-stat">❤️${s.hp} ⚔️${s.dmg}<br>💰 שדרוג: ${lvl>=max?'מקסימום':cost}</div>`+(lvl>=max?'<div class="up-max">מקסימום ⭐</div>':`<button class="up-btn" ${can?'':'disabled'} onclick="doUpgrade(${c.id})">שדרג</button>`);
    g.appendChild(el);
  });
}
function doUpgrade(id){
  const lvl=getLvl(id);
  const cost=getCost(lvl);
  if(GS.coins<cost||lvl>=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=`<td>${i+1}</td><td>${p.name}</td><td>${p.trophies}</td>`;
    tbody.appendChild(tr);
  });
  if(list.length===0){tbody.innerHTML='<tr><td colspan="3">אין נתונים עדיין</td></tr>';}
}

// ==================== 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=`<div class="reward-icon">${r.icon}</div><div class="reward-info"><div class="reward-title">${r.title}</div><div class="reward-desc">${r.desc}</div><div class="reward-trophy">נדרש: ${floor+r.offset} גביעים</div></div>${claimed?'<div class="reward-check">✓</div>':''}`;
    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+'<br><span style="font-size:1.2rem">'+c.name+'</span>';
  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&&x<rect.width&&y>0&&y<rect.height){
    ghost.style.left=(x-20)+'px';ghost.style.top=(y-20)+'px';ghost.style.display='block';
  }else{ghost.style.display='none';}
});

// Init
updateMenu();
updateRewards();

Game Source: function updateRewards(){ const btn=document.getElementByI

Creator: FrozenOtter31

Libraries: none

Complexity: complex (499 lines, 19.5 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: function-updaterewards-const-btn-documen-frozenotter31" to link back to the original. Then publish at arcadelab.ai/publish.