🎮ArcadeLab

זירת החיסולים

by MysticFlare49
1588 lines62.3 KB
▶ Play
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>זירת החיסולים</title>
<style>
:root{
  --bg:#10131f; --panel:#1b2032; --panel2:#242b47; --line:#323a5e;
  --gold:#ffc93c; --gold-d:#c69413;
  --green:#35d07f; --green-d:#1e8f57;
  --red:#ff5b5b; --cyan:#3ec7f0; --violet:#a78bfa;
  --text:#f4f6ff; --dim:#9aa3c7;
}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;
  user-select:none;-webkit-user-select:none;-webkit-touch-callout:none;}
html,body{height:100%;overflow:hidden;overscroll-behavior:none;}
body{position:fixed;inset:0;width:100%;background:var(--bg);color:var(--text);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Heebo",Arial,sans-serif;
  touch-action:none;}
button{font-family:inherit;cursor:pointer;color:var(--text);}
input{user-select:text;-webkit-user-select:text;}

/* ---------- screens ---------- */
.screen{position:absolute;inset:0;display:none;flex-direction:column;}
.screen.active{display:flex;}
.hidden{display:none !important;}

/* ---------- home ---------- */
#home{padding:calc(14px + env(safe-area-inset-top)) 18px calc(20px + env(safe-area-inset-bottom));align-items:center;}
.top-row{width:100%;display:flex;justify-content:center;position:relative;}
#soundBtn{position:absolute;left:0;top:0;}
.icon-btn{width:44px;height:44px;border-radius:14px;background:var(--panel);
  border:2px solid var(--line);font-size:1.2rem;display:flex;align-items:center;justify-content:center;}
#trophyPill{display:flex;align-items:center;gap:10px;background:var(--panel);
  border:2px solid var(--line);border-radius:999px;padding:10px 20px;
  font-size:1.05rem;font-weight:800;}
#trophyPill:active{transform:scale(.97);}
.tp-cup{font-size:1.2rem;}
#trophyCount{color:var(--gold);}
.tp-div{width:2px;height:18px;background:var(--line);border-radius:2px;}
.tp-name{color:var(--dim);max-width:34vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}

.home-mid{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;text-align:center;width:100%;}
.title-emojis{display:flex;gap:18px;align-items:flex-end;}
.title-emojis span{display:inline-block;animation:bob 2.4s ease-in-out infinite;line-height:0;}
.title-emojis span:nth-child(2){animation-delay:.35s;}
.title-emojis span:nth-child(3){animation-delay:.7s;}
@keyframes bob{50%{transform:translateY(-9px);}}
.game-title{font-size:clamp(2.7rem,14vw,4.4rem);font-weight:900;line-height:1.02;
  letter-spacing:-1px;color:var(--gold);
  text-shadow:0 5px 0 rgba(0,0,0,.55),0 14px 40px rgba(255,201,60,.16);}
.subtitle{color:var(--dim);font-weight:700;}
.selected-info{background:var(--panel);border:2px solid var(--line);padding:9px 18px;border-radius:14px;
  font-weight:700;display:flex;align-items:center;gap:9px;}
.next-unlock{color:var(--gold);font-size:.92rem;font-weight:800;min-height:1.2em;}

.home-bottom{width:100%;display:flex;gap:10px;align-items:stretch;}
.btn{border:none;border-radius:18px;font-weight:900;transition:transform .06s;}
.btn:active{transform:translateY(4px);border-bottom-width:2px !important;}
.btn-green{background:var(--green);border-bottom:6px solid var(--green-d);color:#06281a;}
.btn-panel{background:var(--panel2);border-bottom:6px solid #10141f;color:var(--text);}
#startBtn{flex:1.7;font-size:1.7rem;padding:22px 8px;}
.side-btn{flex:1;font-size:1rem;padding:14px 6px;display:flex;flex-direction:column;
  align-items:center;justify-content:center;gap:4px;}
.side-btn .sb-ic{font-size:1.35rem;}

/* ---------- characters / trophies ---------- */
#chars,#trophies{padding:calc(12px + env(safe-area-inset-top)) 16px 0;}
.chars-head{display:flex;align-items:center;gap:12px;padding-bottom:12px;}
.chars-head h2{flex:1;font-size:1.5rem;font-weight:900;}
.chars-trophy{font-weight:800;color:var(--gold);background:var(--panel);
  border:2px solid var(--line);border-radius:999px;padding:8px 14px;}
.char-list{flex:1;overflow-y:auto;touch-action:pan-y;display:flex;flex-direction:column;gap:12px;
  padding-bottom:calc(20px + env(safe-area-inset-bottom));}
.char-card{display:flex;gap:14px;background:var(--panel);border:2.5px solid var(--line);
  border-radius:20px;padding:14px;text-align:right;width:100%;align-items:center;}
.char-card.selected{border-color:var(--gold);box-shadow:0 0 0 3px rgba(255,201,60,.16);}
.char-card.locked{opacity:.8;}
.cc-emoji{font-size:2rem;width:76px;height:76px;display:flex;align-items:center;justify-content:center;
  background:#141a2e;border-radius:18px;border:2.5px solid var(--cc,#555);flex:0 0 auto;}
.cc-body{flex:1;min-width:0;}
.cc-name{font-size:1.25rem;font-weight:900;color:var(--cc,#fff);display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.cc-badge{font-size:.68rem;background:var(--gold);color:#221a00;padding:3px 9px;border-radius:99px;font-weight:900;}
.cc-line{color:var(--dim);font-size:.85rem;margin-top:3px;font-weight:600;}
.cc-lock{margin-top:9px;color:var(--gold);font-weight:800;font-size:.9rem;}
.cc-prog{height:9px;background:#000;border-radius:5px;margin-top:6px;overflow:hidden;}
.cc-prog div{height:100%;background:var(--gold);border-radius:5px;}
.cc-progtxt{font-size:.75rem;color:var(--dim);margin-top:4px;font-weight:700;}
.shake{animation:shk .35s;}
@keyframes shk{25%{transform:translateX(-6px);}50%{transform:translateX(6px);}75%{transform:translateX(-4px);}}

.trophy-body{flex:1;display:flex;flex-direction:column;align-items:center;gap:13px;
  padding:8px 4px calc(24px + env(safe-area-inset-bottom));overflow-y:auto;touch-action:pan-y;width:100%;}
.big-cup{font-size:4.2rem;line-height:1;margin-top:6px;}
.big-count{font-size:3rem;font-weight:900;color:var(--gold);line-height:1;
  text-shadow:0 4px 0 rgba(0,0,0,.5);}
.wins-line{color:var(--dim);font-weight:700;}
.name-row{display:flex;align-items:center;gap:10px;background:var(--panel);
  border:2px solid var(--line);border-radius:16px;padding:10px 14px;font-weight:800;max-width:100%;}
#trophyNameLabel{max-width:36vw;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.small-btn{font-size:.88rem;padding:8px 14px;border-radius:12px;border-bottom-width:4px !important;}
.unlock-progress{width:100%;display:flex;flex-direction:column;gap:10px;margin-top:4px;}
.prog-row{display:flex;gap:12px;align-items:center;background:var(--panel);
  border:2px solid var(--line);border-radius:16px;padding:12px;width:100%;}
.prog-body{flex:1;min-width:0;}
.prog-name{font-weight:800;font-size:.95rem;}

/* ---------- game ---------- */
#game{background:#0c0f1a;}
#cv{position:absolute;inset:0;}
#hud{position:absolute;top:calc(8px + env(safe-area-inset-top));left:0;right:0;
  display:flex;justify-content:center;gap:7px;pointer-events:none;z-index:5;padding:0 54px;}
.chip{background:rgba(18,22,40,.88);border:1.5px solid var(--line);border-radius:12px;
  padding:4px 7px;display:flex;align-items:center;gap:5px;min-width:0;}
.chip.me{border-color:var(--gold);}
.chip.dead{opacity:.45;filter:grayscale(1);}
.chip-n{font-size:.72rem;font-weight:800;max-width:52px;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.chip-bar{width:30px;height:6px;background:#000;border-radius:3px;overflow:hidden;}
.chip-bar div{height:100%;width:100%;background:var(--green);border-radius:3px;}
#exitBtn{position:absolute;top:calc(8px + env(safe-area-inset-top));left:10px;
  width:38px;height:38px;border-radius:12px;background:rgba(18,22,40,.88);
  border:1.5px solid var(--line);color:var(--dim);font-size:1rem;font-weight:900;z-index:6;}

#joyZone{position:absolute;left:0;bottom:0;width:56%;height:72%;touch-action:none;z-index:4;}
#joyBase{position:absolute;width:118px;height:118px;border-radius:50%;
  background:rgba(255,255,255,.05);border:2px solid rgba(255,255,255,.13);
  left:26px;bottom:calc(40px + env(safe-area-inset-bottom));pointer-events:none;}
#joyBase.on{background:rgba(255,255,255,.09);}
#joyKnob{position:absolute;width:56px;height:56px;border-radius:50%;
  background:rgba(255,255,255,.26);border:2px solid rgba(255,255,255,.4);
  left:50%;top:50%;transform:translate(-50%,-50%);}

#fireBtn{position:absolute;right:20px;bottom:calc(32px + env(safe-area-inset-bottom));
  width:96px;height:96px;border-radius:50%;font-size:2.4rem;z-index:4;
  background:linear-gradient(180deg,#ff6b5e,#e04338);border:none;
  border-bottom:6px solid #a52c24;box-shadow:0 8px 20px rgba(0,0,0,.45);touch-action:none;}
#fireBtn.press{transform:scale(.92);border-bottom-width:3px;}
#powerBtn{position:absolute;right:34px;bottom:calc(150px + env(safe-area-inset-bottom));
  width:74px;height:74px;border-radius:50%;padding:6px;border:none;z-index:4;touch-action:none;
  background:conic-gradient(var(--gold) var(--pct,0deg), #262c49 var(--pct,0deg));}
#powerBtn .pin{width:100%;height:100%;border-radius:50%;background:#141a2e;
  display:flex;align-items:center;justify-content:center;font-size:1.7rem;opacity:.5;}
#powerBtn.ready .pin{opacity:1;}
#powerBtn.ready{animation:ppulse 1s infinite;box-shadow:0 0 24px rgba(255,201,60,.6);}
@keyframes ppulse{50%{transform:scale(1.09);}}

#countWrap{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;
  justify-content:center;gap:14px;pointer-events:none;z-index:8;}
#cdNum{font-size:min(28vw,130px);font-weight:900;color:var(--gold);
  text-shadow:0 6px 0 rgba(0,0,0,.5);line-height:1;}
#cdNum.pop{animation:cdpop .5s;}
@keyframes cdpop{0%{transform:scale(1.7);opacity:.2;}100%{transform:scale(1);opacity:1;}}
#cdHint{background:rgba(18,22,40,.88);border:1.5px solid var(--line);border-radius:14px;
  padding:9px 14px;font-weight:800;color:var(--dim);font-size:.88rem;max-width:92vw;text-align:center;}

/* ---------- overlay / modal ---------- */
.overlay{position:absolute;inset:0;background:rgba(8,10,20,.74);display:flex;
  align-items:center;justify-content:center;z-index:20;padding:20px;}
.overlay-card{background:var(--panel);border:2.5px solid var(--line);border-radius:24px;
  padding:26px 22px;width:min(370px,92vw);display:flex;flex-direction:column;gap:13px;
  text-align:center;position:relative;z-index:2;}
.overlay-card .btn{padding:15px 10px;font-size:1.2rem;}
.ov-emoji{font-size:4.2rem;line-height:1;animation:cdpop .5s;}
#ovTitle{font-size:1.65rem;font-weight:900;color:var(--gold);}
#ovSub{color:var(--text);font-weight:600;line-height:1.55;}
.ov-total{color:var(--dim);font-size:.9rem;}
.ov-unlock{background:rgba(255,201,60,.12);border:1.5px solid var(--gold);
  border-radius:14px;padding:11px;color:var(--gold);font-weight:800;line-height:1.5;
  display:flex;flex-direction:column;gap:8px;}
.ov-unlock-row{display:flex;align-items:center;justify-content:center;gap:10px;}
#confetti{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:1;}
#confetti span{position:absolute;top:-36px;animation:fall linear forwards;}
@keyframes fall{to{transform:translateY(112vh) rotate(560deg);opacity:.75;}}
#nameInput{font-size:1.2rem;font-weight:800;padding:14px;border-radius:14px;
  border:2px solid var(--line);background:#12172a;color:var(--text);text-align:center;outline:none;width:100%;}
#nameInput:focus{border-color:var(--gold);}
</style>
</head>
<body>

<!-- ============ HOME ============ -->
<div id="home" class="screen active">
  <div class="top-row">
    <button id="soundBtn" class="icon-btn">🔊</button>
    <button id="trophyPill">
      <span class="tp-cup">🏆</span><span id="trophyCount">0</span>
      <span class="tp-div"></span>
      <span id="playerNameLabel" class="tp-name">בחרו שם ✏️</span>
    </button>
  </div>
  <div class="home-mid">
    <div class="title-emojis" id="heroRow"></div>
    <h1 class="game-title">זירת<br>החיסולים</h1>
    <p class="subtitle">3 לוחמים. מסלול אחד ארוך. רק אחד שורד.</p>
    <div id="selectedInfo" class="selected-info"></div>
    <div id="nextUnlock" class="next-unlock"></div>
  </div>
  <div class="home-bottom">
    <button id="trophiesBtn" class="btn btn-panel side-btn"><span class="sb-ic">🏆</span>גביעים</button>
    <button id="startBtn" class="btn btn-green">⚔️ התחלה</button>
    <button id="charsBtn" class="btn btn-panel side-btn"><span class="sb-ic">🎭</span>דמויות</button>
  </div>
</div>

<!-- ============ CHARACTERS ============ -->
<div id="chars" class="screen">
  <div class="chars-head">
    <button id="backBtn" class="icon-btn">→</button>
    <h2>הדמויות</h2>
    <div class="chars-trophy">🏆 <span id="trophyCount2">0</span></div>
  </div>
  <div id="charList" class="char-list"></div>
</div>

<!-- ============ TROPHIES ============ -->
<div id="trophies" class="screen">
  <div class="chars-head">
    <button id="backBtn2" class="icon-btn">→</button>
    <h2>הגביעים שלי</h2>
  </div>
  <div class="trophy-body">
    <div class="big-cup">🏆</div>
    <div id="bigTrophyCount" class="big-count">0</div>
    <div id="winsLine" class="wins-line"></div>
    <div class="name-row">
      <span>👤</span><span id="trophyNameLabel"></span>
      <button id="editNameBtn" class="btn btn-panel small-btn">עריכה ✏️</button>
    </div>
    <div id="unlockProgress" class="unlock-progress"></div>
  </div>
</div>

<!-- ============ GAME ============ -->
<div id="game" class="screen">
  <canvas id="cv"></canvas>
  <div id="hud"></div>
  <button id="exitBtn">✕</button>
  <div id="joyZone">
    <div id="joyBase"><div id="joyKnob"></div></div>
  </div>
  <button id="powerBtn"><span class="pin"><span id="powerIcon">💥</span></span></button>
  <button id="fireBtn">👊</button>
  <div id="countWrap" class="hidden">
    <div id="cdNum"></div>
    <div id="cdHint"></div>
  </div>
  <div id="overlay" class="overlay hidden">
    <div id="confetti"></div>
    <div class="overlay-card">
      <div id="ovEmoji" class="ov-emoji">🏆</div>
      <h2 id="ovTitle"></h2>
      <div id="ovSub"></div>
      <div id="ovUnlock" class="ov-unlock hidden"></div>
      <button id="againBtn" class="btn btn-green">⚔️ עוד קרב!</button>
      <button id="homeBtn" class="btn btn-panel">🏠 דף הבית</button>
    </div>
  </div>
</div>

<!-- ============ NAME MODAL ============ -->
<div id="nameModal" class="overlay hidden">
  <div class="overlay-card">
    <h2 style="font-weight:900;">✏️ מה השם שלכם?</h2>
    <input id="nameInput" maxlength="14" placeholder="השם שלכם...">
    <button id="saveNameBtn" class="btn btn-green">שמירה ✓</button>
    <button id="cancelNameBtn" class="btn btn-panel">ביטול</button>
  </div>
</div>

<script>
'use strict';
/* ================= helpers ================= */
var $=function(id){return document.getElementById(id);};
var clamp=function(v,a,b){return v<a?a:(v>b?b:v);};
var rand=function(a,b){return a+Math.random()*(b-a);};
var fmt=function(n){return n.toLocaleString();};
var distF=function(a,b){return Math.hypot(b.x-a.x,b.y-a.y);};
var angTo=function(a,b){return Math.atan2(b.y-a.y,b.x-a.x);};
function shuffle(arr){for(var i=arr.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1));var t=arr[i];arr[i]=arr[j];arr[j]=t;}return arr;}
function esc(s){return String(s).replace(/[&<>"]/g,function(c){return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c];});}

/* ================= save (immediate, robust) ================= */
var SAVE_KEY='zirat-hahisulim-save';
var save={name:'',trophies:0,selected:'thief',sound:true};
function applySave(raw){
  try{
    var d=JSON.parse(raw);
    if(typeof d.name==='string')save.name=d.name;
    if(typeof d.trophies==='number')save.trophies=d.trophies;
    if(typeof d.selected==='string')save.selected=d.selected;
    if(typeof d.sound==='boolean')save.sound=d.sound;
  }catch(e){}
}
function loadSave(){
  if(window.storage){
    return window.storage.get(SAVE_KEY).then(function(r){
      if(r&&r.value)applySave(r.value);
    }).catch(function(){});
  }
  try{
    var raw=window.localStorage.getItem(SAVE_KEY);
    if(raw)applySave(raw);
  }catch(e){}
  return Promise.resolve();
}
function persist(){
  var data=JSON.stringify(save);
  try{
    if(window.storage){window.storage.set(SAVE_KEY,data).catch(function(){});return;}
  }catch(e){}
  try{window.localStorage.setItem(SAVE_KEY,data);}catch(e){}
}
document.addEventListener('visibilitychange',function(){
  if(document.visibilityState==='hidden')persist();
});

/* ================= characters ================= */
var CHARS=[
 {id:'thief',name:'הגנב',cost:0,color:'#a78bfa',type:'melee',
  dmg:200,rate:3.5,spd:1,atkEmoji:'👊',
  power:{emoji:'🦵',name:'בעיטת־על',charge:10},
  lines:['👊 בוקס מקרוב — 200 נזק','🦵 מכת כוח: בעיטה — 750 נזק','⏱️ נטענת ב־10 שניות']},
 {id:'robot',name:'הרובוט',cost:1000,color:'#3ec7f0',type:'shoot',
  dmg:100,rate:7,spd:1,atkEmoji:'🔩',projKind:'bolt',
  power:{emoji:'⚡',name:'לייזר־ענק',charge:14},
  lines:['🔩 יורה ברגים — 100 נזק','⚡ מכת כוח: לייזר על כל המגרש — 500 לכל היריבים','⏱️ נטענת ב־14 שניות']},
 {id:'samurai',name:'הסמוראי',cost:2000,color:'#ff5b5b',type:'shoot',
  dmg:100,rate:7,spd:1.3,atkEmoji:'🔪',projKind:'knife',
  power:{emoji:'⚔️',name:'חרב־החיסול',charge:20},
  lines:['🔪 יורה סכינים — 100 נזק','💨 הכי מהיר בזירה!','⚔️ מכת כוח: חרב — חיסול מיידי מקרוב','⏱️ נטענת ב־20 שניות']},
];
var charById=function(id){for(var i=0;i<CHARS.length;i++)if(CHARS[i].id===id)return CHARS[i];return CHARS[0];};
var unlocked=function(c){return save.trophies>=c.cost;};
var BOT_NAMES=['צל','רעם','עקרב','ברזל','קוברה','פנתר','טורנדו','ניצוץ'];

/* ================= character art ================= */
function rrG(g,x,y,w,h,rad){
  g.beginPath();
  if(g.roundRect)g.roundRect(x,y,w,h,rad);
  else g.rect(x,y,w,h);
}
function drawCharacter(g,def,r,aim,o){
  o=o||{};
  var t=o.time||0;
  var sw=o.moving?Math.sin((o.walk||0)*10):0;
  g.save();
  g.translate(o.x||0,o.y||0);
  g.fillStyle='rgba(0,0,0,.25)';
  g.beginPath();g.ellipse(0,r*.45,r*1.02,r*.55,0,0,6.2832);g.fill();
  g.rotate(aim);
  g.lineCap='round';g.lineJoin='round';
  if(def.id==='thief')drawThief(g,r,t,sw);
  else if(def.id==='robot')drawRobot(g,r,t,sw);
  else drawSamurai(g,r,t,sw);
  g.restore();
}
function drawThief(g,r,t,sw){
  g.strokeStyle='#8b6ae0';g.lineWidth=r*.22;
  g.beginPath();g.moveTo(-r*.5,-r*.12);
  g.quadraticCurveTo(-r*1.05,-r*.3+Math.sin(t*6)*r*.1,-r*1.45,-r*.1+Math.sin(t*5)*r*.16);g.stroke();
  g.beginPath();g.moveTo(-r*.5,r*.12);
  g.quadraticCurveTo(-r*1.0,r*.32+Math.cos(t*5)*r*.1,-r*1.4,r*.3+Math.cos(t*6)*r*.14);g.stroke();
  g.fillStyle='#43356e';g.strokeStyle='#171028';g.lineWidth=r*.09;
  g.beginPath();g.arc(r*.68,-r*.6+sw*r*.14,r*.34,0,6.2832);g.fill();g.stroke();
  g.beginPath();g.arc(r*.68,r*.6-sw*r*.14,r*.34,0,6.2832);g.fill();g.stroke();
  g.fillStyle='#2b2148';g.beginPath();g.arc(0,0,r,0,6.2832);g.fill();
  g.lineWidth=r*.12;g.strokeStyle='#171028';g.beginPath();g.arc(0,0,r,0,6.2832);g.stroke();
  g.strokeStyle='#a78bfa';g.lineWidth=r*.18;
  g.beginPath();g.arc(0,0,r*.55,2.4,3.9);g.stroke();
  g.fillStyle='#382c60';g.beginPath();g.arc(0,0,r*.66,0,6.2832);g.fill();
  g.lineWidth=r*.08;g.strokeStyle='#171028';g.beginPath();g.arc(0,0,r*.66,0,6.2832);g.stroke();
  g.fillStyle='#f2cba2';
  rrG(g,r*.12,-r*.4,r*.44,r*.8,r*.2);g.fill();
  g.fillStyle='#1a1430';
  g.beginPath();g.arc(r*.36,-r*.18,r*.09,0,6.2832);g.fill();
  g.beginPath();g.arc(r*.36,r*.18,r*.09,0,6.2832);g.fill();
  g.fillStyle='#a78bfa';g.beginPath();g.arc(-r*.58,0,r*.13,0,6.2832);g.fill();
}
function drawRobot(g,r,t,sw){
  g.strokeStyle='#57648f';g.lineWidth=r*.11;
  g.beginPath();g.moveTo(-r*.55,0);g.lineTo(-r*1.05,0);g.stroke();
  var blink=Math.sin(t*7)>0;
  g.fillStyle=blink?'#ff5b5b':'#7a2f34';
  g.beginPath();g.arc(-r*1.14,0,r*.14,0,6.2832);g.fill();
  g.fillStyle='#49557c';rrG(g,r*.4,-r*.16,r*.8,r*.32,r*.12);g.fill();
  g.fillStyle='#222a44';g.beginPath();g.arc(r*1.2,0,r*.15,0,6.2832);g.fill();
  g.fillStyle='#8b97be';g.strokeStyle='#49557c';g.lineWidth=r*.09;
  g.beginPath();g.arc(r*.58,-r*.62+sw*r*.12,r*.3,0,6.2832);g.fill();g.stroke();
  g.beginPath();g.arc(r*.58,r*.62-sw*r*.12,r*.3,0,6.2832);g.fill();g.stroke();
  g.fillStyle='#8b97be';g.beginPath();g.arc(0,0,r,0,6.2832);g.fill();
  g.lineWidth=r*.12;g.strokeStyle='#49557c';g.beginPath();g.arc(0,0,r,0,6.2832);g.stroke();
  g.fillStyle='#6d7aa6';rrG(g,-r*.55,-r*.44,r*.8,r*.88,r*.2);g.fill();
  g.strokeStyle='#49557c';g.lineWidth=r*.07;rrG(g,-r*.55,-r*.44,r*.8,r*.88,r*.2);g.stroke();
  g.fillStyle='#3c4666';
  var riv=[[-r*.42,-r*.3],[-r*.42,r*.3],[r*.1,-r*.3],[r*.1,r*.3]];
  for(var i=0;i<riv.length;i++){g.beginPath();g.arc(riv[i][0],riv[i][1],r*.06,0,6.2832);g.fill();}
  g.fillStyle='#a9b8e0';g.beginPath();g.arc(0,0,r*.6,0,6.2832);g.fill();
  g.lineWidth=r*.08;g.strokeStyle='#49557c';g.beginPath();g.arc(0,0,r*.6,0,6.2832);g.stroke();
  g.fillStyle='#0f2c52';rrG(g,r*.08,-r*.32,r*.44,r*.64,r*.16);g.fill();
  g.fillStyle='#5fe3ff';
  g.beginPath();g.arc(r*.3,-r*.14,r*.08,0,6.2832);g.fill();
  g.beginPath();g.arc(r*.3,r*.14,r*.08,0,6.2832);g.fill();
}
function drawSamurai(g,r,t,sw){
  g.strokeStyle='#33374a';g.lineWidth=r*.14;
  g.beginPath();g.moveTo(-r*.15,-r*.8);g.lineTo(-r*1.2,r*.05);g.stroke();
  g.strokeStyle='#e0b04c';g.lineWidth=r*.15;
  g.beginPath();g.moveTo(-r*.15,-r*.8);g.lineTo(-r*.45,-r*.56);g.stroke();
  g.fillStyle='#8f2b2b';g.strokeStyle='#5e1717';g.lineWidth=r*.08;
  g.beginPath();g.ellipse(-r*.08,-r*.82,r*.5,r*.3,0,0,6.2832);g.fill();g.stroke();
  g.beginPath();g.ellipse(-r*.08,r*.82,r*.5,r*.3,0,0,6.2832);g.fill();g.stroke();
  g.fillStyle='#a83535';g.strokeStyle='#5e1717';g.lineWidth=r*.09;
  g.beginPath();g.arc(r*.66,-r*.58+sw*r*.13,r*.31,0,6.2832);g.fill();g.stroke();
  g.beginPath();g.arc(r*.66,r*.58-sw*r*.13,r*.31,0,6.2832);g.fill();g.stroke();
  g.fillStyle='#e6ecf7';
  g.beginPath();g.moveTo(r*.86,r*.5);g.lineTo(r*1.5,r*.6);g.lineTo(r*.86,r*.74);g.closePath();g.fill();
  g.fillStyle='#c73e3e';g.beginPath();g.arc(0,0,r,0,6.2832);g.fill();
  g.lineWidth=r*.12;g.strokeStyle='#7e1f1f';g.beginPath();g.arc(0,0,r,0,6.2832);g.stroke();
  g.strokeStyle='#e8b64c';g.lineWidth=r*.1;
  g.beginPath();g.arc(0,0,r*.58,2.35,3.95);g.stroke();
  g.fillStyle='#f3ede2';g.beginPath();g.arc(0,0,r*.62,0,6.2832);g.fill();
  g.lineWidth=r*.08;g.strokeStyle='#7e1f1f';g.beginPath();g.arc(0,0,r*.62,0,6.2832);g.stroke();
  g.strokeStyle='#20202c';g.lineWidth=r*.1;
  g.beginPath();g.moveTo(r*.14,-r*.3);g.lineTo(r*.42,-r*.13);g.stroke();
  g.beginPath();g.moveTo(r*.14,r*.3);g.lineTo(r*.42,r*.13);g.stroke();
  g.strokeStyle='#c73e3e';g.lineWidth=r*.07;
  g.beginPath();g.moveTo(-r*.06,-r*.46);g.lineTo(-r*.3,-r*.26);g.stroke();
  g.beginPath();g.moveTo(-r*.06,r*.46);g.lineTo(-r*.3,r*.26);g.stroke();
  g.strokeStyle='#e8b64c';g.lineWidth=r*.13;
  g.beginPath();g.arc(-r*.5,0,r*.42,2.0,4.28);g.stroke();
}
function charIcon(def,px){
  var c=document.createElement('canvas');
  c.width=px*2;c.height=px*2;
  c.style.width=px+'px';c.style.height=px+'px';
  var g=c.getContext('2d');
  g.scale(2,2);
  drawCharacter(g,def,px*0.30,-Math.PI/2,{x:px/2,y:px/2+px*0.04,time:1.3,walk:0,moving:false});
  return c;
}

/* ================= sound ================= */
var actx=null;
document.addEventListener('pointerdown',function(){
  if(!actx){try{actx=new (window.AudioContext||window.webkitAudioContext)();}catch(e){}}
  else if(actx.state==='suspended'){actx.resume().catch(function(){});}
},true);
function tone(freq,dur,type,vol,slideTo){
  if(!save.sound||!actx)return;
  try{
    var o=actx.createOscillator(),g=actx.createGain(),t=actx.currentTime;
    o.type=type||'square';
    o.frequency.setValueAtTime(freq,t);
    if(slideTo)o.frequency.exponentialRampToValueAtTime(Math.max(20,slideTo),t+dur);
    g.gain.setValueAtTime(vol||.12,t);
    g.gain.exponentialRampToValueAtTime(.0001,t+dur);
    o.connect(g);g.connect(actx.destination);
    o.start(t);o.stop(t+dur+.03);
  }catch(e){}
}
function sfx(n){
  switch(n){
    case 'shoot':tone(640,.05,'square',.04,220);break;
    case 'shoot2':tone(480,.05,'square',.025,180);break;
    case 'punch':tone(150,.08,'triangle',.2,70);break;
    case 'whiff':tone(320,.05,'sine',.035,140);break;
    case 'hit':tone(230,.06,'square',.1,100);break;
    case 'hurt':tone(170,.11,'sawtooth',.13,70);break;
    case 'boom':tone(120,.32,'sawtooth',.22,35);tone(65,.4,'square',.16,28);break;
    case 'kick':tone(95,.26,'sawtooth',.28,32);break;
    case 'sword':tone(950,.14,'square',.16,110);break;
    case 'laser':tone(310,.6,'sawtooth',.2,55);break;
    case 'ready':tone(880,.09,'sine',.14);setTimeout(function(){tone(1175,.13,'sine',.14);},95);break;
    case 'select':tone(660,.08,'sine',.12,760);break;
    case 'deny':tone(190,.12,'square',.1,120);break;
    case 'count':tone(440,.09,'sine',.12);break;
    case 'go':tone(523,.12,'square',.14);setTimeout(function(){tone(784,.2,'square',.14);},120);break;
    case 'win':[523,659,784,1046].forEach(function(f,i){setTimeout(function(){tone(f,.18,'square',.14);},i*130);});break;
    case 'lose':[392,311,262].forEach(function(f,i){setTimeout(function(){tone(f,.22,'sine',.13);},i*160);});break;
  }
}

/* ================= screens / home UI ================= */
function show(id){
  var s=document.querySelectorAll('.screen');
  for(var i=0;i<s.length;i++)s[i].classList.remove('active');
  $(id).classList.add('active');
}
function refreshHome(){
  $('trophyCount').textContent=fmt(save.trophies);
  $('trophyCount2').textContent=fmt(save.trophies);
  $('playerNameLabel').textContent=save.name?save.name:'בחרו שם ✏️';
  var c=charById(save.selected);
  var si=$('selectedInfo');si.innerHTML='';
  si.appendChild(document.createTextNode('משחקים בתור: '));
  var b=document.createElement('b');b.textContent=c.name;b.style.color=c.color;
  si.appendChild(b);
  si.appendChild(charIcon(c,24));
  var next=null;
  for(var i=0;i<CHARS.length;i++)if(!unlocked(CHARS[i])){next=CHARS[i];break;}
  $('nextUnlock').textContent=next
    ?('עוד '+fmt(next.cost-save.trophies)+' גביעים ו'+next.name+' שלכם!')
    :'כל הדמויות פתוחות! 🎉';
  $('soundBtn').textContent=save.sound?'🔊':'🔇';
}
function renderChars(){
  var list=$('charList');list.innerHTML='';
  CHARS.forEach(function(c){
    var un=unlocked(c);
    var card=document.createElement('button');
    card.className='char-card'+(un?'':' locked')+(save.selected===c.id?' selected':'');
    card.style.setProperty('--cc',c.color);
    var em=document.createElement('div');em.className='cc-emoji';
    if(un)em.appendChild(charIcon(c,54));
    else em.textContent='🔒';
    var body=document.createElement('div');body.className='cc-body';
    var html='<div class="cc-name">'+esc(c.name)+(save.selected===c.id?' <span class="cc-badge">נבחר ✓</span>':'')+'</div>';
    html+=c.lines.map(function(l){return '<div class="cc-line">'+l+'</div>';}).join('');
    if(!un){
      var pct=Math.min(100,Math.round(save.trophies/c.cost*100));
      html+='<div class="cc-lock">🔒 נפתח ב־'+fmt(c.cost)+' גביעים'
        +'<div class="cc-prog"><div style="width:'+pct+'%"></div></div>'
        +'<div class="cc-progtxt">'+fmt(save.trophies)+' / '+fmt(c.cost)+'</div></div>';
    }
    body.innerHTML=html;
    card.appendChild(em);card.appendChild(body);
    card.addEventListener('click',function(){
      if(un){save.selected=c.id;persist();renderChars();refreshHome();sfx('select');}
      else{card.classList.remove('shake');void card.offsetWidth;card.classList.add('shake');sfx('deny');}
    });
    list.appendChild(card);
  });
}
function refreshTrophies(){
  $('bigTrophyCount').textContent=fmt(save.trophies);
  $('winsLine').textContent='ניצחונות במקום הראשון: '+fmt(Math.floor(save.trophies/10));
  $('trophyNameLabel').textContent=save.name?save.name:'עוד אין שם';
  var up=$('unlockProgress');up.innerHTML='';
  var lockedChars=CHARS.filter(function(c){return !unlocked(c);});
  if(!lockedChars.length){
    var d=document.createElement('div');d.className='prog-row';
    d.innerHTML='<div class="prog-name" style="color:var(--gold)">כל הדמויות פתוחות! 🎉</div>';
    up.appendChild(d);
  }else lockedChars.forEach(function(c){
    var row=document.createElement('div');row.className='prog-row';
    row.appendChild(charIcon(c,36));
    var body=document.createElement('div');body.className='prog-body';
    var pct=Math.min(100,Math.round(save.trophies/c.cost*100));
    body.innerHTML='<div class="prog-name" style="color:'+c.color+'">'+esc(c.name)+' — נפתח ב־'+fmt(c.cost)+' 🏆</div>'
      +'<div class="cc-prog"><div style="width:'+pct+'%"></div></div>'
      +'<div class="cc-progtxt">'+fmt(save.trophies)+' / '+fmt(c.cost)+'</div>';
    row.appendChild(body);
    up.appendChild(row);
  });
}
$('startBtn').addEventListener('click',function(){startMatch();});
$('charsBtn').addEventListener('click',function(){renderChars();show('chars');});
$('trophiesBtn').addEventListener('click',function(){refreshTrophies();show('trophies');});
$('backBtn').addEventListener('click',function(){refreshHome();show('home');});
$('backBtn2').addEventListener('click',function(){refreshHome();show('home');});
$('soundBtn').addEventListener('click',function(){save.sound=!save.sound;persist();refreshHome();if(save.sound)sfx('select');});

/* name modal */
function openNameModal(){
  $('nameInput').value=save.name;
  $('nameModal').classList.remove('hidden');
  setTimeout(function(){$('nameInput').focus();},60);
}
$('trophyPill').addEventListener('click',openNameModal);
$('editNameBtn').addEventListener('click',openNameModal);
function saveName(){
  save.name=$('nameInput').value.trim().slice(0,14);
  persist();refreshHome();refreshTrophies();
  $('nameModal').classList.add('hidden');
  sfx('select');
}
$('saveNameBtn').addEventListener('click',saveName);
$('nameInput').addEventListener('keydown',function(e){if(e.key==='Enter')saveName();});
$('cancelNameBtn').addEventListener('click',function(){$('nameModal').classList.add('hidden');});

/* ================= canvas / world ================= */
var cv=$('cv'),ctx=cv.getContext('2d');
var W=0,H=0,S=1,dpr=1;
var world={w:0,h:0};
var cam={x:0,y:0};
var walls=[],bushes=[];
var WALL_DEFS=[
 {fx:.62,fy:.095,len:.40,h:true},
 {fx:.15,fy:.20,len:.34,h:false},
 {fx:.60,fy:.28,len:.36,h:true},
 {fx:.85,fy:.385,len:.30,h:false},
 {fx:.35,fy:.47,len:.40,h:true},
 {fx:.15,fy:.575,len:.30,h:false},
 {fx:.65,fy:.66,len:.36,h:true},
 {fx:.85,fy:.765,len:.28,h:false},
 {fx:.40,fy:.86,len:.38,h:true}
];
var BUSH_DEFS=[
 {fx:.38,fy:.045,bw:.30,bh:.17},
 {fx:.80,fy:.155,bw:.30,bh:.20},
 {fx:.22,fy:.335,bw:.32,bh:.22},
 {fx:.76,fy:.53,bw:.28,bh:.20},
 {fx:.24,fy:.705,bw:.32,bh:.22},
 {fx:.62,fy:.925,bw:.30,bh:.18}
];
function buildWalls(){
  var th=26*S;
  walls=WALL_DEFS.map(function(d){
    var L=d.len*world.w;
    var w=d.h?L:th,hh=d.h?th:L;
    return {x:d.fx*world.w-w/2,y:d.fy*world.h-hh/2,w:w,h:hh};
  });
}
function buildBushes(){
  bushes=BUSH_DEFS.map(function(d,idx){
    var bw=d.bw*world.w,bh=d.bh*world.w;
    var b={x:d.fx*world.w-bw/2,y:d.fy*world.h-bh/2,w:bw,h:bh,blobs:[]};
    var s=idx*127.31+7.77;
    function rnd(){s=Math.sin(s)*43758.5453;return s-Math.floor(s);}
    var n=Math.max(8,Math.round(bw*bh/(2400*S*S)));
    for(var i=0;i<n;i++){
      b.blobs.push({dx:rnd()*bw,dy:rnd()*bh,r:(0.55+rnd()*0.65)*20*S,sh:Math.floor(rnd()*3)});
    }
    return b;
  });
}
function bushAt(x,y){
  for(var i=0;i<bushes.length;i++){
    var b=bushes[i];
    if(x>b.x+5&&x<b.x+b.w-5&&y>b.y+5&&y<b.y+b.h-5)return i;
  }
  return -1;
}
function resizeCanvas(){
  dpr=Math.min(window.devicePixelRatio||1,2);
  var ow=world.w,oh=world.h;
  W=window.innerWidth;H=window.innerHeight;
  cv.width=Math.round(W*dpr);cv.height=Math.round(H*dpr);
  cv.style.width=W+'px';cv.style.height=H+'px';
  ctx.setTransform(dpr,0,0,dpr,0,0);
  S=clamp(Math.min(W,H)/480,0.8,1.7);
  world.w=W;world.h=Math.round(H*2.6);
  if(G.running&&ow>0){
    var rx=world.w/ow,ry=world.h/oh;
    G.fighters.forEach(function(f){f.x*=rx;f.y*=ry;});
    G.projs.forEach(function(p){p.x*=rx;p.y*=ry;});
  }
  buildWalls();buildBushes();
  updateCam(0,true);
}
window.addEventListener('resize',resizeCanvas);

/* geometry */
function hitWall(x,y,r){
  for(var i=0;i<walls.length;i++){
    var w=walls[i];
    var cx=clamp(x,w.x,w.x+w.w),cy=clamp(y,w.y,w.y+w.h);
    var dx=x-cx,dy=y-cy;
    if(dx*dx+dy*dy<r*r)return true;
  }
  return false;
}
function seg(ax,ay,bx,by,cx,cy,dx,dy){
  var d1=(dx-cx)*(ay-cy)-(dy-cy)*(ax-cx);
  var d2=(dx-cx)*(by-cy)-(dy-cy)*(bx-cx);
  var d3=(bx-ax)*(cy-ay)-(by-ay)*(cx-ax);
  var d4=(bx-ax)*(dy-ay)-(by-ay)*(dx-ax);
  return ((d1>0&&d2<0)||(d1<0&&d2>0))&&((d3>0&&d4<0)||(d3<0&&d4>0));
}
function losClear(x1,y1,x2,y2){
  for(var i=0;i<walls.length;i++){
    var w=walls[i];
    var x=w.x,y=w.y,X=w.x+w.w,Y=w.y+w.h;
    if(seg(x1,y1,x2,y2,x,y,X,y)||seg(x1,y1,x2,y2,X,y,X,Y)||
       seg(x1,y1,x2,y2,X,Y,x,Y)||seg(x1,y1,x2,y2,x,Y,x,y))return false;
  }
  return true;
}

/* ================= game state ================= */
var G={running:false,state:'idle',fighters:[],projs:[],fx:[],time:0,
  countdown:0,cdLabel:'',shake:0,laser:0,laserOwner:null,endTimer:0,place:0,newUnlocks:[]};
var firing=false,fireJust=false;
var joyVec={x:0,y:0};
var keys={};
var manualAim=false,aimVec={x:1,y:0};

function makeFighter(def,x,y,isPlayer,name){
  return {def:def,x:x,y:y,r:22*S,hp:1000,maxHp:1000,alive:true,isPlayer:isPlayer,
    name:name,kx:0,ky:0,aim:isPlayer?-Math.PI/2:Math.PI/2,
    charge:0,atkCd:0,flash:0,dash:null,readySfx:false,chipDead:false,
    revealT:0,bushIdx:-1,walkT:0,moving:false,
    ai:isPlayer?null:{target:null,retarget:0,strafe:Math.random()<.5?1:-1,
      strafeT:rand(.7,1.5),ideal:rand(170,290),powerT:rand(.3,1.2),
      wander:null,wanderT:0}};
}
function enemiesAlive(f){return G.fighters.filter(function(e){return e!==f&&e.alive;});}
function clampF(f){f.x=clamp(f.x,f.r+6,world.w-f.r-6);f.y=clamp(f.y,f.r+6,world.h-f.r-6);}
function ejectFromWalls(f){
  var tries=0;
  while(hitWall(f.x,f.y,f.r+2)&&tries++<60){
    var a=Math.atan2(world.h/2-f.y,world.w/2-f.x);
    f.x+=Math.cos(a)*10;f.y+=Math.sin(a)*10;
  }
  clampF(f);
}
function canSee(v,e){
  if(e.revealT>0)return true;
  if(e.bushIdx<0)return true;
  if(v&&v.bushIdx===e.bushIdx)return true;
  if(v&&distF(v,e)<130*S)return true;
  return false;
}
function updateCam(dt,snap){
  if(!G.fighters.length)return;
  var p=G.fighters[0];
  var tx=clamp(p.x-W/2,0,Math.max(0,world.w-W));
  var ty=clamp(p.y-H*0.52,-70,Math.max(-70,world.h-H+160));
  if(snap){cam.x=tx;cam.y=ty;}
  else{var k=Math.min(1,10*dt);cam.x+=(tx-cam.x)*k;cam.y+=(ty-cam.y)*k;}
}

function startMatch(){
  if(!unlocked(charById(save.selected)))save.selected='thief';
  G.running=false;
  resizeCanvas();
  var pdef=charById(save.selected);
  var pool=shuffle(CHARS.slice());
  var names=shuffle(BOT_NAMES.slice());
  G.fighters=[
    makeFighter(pdef,world.w*.5,world.h*.88,true,save.name||'אני'),
    makeFighter(pool[0],world.w*.20,world.h*.10,false,names[0]),
    makeFighter(pool[1],world.w*.75,world.h*.42,false,names[1])
  ];
  G.fighters.forEach(function(f){ejectFromWalls(f);f.bushIdx=bushAt(f.x,f.y);});
  G.projs=[];G.fx=[];G.time=0;G.shake=0;G.laser=0;G.laserOwner=null;
  G.state='count';G.countdown=2.85;G.cdLabel='';G.endTimer=0;G.place=0;G.newUnlocks=[];
  firing=false;fireJust=false;manualAim=false;joyVec={x:0,y:0};
  setupHUD();
  $('fireBtn').textContent=pdef.atkEmoji;
  $('powerIcon').textContent=pdef.power.emoji;
  var hint;
  if(pdef.type==='melee')hint='🕹️ תזוזה  •  👊 החזיקו למכות  •  '+pdef.power.emoji+' מכת כוח';
  else hint='🕹️ תזוזה  •  '+pdef.atkEmoji+' החזיקו לירי · גררו לכיוון  •  '+pdef.power.emoji+' מכת כוח';
  $('cdHint').textContent=hint;
  $('countWrap').classList.remove('hidden');
  $('overlay').classList.add('hidden');
  $('powerBtn').classList.remove('ready');
  $('powerBtn').style.setProperty('--pct','0deg');
  G.running=true;
  updateCam(0,true);
  updateHUD();
  show('game');
}

function setupHUD(){
  var hud=$('hud');hud.innerHTML='';
  G.fighters.forEach(function(f){
    var chip=document.createElement('div');
    chip.className='chip'+(f.isPlayer?' me':'');
    var ic=charIcon(f.def,20);
    var skull=document.createElement('span');
    skull.textContent='💀';skull.style.display='none';skull.style.fontSize='1rem';
    var nm=document.createElement('span');nm.className='chip-n';nm.textContent=f.name;
    var bar=document.createElement('div');bar.className='chip-bar';
    var fill=document.createElement('div');bar.appendChild(fill);
    chip.appendChild(ic);chip.appendChild(skull);chip.appendChild(nm);chip.appendChild(bar);
    f.chip=chip;f.chipBar=fill;f.chipIcon=ic;f.chipSkull=skull;
    hud.appendChild(chip);
  });
}
function updateHUD(){
  for(var i=0;i<G.fighters.length;i++){
    var f=G.fighters[i];
    if(!f.chipBar)continue;
    var pct=clamp(f.hp/f.maxHp,0,1);
    f.chipBar.style.width=(pct*100)+'%';
    f.chipBar.style.background=pct>.5?'#35d07f':(pct>.25?'#ffc93c':'#ff5b5b');
    if(!f.alive&&!f.chipDead){
      f.chipDead=true;f.chip.classList.add('dead');
      f.chipIcon.style.display='none';f.chipSkull.style.display='inline';
    }
  }
}
function updatePowerBtn(){
  var f=G.fighters[0];if(!f)return;
  var pct=clamp(f.charge/f.def.power.charge,0,1);
  $('powerBtn').style.setProperty('--pct',(pct*360)+'deg');
  var ready=pct>=1&&f.alive&&G.state==='play';
  $('powerBtn').classList.toggle('ready',ready);
  if(!ready)f.readySfx=false;
  else if(!f.readySfx){f.readySfx=true;sfx('ready');}
}

/* ================= input ================= */
var joyZone=$('joyZone'),joyBase=$('joyBase'),joyKnob=$('joyKnob');
var joyId=null,joyOrigin={x:0,y:0};
var JOY_R=52;
function moveKnob(dx,dy){joyKnob.style.transform='translate(calc(-50% + '+dx+'px), calc(-50% + '+dy+'px))';}
function resetJoyPos(){
  joyBase.style.left='26px';joyBase.style.top='auto';
  joyBase.style.bottom='calc(40px + env(safe-area-inset-bottom))';
}
joyZone.addEventListener('pointerdown',function(e){
  if(joyId!==null)return;
  joyId=e.pointerId;
  try{joyZone.setPointerCapture(joyId);}catch(err){}
  joyOrigin={x:e.clientX,y:e.clientY};
  var zr=joyZone.getBoundingClientRect();
  joyBase.style.left=(e.clientX-zr.left-59)+'px';
  joyBase.style.bottom='auto';
  joyBase.style.top=(e.clientY-zr.top-59)+'px';
  joyBase.classList.add('on');
  joyVec={x:0,y:0};moveKnob(0,0);
  e.preventDefault();
});
joyZone.addEventListener('pointermove',function(e){
  if(e.pointerId!==joyId)return;
  var dx=e.clientX-joyOrigin.x,dy=e.clientY-joyOrigin.y;
  var d=Math.hypot(dx,dy);
  if(d>JOY_R){dx*=JOY_R/d;dy*=JOY_R/d;}
  joyVec={x:dx/JOY_R,y:dy/JOY_R};
  moveKnob(dx,dy);
});
function endJoy(e){
  if(e.pointerId!==joyId)return;
  joyId=null;joyVec={x:0,y:0};moveKnob(0,0);
  joyBase.classList.remove('on');resetJoyPos();
}
joyZone.addEventListener('pointerup',endJoy);
joyZone.addEventListener('pointercancel',endJoy);

var fireBtn=$('fireBtn');
var fireId=null,fireStart={x:0,y:0};
fireBtn.addEventListener('pointerdown',function(e){
  if(fireId!==null)return;
  fireId=e.pointerId;
  try{fireBtn.setPointerCapture(e.pointerId);}catch(err){}
  fireStart={x:e.clientX,y:e.clientY};
  firing=true;fireJust=true;manualAim=false;
  fireBtn.classList.add('press');
  e.preventDefault();
});
fireBtn.addEventListener('pointermove',function(e){
  if(e.pointerId!==fireId)return;
  var dx=e.clientX-fireStart.x,dy=e.clientY-fireStart.y;
  var d=Math.hypot(dx,dy);
  if(d>16){manualAim=true;aimVec={x:dx/d,y:dy/d};}
  else manualAim=false;
});
function endFire(e){
  if(e.pointerId!==fireId)return;
  fireId=null;firing=false;manualAim=false;
  fireBtn.classList.remove('press');
}
fireBtn.addEventListener('pointerup',endFire);
fireBtn.addEventListener('pointercancel',endFire);

$('powerBtn').addEventListener('pointerdown',function(e){tryPlayerPower();e.preventDefault();});
$('exitBtn').addEventListener('click',function(){
  G.running=false;G.state='idle';
  $('countWrap').classList.add('hidden');
  $('overlay').classList.add('hidden');
  refreshHome();show('home');
});

window.addEventListener('keydown',function(e){
  keys[e.key.toLowerCase()]=true;
  if(e.key===' '){keys.space=true;fireJust=true;e.preventDefault();}
  if((e.key==='e'||e.key==='E'||e.key==='Enter')&&G.state==='play')tryPlayerPower();
});
window.addEventListener('keyup',function(e){
  keys[e.key.toLowerCase()]=false;
  if(e.key===' ')keys.space=false;
});
document.addEventListener('contextmenu',function(e){e.preventDefault();});
document.addEventListener('touchmove',function(e){
  if(e.target&&e.target.closest&&e.target.closest('#game'))e.preventDefault();
},{passive:false});

/* ================= combat ================= */
var BASE_SPEED=195;
function moveFighter(f,dx,dy,dt){
  var sp=BASE_SPEED*S*f.def.spd;
  var nx=f.x+(dx*sp+f.kx)*dt;
  var ny=f.y+(dy*sp+f.ky)*dt;
  var dec=Math.pow(0.002,dt);
  f.kx*=dec;f.ky*=dec;
  if(!hitWall(nx,f.y,f.r))f.x=nx;
  if(!hitWall(f.x,ny,f.r))f.y=ny;
  clampF(f);
  var m=Math.hypot(dx,dy);
  if(m>0.12){f.moving=true;f.walkT+=dt*f.def.spd;}
  else f.moving=false;
}
function knock(t,from,power){
  var a=angTo(from,t);
  t.kx+=Math.cos(a)*power;t.ky+=Math.sin(a)*power;
}
function autoTarget(f){
  var best=null,bd=1e9,any=null,ad=1e9;
  var en=enemiesAlive(f);
  for(var i=0;i<en.length;i++){
    var e=en[i];
    if(!canSee(f,e))continue;
    var d=distF(f,e);
    if(d<ad){ad=d;any=e;}
    if(d<bd&&losClear(f.x,f.y,e.x,e.y)){bd=d;best=e;}
  }
  return best||any;
}
function shootProj(f,angle){
  f.revealT=Math.max(f.revealT,.9);
  var sp=470*S;
  G.projs.push({x:f.x+Math.cos(angle)*(f.r+10),y:f.y+Math.sin(angle)*(f.r+10),
    vx:Math.cos(angle)*sp,vy:Math.sin(angle)*sp,r:8*S,
    dmg:f.def.dmg,owner:f,kind:f.def.projKind,
    rot:Math.random()*6.28,spin:(Math.random()<.5?-1:1)*rand(9,14)});
  sfx(f.isPlayer?'shoot':'shoot2');
}
function meleeSwing(f){
  f.revealT=Math.max(f.revealT,.9);
  var target=null,bd=1e9;
  var en=enemiesAlive(f);
  for(var i=0;i<en.length;i++){
    var e=en[i];
    var d=distF(f,e);
    if(d<f.r+e.r+34*S&&d<bd&&losClear(f.x,f.y,e.x,e.y)){target=e;bd=d;}
  }
  if(target){
    f.aim=angTo(f,target);
    fxEmoji(target.x,target.y,'👊',30*S,.28);
    fxRing(target.x,target.y,f.def.color,8,26*S,.25);
    knock(target,f,260*S);
    sfx('punch');
    hurt(target,f.def.dmg,f);
  }else{
    var wx=f.x+Math.cos(f.aim)*(f.r+22*S),wy=f.y+Math.sin(f.aim)*(f.r+22*S);
    fxEmoji(wx,wy,'💨',20*S,.2);
    if(f.isPlayer)sfx('whiff');
  }
}
function damageNoEnd(f,dmg,src,silent){
  if(!f.alive||G.state!=='play')return false;
  f.hp-=dmg;f.flash=.16;
  f.revealT=Math.max(f.revealT,.5);
  var kill=f.hp<=0;
  if(!silent)fxDmg(f.x,f.y-f.r-30*S,String(dmg),
    kill?'#ffd75e':(f.isPlayer?'#ff9b9b':'#ffe9a8'),dmg>=500);
  if(f.isPlayer)sfx('hurt');else if(src&&src.isPlayer)sfx('hit');
  if(kill){
    f.hp=0;f.alive=false;f.dash=null;
    fxEmoji(f.x,f.y,'💥',54*S,.5);
    fxRing(f.x,f.y,'#ff8a5c',10,70*S,.45);
    fxSpark(f.x,f.y,'#ffce7a');
    addShake(9);sfx('boom');
    if(src&&src.isPlayer){try{if(navigator.vibrate)navigator.vibrate(35);}catch(e){}}
  }
  return kill;
}
function hurt(f,dmg,src){damageNoEnd(f,dmg,src,false);checkEnd();}
function checkEnd(){
  if(G.state!=='play')return;
  var alive=G.fighters.filter(function(x){return x.alive;});
  var p=G.fighters[0];
  if(!p.alive){G.place=alive.length+1;beginEnd();}
  else if(alive.length===1){G.place=1;beginEnd();}
}
function beginEnd(){
  G.state='ending';
  G.endTimer=G.place===1?0.8:1.1;
  firing=false;
  G.newUnlocks=[];
  if(G.place===1){
    var before=save.trophies;
    save.trophies+=10;persist();
    G.newUnlocks=CHARS.filter(function(c){return before<c.cost&&save.trophies>=c.cost;});
  }
}
function addShake(m){G.shake=Math.max(G.shake,m);}

/* powers */
function usePower(f){
  var id=f.def.id,i,e,d;
  if(id==='robot'){
    f.charge=0;f.revealT=Math.max(f.revealT,1.2);
    G.laser=1.0;G.laserOwner=f;
    addShake(13);sfx('laser');
    fxMsg(f.x,f.y-f.r-40*S,'⚡ לייזר־ענק! ⚡');
    var en=enemiesAlive(f);
    for(i=0;i<en.length;i++)damageNoEnd(en[i],500,f,false);
    checkEnd();
    return true;
  }
  if(id==='thief'){
    var t=null,bd=1e9;
    var en2=enemiesAlive(f);
    for(i=0;i<en2.length;i++){
      e=en2[i];d=distF(f,e);
      if(d<f.r+e.r+60*S&&d<bd&&losClear(f.x,f.y,e.x,e.y)){t=e;bd=d;}
    }
    if(!t)return false;
    f.charge=0;f.revealT=Math.max(f.revealT,1.2);
    f.aim=angTo(f,t);
    fxEmoji(t.x,t.y,'🦵',44*S,.4);
    fxRing(t.x,t.y,'#ffc93c',10,60*S,.35);
    addShake(9);sfx('kick');
    knock(t,f,900*S);
    hurt(t,750,f);
    return true;
  }
  if(id==='samurai'){
    var best=null,bd2=1e9;
    var en3=enemiesAlive(f);
    for(i=0;i<en3.length;i++){
      e=en3[i];d=distF(f,e);
      if(d<300*S&&d<bd2&&canSee(f,e)&&losClear(f.x,f.y,e.x,e.y)){best=e;bd2=d;}
    }
    if(!best)return false;
    f.charge=0;f.revealT=Math.max(f.revealT,1.2);
    f.dash={a:angTo(f,best),t:0,dur:.22,killed:[]};
    sfx('sword');
    return true;
  }
  return false;
}
function tryPlayerPower(){
  var f=G.fighters[0];
  if(!f||G.state!=='play'||!f.alive||f.dash)return;
  if(f.charge<f.def.power.charge){
    var pb=$('powerBtn');
    pb.classList.remove('shake');void pb.offsetWidth;pb.classList.add('shake');
    sfx('deny');return;
  }
  var ok=usePower(f);
  if(!ok){fxMsg(f.x,f.y-f.r-40*S,'אין יריב קרוב!');sfx('deny');}
}
function dashStep(f,dt){
  var d=f.dash;d.t+=dt;
  var sp=1500*S;
  var nx=f.x+Math.cos(d.a)*sp*dt,ny=f.y+Math.sin(d.a)*sp*dt;
  if(hitWall(nx,ny,f.r)||nx<f.r||nx>world.w-f.r||ny<f.r||ny>world.h-f.r){
    d.t=d.dur;
  }else{f.x=nx;f.y=ny;}
  fxGhost(f.x,f.y,f.r,f.def.color);
  var en=enemiesAlive(f);
  for(var i=0;i<en.length;i++){
    var e=en[i];
    if(d.killed.indexOf(e)>=0)continue;
    if(distF(f,e)<f.r+e.r+10*S){
      d.killed.push(e);
      fxEmoji(e.x,e.y,'⚔️',46*S,.45);
      fxMsg(e.x,e.y-44*S,'חיסול!');
      fxRing(e.x,e.y,'#ffffff',6,70*S,.4);
      addShake(10);
      damageNoEnd(e,999999,f,true);
    }
  }
  checkEnd();
  if(f.dash&&d.t>=d.dur)f.dash=null;
}

/* player + bots */
function playerControl(f,dt){
  var mx=joyVec.x,my=joyVec.y;
  mx+=(keys['arrowright']||keys['d']?1:0)-(keys['arrowleft']||keys['a']?1:0);
  my+=(keys['arrowdown']||keys['s']?1:0)-(keys['arrowup']||keys['w']?1:0);
  var m=Math.hypot(mx,my);
  if(m>1){mx/=m;my/=m;}
  moveFighter(f,mx,my,dt);
  var t=autoTarget(f);
  if(manualAim)f.aim=Math.atan2(aimVec.y,aimVec.x);
  else if(t)f.aim=angTo(f,t);
  else if(m>0.1)f.aim=Math.atan2(my,mx);
  f.atkCd-=dt;
  if(fireJust){f.atkCd=Math.min(f.atkCd,0);fireJust=false;}
  if((firing||keys.space)&&f.atkCd<=0){
    if(f.def.type==='shoot')shootProj(f,f.aim);
    else meleeSwing(f);
    f.atkCd=1/f.def.rate;
  }
}
function botThink(f,dt){
  var ai=f.ai;
  ai.retarget-=dt;
  if(ai.retarget<=0){ai.target=autoTarget(f);ai.retarget=rand(.45,.95);}
  if(ai.target&&(!ai.target.alive||!canSee(f,ai.target)))ai.target=autoTarget(f);
  var t=ai.target;
  var mx=0,my=0,d=0;
  if(t){
    var dx=t.x-f.x,dy=t.y-f.y;
    d=Math.hypot(dx,dy)||1;
    var ux=dx/d,uy=dy/d;
    ai.strafeT-=dt;
    if(ai.strafeT<=0){ai.strafe*=-1;ai.strafeT=rand(.7,1.5);}
    if(f.def.type==='melee'){
      mx=ux;my=uy;
      if(d<f.r+t.r+28*S){mx*=.15;my*=.15;}
      mx+=-uy*ai.strafe*.25;my+=ux*ai.strafe*.25;
    }else{
      var ideal=ai.ideal*S;
      if(d>ideal+70*S){mx=ux;my=uy;}
      else if(d<ideal-70*S){mx=-ux;my=-uy;}
      mx+=-uy*ai.strafe*.8;my+=ux*ai.strafe*.8;
    }
    f.aim=Math.atan2(dy,dx);
  }else{
    ai.wanderT-=dt;
    if(ai.wanderT<=0||!ai.wander){
      var ne=null,nd=1e9;
      var en=enemiesAlive(f);
      for(var i=0;i<en.length;i++){
        var dd0=distF(f,en[i]);
        if(dd0<nd){nd=dd0;ne=en[i];}
      }
      var bx=ne?ne.x:world.w/2,by=ne?ne.y:world.h/2;
      ai.wander={x:clamp(bx+rand(-1,1)*300*S,40,world.w-40),
                 y:clamp(by+rand(-1,1)*300*S,40,world.h-40)};
      ai.wanderT=rand(1.2,2.4);
    }
    var wdx=ai.wander.x-f.x,wdy=ai.wander.y-f.y;
    var wd=Math.hypot(wdx,wdy)||1;
    if(wd>30){mx=wdx/wd;my=wdy/wd;f.aim=Math.atan2(wdy,wdx);}
  }
  var mm=Math.hypot(mx,my);
  if(mm>1){mx/=mm;my/=mm;}
  if(mm>0.01){
    var px=f.x+mx*46*S,py=f.y+my*46*S;
    if(hitWall(px,py,f.r)){
      var a=Math.atan2(my,mx);
      var offs=[1.2,-1.2,2.2,-2.2];
      for(var k=0;k<offs.length;k++){
        var nx2=f.x+Math.cos(a+offs[k])*46*S,ny2=f.y+Math.sin(a+offs[k])*46*S;
        if(!hitWall(nx2,ny2,f.r)){mx=Math.cos(a+offs[k]);my=Math.sin(a+offs[k]);break;}
      }
    }
  }
  moveFighter(f,mx,my,dt);
  f.atkCd-=dt;
  if(t){
    if(f.def.type==='shoot'){
      if(f.atkCd<=0&&d<560*S&&losClear(f.x,f.y,t.x,t.y)){
        shootProj(f,f.aim+rand(-.13,.13));
        f.atkCd=rand(.32,.5);
      }
    }else{
      if(f.atkCd<=0&&d<f.r+t.r+36*S){
        meleeSwing(f);
        f.atkCd=.5;
      }
    }
  }
  if(f.charge>=f.def.power.charge){
    ai.powerT-=dt;
    if(ai.powerT<=0){
      var used=usePower(f);
      ai.powerT=used?.6:.25;
    }
  }
}
function separate(){
  var fs=G.fighters.filter(function(f){return f.alive;});
  for(var i=0;i<fs.length;i++)for(var j=i+1;j<fs.length;j++){
    var a=fs[i],b=fs[j];
    var dx=b.x-a.x,dy=b.y-a.y;
    var d=Math.hypot(dx,dy);
    var min=a.r+b.r;
    if(d<min){
      if(d<0.01){dx=1;dy=0;d=1;}
      var push=(min-d)/2,ux=dx/d,uy=dy/d;
      a.x-=ux*push;a.y-=uy*push;
      b.x+=ux*push;b.y+=uy*push;
      clampF(a);clampF(b);
      if(hitWall(a.x,a.y,a.r))ejectFromWalls(a);
      if(hitWall(b.x,b.y,b.r))ejectFromWalls(b);
    }
  }
}
function updateProjs(dt){
  for(var i=G.projs.length-1;i>=0;i--){
    var p=G.projs[i];
    p.x+=p.vx*dt;p.y+=p.vy*dt;p.rot+=p.spin*dt;
    if(p.x<-40||p.x>world.w+40||p.y<-40||p.y>world.h+40){G.projs.splice(i,1);continue;}
    if(hitWall(p.x,p.y,p.r)){fxSpark(p.x,p.y,'#c9d2ff');G.projs.splice(i,1);continue;}
    var hitF=false;
    for(var j=0;j<G.fighters.length;j++){
      var f=G.fighters[j];
      if(!f.alive||f===p.owner)continue;
      var dx=f.x-p.x,dy=f.y-p.y;
      var rr2=f.r+p.r;
      if(dx*dx+dy*dy<rr2*rr2){
        fxSpark(p.x,p.y,p.owner.def.color);
        hurt(f,p.dmg,p.owner);
        hitF=true;break;
      }
    }
    if(hitF)G.projs.splice(i,1);
  }
}

/* ================= fx ================= */
function addFx(o){o.t=0;G.fx.push(o);}
function fxDmg(x,y,text,color,big){addFx({type:'dmg',x:x,y:y,text:text,color:color,big:!!big,dur:.8});}
function fxMsg(x,y,text){addFx({type:'msg',x:x,y:y,text:text,dur:1.0});}
function fxEmoji(x,y,e,size,dur){addFx({type:'emoji',x:x,y:y,e:e,size:size,dur:dur||.35});}
function fxRing(x,y,color,r0,r1,dur){addFx({type:'ring',x:x,y:y,color:color,r0:r0,r1:r1,dur:dur||.3});}
function fxSpark(x,y,color){addFx({type:'spark',x:x,y:y,color:color,dur:.22});}
function fxGhost(x,y,r,color){addFx({type:'ghost',x:x,y:y,r:r,color:color,dur:.3});}
function updateFx(dt){
  for(var i=G.fx.length-1;i>=0;i--){
    G.fx[i].t+=dt;
    if(G.fx[i].t>=G.fx[i].dur)G.fx.splice(i,1);
  }
}

/* ================= update ================= */
function update(dt){
  G.time+=dt;
  if(G.shake>0)G.shake=Math.max(0,G.shake-34*dt);
  if(G.laser>0)G.laser-=dt;
  updateFx(dt);

  if(G.state==='count'){
    G.countdown-=dt;
    var label=G.countdown>0.6?String(Math.ceil((G.countdown-0.6)/0.75)):'קרב!';
    if(label!==G.cdLabel){
      G.cdLabel=label;
      var cd=$('cdNum');
      cd.textContent=label;
      cd.classList.remove('pop');void cd.offsetWidth;cd.classList.add('pop');
      sfx(label==='קרב!'?'go':'count');
    }
    if(G.countdown<=0){
      G.state='play';
      $('countWrap').classList.add('hidden');
    }
    updateCam(dt,false);
    updateHUD();updatePowerBtn();
    return;
  }
  if(G.state==='ending'||G.state==='over'){
    updateProjs(dt);
    if(G.fighters.length&&G.fighters[0].alive)updateCam(dt,false);
    updateHUD();updatePowerBtn();
    if(G.state==='ending'){
      G.endTimer-=dt;
      if(G.endTimer<=0)showOverlay();
    }
    return;
  }
  if(G.state!=='play')return;

  var i,f;
  for(i=0;i<G.fighters.length;i++){
    f=G.fighters[i];
    if(!f.alive)continue;
    f.bushIdx=bushAt(f.x,f.y);
    f.revealT=Math.max(0,f.revealT-dt);
    f.flash=Math.max(0,f.flash-dt);
  }
  for(i=0;i<G.fighters.length;i++){
    f=G.fighters[i];
    if(!f.alive)continue;
    if(f.dash){dashStep(f,dt);continue;}
    f.charge=Math.min(f.def.power.charge,f.charge+dt);
    if(f.isPlayer)playerControl(f,dt);
    else botThink(f,dt);
  }
  separate();
  updateProjs(dt);
  updateCam(dt,false);
  updateHUD();
  updatePowerBtn();
}

/* ================= render ================= */
function circleFill(x,y,r){ctx.beginPath();ctx.arc(x,y,r,0,6.2832);ctx.fill();}
function circleStroke(x,y,r){ctx.beginPath();ctx.arc(x,y,r,0,6.2832);ctx.stroke();}
function rr(x,y,w,h,r){
  ctx.beginPath();
  if(ctx.roundRect)ctx.roundRect(x,y,w,h,r);
  else ctx.rect(x,y,w,h);
}
function drawEmoji(e,x,y,size){
  ctx.font=size+'px "Apple Color Emoji","Segoe UI Emoji","Noto Color Emoji",sans-serif';
  ctx.textAlign='center';ctx.textBaseline='middle';
  ctx.fillText(e,x,y);
}
function drawProj(p){
  ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);
  if(p.kind==='knife'){
    ctx.fillStyle='#e6ecf7';
    ctx.beginPath();ctx.moveTo(11*S,0);ctx.lineTo(-2*S,-3.6*S);ctx.lineTo(-2*S,3.6*S);ctx.closePath();ctx.fill();
    ctx.fillStyle='#e8b64c';ctx.fillRect(-3.4*S,-4*S,1.8*S,8*S);
    ctx.fillStyle='#8a5a33';ctx.fillRect(-9*S,-2*S,5.6*S,4*S);
  }else{
    ctx.fillStyle='#9aa6c5';ctx.strokeStyle='#57648f';ctx.lineWidth=1.6*S;
    ctx.beginPath();
    for(var k=0;k<6;k++){
      var a=k*1.0472;
      var px=Math.cos(a)*7*S,py=Math.sin(a)*7*S;
      if(k===0)ctx.moveTo(px,py);else ctx.lineTo(px,py);
    }
    ctx.closePath();ctx.fill();ctx.stroke();
    ctx.fillStyle='#57648f';ctx.beginPath();ctx.arc(0,0,2.6*S,0,6.2832);ctx.fill();
  }
  ctx.restore();
}
var BUSH_DARK=['#2f7a3d','#38904a','#42a357'];
var BUSH_LIGHT=['#6cc46f','#7fd583','#93e397'];
function drawBush(b,light){
  var pal=light?BUSH_LIGHT:BUSH_DARK;
  ctx.globalAlpha=light?0.55:0.96;
  ctx.fillStyle=light?'#57ab5c':'#26603a';
  rr(b.x,b.y,b.w,b.h,16*S);ctx.fill();
  for(var i=0;i<b.blobs.length;i++){
    var bl=b.blobs[i];
    ctx.fillStyle=pal[bl.sh];
    ctx.beginPath();ctx.arc(b.x+bl.dx,b.y+bl.dy,bl.r,0,6.2832);ctx.fill();
  }
  ctx.globalAlpha=1;
}
function render(){
  ctx.clearRect(0,0,W,H);
  ctx.save();
  var shx=0,shy=0;
  if(G.shake>0){shx=rand(-G.shake,G.shake);shy=rand(-G.shake,G.shake);}
  ctx.translate(-cam.x+shx,-cam.y+shy);

  ctx.fillStyle='#0c0f1a';
  ctx.fillRect(cam.x-30,cam.y-30,W+60,H+60);
  ctx.fillStyle='#141827';
  ctx.fillRect(0,0,world.w,world.h);
  ctx.strokeStyle='rgba(85,100,170,.13)';ctx.lineWidth=1;
  var g=46*S;
  var gx0=Math.max(g,Math.ceil(cam.x/g)*g),gx1=Math.min(world.w-1,cam.x+W);
  var gy0=Math.max(g,Math.ceil(cam.y/g)*g),gy1=Math.min(world.h-1,cam.y+H);
  var vx0=Math.max(0,cam.x),vx1=Math.min(world.w,cam.x+W);
  var vy0=Math.max(0,cam.y),vy1=Math.min(world.h,cam.y+H);
  ctx.beginPath();
  var x,y;
  for(x=gx0;x<gx1;x+=g){ctx.moveTo(x,vy0);ctx.lineTo(x,vy1);}
  for(y=gy0;y<gy1;y+=g){ctx.moveTo(vx0,y);ctx.lineTo(vx1,y);}
  ctx.stroke();
  ctx.strokeStyle='#31395c';ctx.lineWidth=4;
  ctx.strokeRect(2,2,world.w-4,world.h-4);

  var i,f,w;
  for(i=0;i<walls.length;i++){
    w=walls[i];
    if(w.y+w.h<cam.y-40||w.y>cam.y+H+40)continue;
    ctx.fillStyle='#39415f';rr(w.x,w.y,w.w,w.h,8);ctx.fill();
    ctx.fillStyle='rgba(255,255,255,.10)';rr(w.x,w.y,w.w,4,3);ctx.fill();
    ctx.strokeStyle='rgba(0,0,0,.35)';ctx.lineWidth=2;rr(w.x,w.y,w.w,w.h,8);ctx.stroke();
  }
  for(i=0;i<G.fighters.length;i++){
    f=G.fighters[i];
    if(!f.alive){
      ctx.globalAlpha=.5;
      drawEmoji('💀',f.x,f.y,f.r*1.2);
      ctx.globalAlpha=1;
    }
  }
  for(i=0;i<G.projs.length;i++)drawProj(G.projs[i]);

  var pl=G.fighters[0];
  for(i=0;i<G.fighters.length;i++){
    f=G.fighters[i];
    if(f.alive&&f.bushIdx<0)drawFighter(f);
  }
  for(i=0;i<bushes.length;i++){
    var light=!!(pl&&pl.alive&&pl.bushIdx===i);
    drawBush(bushes[i],light);
  }
  for(i=0;i<G.fighters.length;i++){
    f=G.fighters[i];
    if(f.alive&&f.bushIdx>=0&&(f.isPlayer||canSee(pl,f)))drawFighter(f);
  }
  if(G.state==='play'&&manualAim&&pl&&pl.alive&&!pl.dash){
    ctx.save();
    ctx.globalAlpha=.55;ctx.strokeStyle='#ffd75e';ctx.lineWidth=3;ctx.setLineDash([9,8]);
    var a=pl.aim;
    var x0=pl.x+Math.cos(a)*(pl.r+8),y0=pl.y+Math.sin(a)*(pl.r+8);
    var x1=pl.x+Math.cos(a)*(pl.r+8+150*S),y1=pl.y+Math.sin(a)*(pl.r+8+150*S);
    ctx.beginPath();ctx.moveTo(x0,y0);ctx.lineTo(x1,y1);ctx.stroke();
    ctx.setLineDash([]);
    ctx.fillStyle='#ffd75e';
    ctx.beginPath();
    ctx.moveTo(x1+Math.cos(a)*10*S,y1+Math.sin(a)*10*S);
    ctx.lineTo(x1+Math.cos(a+2.5)*8*S,y1+Math.sin(a+2.5)*8*S);
    ctx.lineTo(x1+Math.cos(a-2.5)*8*S,y1+Math.sin(a-2.5)*8*S);
    ctx.closePath();ctx.fill();
    ctx.restore();
  }
  drawFx();
  if(G.laser>0)drawLaser();
  ctx.restore();
  drawArrows();
}
function drawFighter(f){
  if(f.flash>0){
    ctx.globalAlpha=Math.min(1,f.flash*6);
    ctx.fillStyle='#ffffff';circleFill(f.x,f.y,f.r+7);
    ctx.globalAlpha=1;
  }
  if(f.isPlayer){
    ctx.strokeStyle='rgba(255,215,94,'+(0.3+0.22*Math.sin(G.time*5)).toFixed(3)+')';
    ctx.lineWidth=3;circleStroke(f.x,f.y,f.r+10);
  }
  drawCharacter(ctx,f.def,f.r,f.aim,{x:f.x,y:f.y,time:G.time,walk:f.walkT,moving:f.moving});
  if(!f.isPlayer&&f.charge>=f.def.power.charge&&Math.sin(G.time*10)>0){
    drawEmoji('💥',f.x+f.r*.9,f.y-f.r-10,15*S);
  }
  ctx.font='800 '+Math.round(12*S)+'px -apple-system,"Segoe UI",Roboto,sans-serif';
  ctx.textAlign='center';ctx.textBaseline='middle';
  ctx.fillStyle=f.isPlayer?'#ffd75e':'#e3e8ff';
  ctx.fillText(f.name,f.x,f.y-f.r-27*S);
  var bw=56*S,bh=7*S,bx=f.x-bw/2,by=f.y-f.r-19*S;
  ctx.fillStyle='rgba(0,0,0,.55)';rr(bx-1,by-1,bw+2,bh+2,4);ctx.fill();
  var pct=clamp(f.hp/f.maxHp,0,1);
  if(pct>0){
    ctx.fillStyle=pct>.5?'#35d07f':(pct>.25?'#ffc93c':'#ff5b5b');
    rr(bx,by,bw*pct,bh,3);ctx.fill();
  }
}
function drawFx(){
  for(var i=0;i<G.fx.length;i++){
    var e=G.fx[i];
    var p=e.t/e.dur;
    if(e.type==='dmg'){
      ctx.globalAlpha=1-p;
      ctx.font='900 '+Math.round((e.big?26:18)*S)+'px -apple-system,"Segoe UI",Roboto,sans-serif';
      ctx.textAlign='center';ctx.textBaseline='middle';
      var yy=e.y-p*44*S;
      ctx.lineWidth=3;ctx.strokeStyle='rgba(0,0,0,.6)';
      ctx.strokeText(e.text,e.x,yy);
      ctx.fillStyle=e.color||'#ffe9a8';
      ctx.fillText(e.text,e.x,yy);
      ctx.globalAlpha=1;
    }else if(e.type==='msg'){
      ctx.globalAlpha=1-p*p;
      ctx.font='900 '+Math.round(21*S)+'px -apple-system,"Segoe UI",Roboto,sans-serif';
      ctx.textAlign='center';ctx.textBaseline='middle';
      var ym=e.y-p*26*S;
      ctx.lineWidth=4;ctx.strokeStyle='rgba(0,0,0,.65)';
      ctx.strokeText(e.text,e.x,ym);
      ctx.fillStyle='#ffc93c';
      ctx.fillText(e.text,e.x,ym);
      ctx.globalAlpha=1;
    }else if(e.type==='emoji'){
      ctx.globalAlpha=1-p*p;
      var sc=.5+Math.min(1,p*3)*.7;
      drawEmoji(e.e,e.x,e.y,e.size*sc);
      ctx.globalAlpha=1;
    }else if(e.type==='ring'){
      ctx.globalAlpha=(1-p)*.85;
      ctx.strokeStyle=e.color;ctx.lineWidth=Math.max(1,4*(1-p));
      circleStroke(e.x,e.y,e.r0+(e.r1-e.r0)*p);
      ctx.globalAlpha=1;
    }else if(e.type==='spark'){
      ctx.globalAlpha=1-p;
      ctx.fillStyle=e.color||'#fff';
      for(var k=0;k<6;k++){
        var a=k*1.047,rr3=p*20*S;
        circleFill(e.x+Math.cos(a)*rr3,e.y+Math.sin(a)*rr3,2.4*S*(1-p)+.6);
      }
      ctx.globalAlpha=1;
    }else if(e.type==='ghost'){
      ctx.globalAlpha=(1-p)*.3;
      ctx.fillStyle=e.color;
      circleFill(e.x,e.y,e.r*(1-p*.4));
      ctx.globalAlpha=1;
    }
  }
}
function drawLaser(){
  var col=G.laserOwner?G.laserOwner.def.color:'#3ec7f0';
  ctx.globalAlpha=0.14+0.11*Math.abs(Math.sin(G.time*42));
  ctx.fillStyle=col;
  ctx.fillRect(cam.x,cam.y,W,H);
  var n=7;
  for(var i=0;i<n;i++){
    var y=cam.y+(((i/n)+(1-G.laser)*1.4)%1)*H;
    ctx.globalAlpha=.32;ctx.fillStyle=col;ctx.fillRect(cam.x,y-6,W,14);
    ctx.globalAlpha=.55;ctx.fillStyle='#ffffff';ctx.fillRect(cam.x,y,W,3);
  }
  ctx.globalAlpha=1;
}
function drawArrows(){
  if(!G.fighters.length)return;
  if(G.state!=='count'&&G.state!=='play'&&G.state!=='ending')return;
  var pl=G.fighters[0];
  for(var i=1;i<G.fighters.length;i++){
    var e=G.fighters[i];
    if(!e.alive)continue;
    if(!canSee(pl,e))continue;
    var sy=e.y-cam.y,sx=clamp(e.x-cam.x,26,W-26);
    var ay,dir;
    if(sy<50){ay=72;dir=-1;}
    else if(sy>H-40){ay=H-150;dir=1;}
    else continue;
    ctx.fillStyle=e.def.color;
    ctx.beginPath();
    ctx.moveTo(sx,ay+dir*12);
    ctx.lineTo(sx-9,ay-dir*5);
    ctx.lineTo(sx+9,ay-dir*5);
    ctx.closePath();ctx.fill();
    ctx.strokeStyle='rgba(0,0,0,.4)';ctx.lineWidth=2;ctx.stroke();
  }
}

/* ================= overlay ================= */
function showOverlay(){
  G.state='over';
  var place=G.place;
  $('ovEmoji').textContent=place===1?'🏆':(place===2?'🥈':'🥉');
  if(place===1){
    $('ovTitle').textContent='מקום ראשון!';
    $('ovSub').innerHTML='+10 גביעים! 🏆<br><span class="ov-total">סה״כ: '+fmt(save.trophies)+' 🏆</span>';
    spawnConfetti();sfx('win');
  }else{
    $('ovTitle').textContent=place===2?'מקום שני — כמעט!':'מקום שלישי';
    $('ovSub').innerHTML='רק המקום הראשון מקבל גביעים.<br>עוד ניסיון? 💪';
    $('confetti').innerHTML='';
    sfx('lose');
  }
  var ou=$('ovUnlock');
  if(G.newUnlocks.length){
    ou.classList.remove('hidden');ou.innerHTML='';
    G.newUnlocks.forEach(function(c){
      var row=document.createElement('div');row.className='ov-unlock-row';
      var txt=document.createElement('span');
      txt.innerHTML='🎉 נפתחה דמות חדשה: <b>'+esc(c.name)+'</b>!';
      row.appendChild(txt);
      row.appendChild(charIcon(c,30));
      ou.appendChild(row);
    });
  }else ou.classList.add('hidden');
  $('overlay').classList.remove('hidden');
}
function spawnConfetti(){
  var box=$('confetti');box.innerHTML='';
  var em=['🎉','✨','⭐','🏆','💛'];
  for(var i=0;i<26;i++){
    var s=document.createElement('span');
    s.textContent=em[i%em.length];
    s.style.left=(Math.random()*100)+'%';
    s.style.animationDelay=(Math.random()*.8)+'s';
    s.style.animationDuration=(2+Math.random()*1.6)+'s';
    s.style.fontSize=(15+Math.random()*20)+'px';
    box.appendChild(s);
  }
}
$('againBtn').addEventListener('click',function(){startMatch();});
$('homeBtn').addEventListener('click',function(){
  G.running=false;G.state='idle';
  $('overlay').classList.add('hidden');
  refreshHome();show('home');
});

/* ================= loop / init ================= */
var last=0;
function loop(ts){
  requestAnimationFrame(loop);
  var dt=Math.min(0.05,(ts-last)/1000||0.016);
  last=ts;
  if(!G.running)return;
  update(dt);
  render();
}
loadSave().then(function(){
  var hr=$('heroRow');
  CHARS.forEach(function(c){
    var sp=document.createElement('span');
    sp.appendChild(charIcon(c,46));
    hr.appendChild(sp);
  });
  refreshHome();
  renderChars();
  refreshTrophies();
  resizeCanvas();
  resetJoyPos();
  requestAnimationFrame(loop);
});
</script>
</body>
</html>

Game Source: זירת החיסולים

Creator: MysticFlare49

Libraries: none

Complexity: complex (1588 lines, 62.3 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: game-mysticflare49-mrqit04p" to link back to the original. Then publish at arcadelab.ai/publish.