🎮ArcadeLab

مدينة الراكون: القصر المهجور

by CyberGecko23
1642 lines144.4 KB🛠️ Three.js (3D graphics)
▶ Play
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<title>مدينة الراكون: القصر المهجور</title>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;700;900&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;user-select:none;-webkit-tap-highlight-color:transparent;margin:0;padding:0}
html,body{width:100%;height:100%;overflow:hidden;background:#000;font-family:'Cairo',sans-serif;touch-action:none}
#c{position:fixed;inset:0;display:block}
#vig{position:fixed;inset:0;pointer-events:none;z-index:5;background:radial-gradient(ellipse at center,transparent 30%,rgba(0,0,0,.8) 75%,rgba(0,0,0,1) 100%)}
#dmg{position:fixed;inset:0;pointer-events:none;z-index:6;background:radial-gradient(circle,rgba(255,0,0,.55),rgba(100,0,0,.85));opacity:0;transition:opacity .1s}
#dmg.on{opacity:1}
#muzzleFx{position:fixed;inset:0;pointer-events:none;z-index:4;background:radial-gradient(circle at 50% 52%,rgba(255,220,120,.55),transparent 22%);opacity:0;transition:opacity .04s}
#muzzleFx.on{opacity:1}
#crosshair{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);width:30px;height:30px;z-index:12;pointer-events:none;opacity:0;transition:opacity .2s}
#crosshair.on{opacity:1}
#crosshair::before,#crosshair::after{content:'';position:absolute;background:rgba(255,60,60,.95);box-shadow:0 0 6px rgba(0,0,0,1),0 0 4px rgba(255,0,0,.6)}
#crosshair::before{top:14px;left:0;width:30px;height:2px}
#crosshair::after{top:0;left:14px;width:2px;height:30px}
#ch-dot{position:absolute;top:11px;left:11px;width:8px;height:8px;background:#ff2020;border-radius:50%;box-shadow:0 0 8px rgba(255,50,50,1)}
#hud{position:fixed;top:10px;left:10px;right:10px;z-index:10;pointer-events:none;display:flex;justify-content:space-between;align-items:flex-start;gap:8px;flex-wrap:wrap}
.pill{background:rgba(5,8,20,.85);padding:5px 12px;border-radius:12px;border:1px solid rgba(120,170,255,.35);font-size:11px;color:#cdd8f0;font-weight:700;text-shadow:0 0 6px #000}
.pill strong{color:#ffaa33}
#hp{position:fixed;bottom:10px;left:50%;transform:translateX(-50%);color:#fff;font-weight:900;font-size:16px;z-index:10;text-shadow:0 0 12px #ff0000,0 0 5px #000;letter-spacing:2px;pointer-events:none}
#joy{position:fixed;width:120px;height:120px;border-radius:50%;background:radial-gradient(circle,rgba(80,120,220,.12),rgba(40,60,120,.06));border:2px solid rgba(120,170,255,.4);pointer-events:none;z-index:15;transform:translate(-50%,-50%) scale(0);transition:transform .15s,opacity .15s;opacity:0}
#joy.on{transform:translate(-50%,-50%) scale(1);opacity:1}
#joy .st{position:absolute;top:50%;left:50%;width:50px;height:50px;margin:-25px 0 0 -25px;border-radius:50%;background:radial-gradient(circle,rgba(180,220,255,.9),rgba(74,138,255,.55));box-shadow:0 0 20px rgba(150,200,255,.7)}
.bb{position:fixed;bottom:16px;width:68px;height:68px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:'Cairo',sans-serif;font-size:11px;font-weight:700;backdrop-filter:blur(8px);cursor:pointer;text-shadow:0 0 4px #000;padding:4px;text-align:center;line-height:1.15;transition:all .18s;z-index:15}
#ib{right:14px;background:radial-gradient(circle,rgba(80,120,220,.32),rgba(40,60,120,.22));border:3px solid rgba(120,170,255,.55);color:#cdd8f0}
#ib.on{background:radial-gradient(circle,rgba(255,200,80,.55),rgba(200,120,20,.35));border-color:rgba(255,200,80,.95);color:#fff;box-shadow:0 0 30px rgba(255,200,80,.75);animation:p 1.4s infinite}
#fb{right:92px;bottom:20px;width:62px;height:62px;background:radial-gradient(circle,rgba(220,60,60,.38),rgba(120,20,20,.25));border:3px solid rgba(255,100,100,.65);color:#ffcccc;display:none;font-size:11px}
#fb:active{transform:scale(.9)}
#ab{right:98px;bottom:94px;width:50px;height:50px;background:radial-gradient(circle,rgba(80,130,220,.4),rgba(40,70,140,.28));border:2px solid rgba(130,180,255,.7);color:#cdd8f0;display:none;font-size:10px;line-height:1.1}
#ab.aim-on{background:radial-gradient(circle,rgba(255,200,80,.85),rgba(200,110,20,.65));border-color:#ffd700;box-shadow:0 0 25px rgba(255,190,50,1);color:#fff}
#ab:active{transform:scale(.9)}
#bg{right:170px;bottom:20px;width:54px;height:54px;background:radial-gradient(circle,rgba(120,90,200,.35),rgba(60,40,120,.22));border:3px solid rgba(170,140,255,.6);color:#ddd6fe;display:none;font-size:16px}
@keyframes p{0%,100%{transform:scale(1)}50%{transform:scale(1.06)}}
.bb:active{transform:scale(.9)}
#toast{position:fixed;top:38%;left:50%;transform:translate(-50%,-50%);color:#ffd700;font-size:14px;font-weight:900;text-align:center;pointer-events:none;z-index:50;text-shadow:0 0 18px #ffd700,0 0 36px #ff8c00,0 0 8px #000;opacity:0;transition:opacity .4s;padding:10px 22px;background:radial-gradient(circle,rgba(255,200,0,.15),transparent 70%);border-radius:14px;max-width:75vw}
#toast.on{opacity:1}
.scr{position:fixed;inset:0;background:radial-gradient(circle at center,rgba(5,8,15,.98),rgba(0,0,0,1));display:none;align-items:center;justify-content:center;z-index:100;padding:12px;overflow-y:auto}
.scr.on{display:flex}
.cd{background:linear-gradient(180deg,rgba(15,18,35,.98),rgba(5,6,15,.98));border:2px solid #4a8aff;border-radius:18px;padding:18px;text-align:center;max-width:500px;width:100%;max-height:94vh;overflow-y:auto}
.cd h1{color:#8bd0ff;margin-bottom:10px;font-size:18px;text-shadow:0 0 18px #4a8aff}
.cd p{color:#b8c8e8;line-height:1.7;margin:5px 0;font-size:12px}
.cd button{margin-top:10px;padding:11px 18px;font-family:'Cairo',sans-serif;font-size:14px;font-weight:700;color:#000;border:none;border-radius:22px;background:linear-gradient(45deg,#4a8aff,#a8e0ff);cursor:pointer;width:100%}
#mute{position:fixed;bottom:12px;left:12px;background:rgba(5,8,20,.8);border:2px solid #4a8aff;color:#8bd0ff;font-size:12px;padding:5px 10px;border-radius:11px;cursor:pointer;z-index:20;font-family:'Cairo',sans-serif;font-weight:700}
#dlg{position:fixed;bottom:0;left:0;right:0;z-index:30;background:linear-gradient(0deg,rgba(0,0,0,.96),rgba(0,0,0,.75),transparent);padding:14px 14px 18px;display:none;direction:rtl}
#dlg.on{display:block}
#dlg .sp{color:#ffd700;font-weight:900;font-size:13px;margin-bottom:3px;text-shadow:0 0 10px #ffd700}
#dlg .tx{color:#f0f0ff;font-size:13px;line-height:1.7;text-shadow:0 0 6px #000;min-height:40px}
#dlg .hint{color:#8bd0ff;font-size:9px;margin-top:5px;opacity:.75;text-align:center}
#dlg .cur{display:inline-block;animation:bl .8s infinite}
@keyframes bl{0%,50%{opacity:1}51%,100%{opacity:0}}
#nt{position:fixed;inset:0;background:rgba(0,0,0,.92);display:none;align-items:center;justify-content:center;z-index:40;padding:14px}
#nt.on{display:flex}
#nt .pp{background:linear-gradient(180deg,#f4e9d0,#e8d9b5);padding:20px;border-radius:5px;max-width:420px;width:100%;direction:rtl}
#nt .pp h3{color:#4a2f1e;font-size:15px;margin-bottom:10px;border-bottom:1px solid #8b5a2b;padding-bottom:6px}
#nt .pp p{color:#3a2418;font-size:13px;line-height:1.9;margin:6px 0;white-space:pre-line}
#nt .pp .cc{color:#8b0000;font-weight:900;font-size:22px;letter-spacing:6px;text-align:center;margin-top:12px;padding:8px;background:rgba(139,0,0,.08);border:1px dashed #8b0000;border-radius:4px}
#nt .pp button{margin-top:12px;padding:9px;font-family:'Cairo',sans-serif;font-weight:700;background:#4a2f1e;color:#f4e9d0;border:none;border-radius:5px;cursor:pointer;width:100%;font-size:13px}
#ce{position:fixed;inset:0;background:rgba(0,0,0,.92);display:none;align-items:center;justify-content:center;z-index:45;padding:14px}
#ce.on{display:flex}
#ce .pn{background:linear-gradient(180deg,#1a1f3a,#0a0e20);border:2px solid #4a8aff;border-radius:12px;padding:16px;max-width:300px;width:100%;text-align:center}
#ce h3{color:#8bd0ff;font-size:13px;margin-bottom:10px;direction:rtl}
#ce input{width:100%;padding:11px;font-family:'Cairo',sans-serif;font-size:22px;font-weight:900;color:#ffd700;background:rgba(10,15,30,.9);border:2px solid #4a8aff;border-radius:8px;text-align:center;letter-spacing:8px;outline:none}
#ce input:focus{border-color:#ffd700}
#ce input.wr{border-color:#ff3050;animation:sk .4s}
@keyframes sk{0%,25%{transform:translateX(-7px)}75%{transform:translateX(7px)}100%{transform:translateX(0)}}
#ce .br{display:flex;gap:6px;margin-top:10px}
#ce button{flex:1;padding:10px;font-family:'Cairo',sans-serif;font-weight:700;font-size:13px;border:none;border-radius:7px;cursor:pointer}
#ce .ok{background:linear-gradient(45deg,#4a8aff,#a8e0ff);color:#000}
#ce .cc2{background:transparent;color:#8bd0ff;border:2px solid #4a8aff}
#inv{position:fixed;inset:0;background:rgba(5,6,10,.94);z-index:42;display:none;align-items:center;justify-content:center;direction:rtl;font-family:'Cairo',sans-serif;padding:14px}
#inv.on{display:flex}
.inv-card{background:linear-gradient(180deg,#1a1f35,#0a0e1e);border:2px solid #ffaa33;border-radius:14px;padding:20px;min-width:290px;max-width:440px;width:100%;max-height:92vh;overflow-y:auto}
.inv-title{font-size:19px;color:#ffaa33;margin-bottom:12px;letter-spacing:2px;text-align:center;border-bottom:1px solid rgba(255,170,50,.3);padding-bottom:8px}
.inv-section{font-size:11px;color:#8bd0ff;margin:12px 0 6px 0}
.inv-item{padding:10px 14px;background:rgba(255,255,255,.04);border-radius:8px;margin:6px 0;font-size:13px;color:#888;border:1px solid rgba(255,255,255,.08);display:flex;justify-content:space-between;align-items:center}
.inv-item.has{color:#4ade80;border-color:rgba(74,222,128,.4);background:rgba(74,222,128,.06);cursor:pointer}
.inv-item .qty{color:#ffd700;font-weight:900;font-size:15px}
.inv-close{margin-top:14px;padding:11px 26px;background:linear-gradient(45deg,#b91c1c,#7f1d1d);color:#fff;border:none;border-radius:8px;cursor:pointer;font-family:'Cairo',sans-serif;font-size:14px;font-weight:700;width:100%}
#rot{position:fixed;inset:0;z-index:200;background:#05050f;display:none;align-items:center;justify-content:center;flex-direction:column;gap:12px;color:#8bd0ff;text-align:center;padding:14px}
#rot.on{display:flex}
#dt{position:fixed;inset:0;background:transparent;z-index:80;display:none;overflow:hidden}
#dt.on{display:block}
#dt .panel{position:absolute;top:0;bottom:0;width:50%;background:linear-gradient(180deg,#1a1008 0%,#3a2418 30%,#2a1810 50%,#3a2418 70%,#1a1008 100%);box-shadow:inset 0 0 100px rgba(0,0,0,.95);transition:transform .8s cubic-bezier(.6,0,.4,1)}
#dt .pl{left:0;border-right:3px solid #050201;transform:translateX(0)}
#dt .pr{right:0;border-left:3px solid #050201;transform:translateX(0)}
#dt.open .pl{transform:translateX(-100%)}
#dt.open .pr{transform:translateX(100%)}
#dt .lbl{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);color:#e8e0d0;font-size:15px;font-weight:900;z-index:5;text-align:center;padding:14px 26px;background:rgba(0,0,0,.85);border:1px solid #6a5030;border-radius:10px;letter-spacing:1px;opacity:0;transition:opacity .5s}
#dt.show-label .lbl{opacity:1}
#err{position:fixed;bottom:0;left:0;right:0;max-height:80px;overflow-y:auto;background:rgba(185,28,28,.95);color:#fff;font-family:monospace;font-size:11px;padding:6px 10px;z-index:999;display:none}
</style>
</head>
<body>

<canvas id="c"></canvas>
<div id="vig"></div>
<div id="dmg"></div>
<div id="muzzleFx"></div>
<div id="crosshair"><div id="ch-dot"></div></div>

<div id="hud">
  <div class="pill" id="roomPill">المنطقة: <strong>الغرفة</strong></div>
  <div class="pill">🔫 <span id="magNum">7</span> / <span id="resNum">21</span> <span id="reloadSt" style="color:#ffaa33;font-size:10px"></span></div>
</div>
<div id="joy"><div class="st"></div></div>
<div id="toast"></div>
<div id="hp">❤️❤️❤️❤️❤️</div>
<button id="mute">🔊</button>
<button class="bb" id="ib"><span id="it">تفاعل</span></button>
<button class="bb" id="ab">🎯<br>تصويب</button>
<button class="bb" id="fb">🔥<br>إطلاق</button>
<button class="bb" id="bg">🎒</button>

<div id="inv"><div class="inv-card">
  <div class="inv-title">🎒 الشنطة</div>
  <div class="inv-section">— الأسلحة —</div>
  <div class="inv-item" id="inv-gun"><span>🔫 مسدس 9مم</span><span class="qty" id="inv-gun-q">—</span></div>
  <div class="inv-section">— المقتنيات —</div>
  <div class="inv-item" id="inv-key"><span>🔑 مفتاح الأرشيف</span><span class="qty">—</span></div>
  <div class="inv-item" id="inv-card"><span>💳 كرت الأصنصير</span><span class="qty">—</span></div>
  <div class="inv-item" id="inv-med"><span>💊 دواء الإسعاف</span><span class="qty" id="inv-med-q">—</span></div>
  <div class="inv-item" id="inv-lighter"><span>🔥 ولاعة</span><span class="qty" id="inv-lighter-q">—</span></div>
  <div class="inv-item" id="inv-nurse-key"><span>🗝️ مفتاح الاستراحة</span><span class="qty" id="inv-nurse-key-q">—</span></div>
  <div class="inv-item" id="inv-cell-key"><span>🔐 مفتاح الزنزانة</span><span class="qty" id="inv-cell-key-q">—</span></div>
  <div class="inv-section">— الملفات —</div>
  <div id="inv-notes"></div>
  <button class="inv-close" id="inv-close">إغلاق (R)</button>
</div></div>

<div id="dt">
  <div class="panel pl"></div>
  <div class="panel pr"></div>
  <div class="lbl" id="dt-lbl">جارٍ...</div>
</div>

<div id="dlg">
  <div class="sp" id="dsp"></div>
  <div class="tx" id="dtx"></div>
  <div class="hint">المسي للمتابعة <span class="cur">▌</span></div>
</div>

<div id="nt"><div class="pp">
  <h3 id="ntt"></h3>
  <p id="ntx"></p>
  <div class="cc" id="ntc"></div>
  <button id="ntb">إغلاق</button>
</div></div>

<div id="ce"><div class="pn">
  <h3 id="ctt">🔒 أدخلي الرمز</h3>
  <input id="cin" type="text" inputmode="numeric" maxlength="4" placeholder="----" autocomplete="off">
  <div class="br">
    <button class="cc2" id="cc2">إلغاء</button>
    <button class="ok" id="cok">فتح</button>
  </div>
</div></div>

<div class="scr on" id="sc-start"><div class="cd">
  <h1>🏚️ القصر المهجور 🌙</h1>
  <p style="color:#ff8a8a">مدينة الراكون... الوباء انتشر. لا أحد في الشوارع.</p>
  <p>تسللتِ إلى هذا القصر بحثاً عن ملجأ. سمعتِ صوت عبدالرحمن من مكان ما في الأسفل.</p>
  <p style="font-size:10px;color:#666;margin-top:10px">WASD=حركة • الفأرة=كاميرا • زر أيسر=إطلاق • زر أيمن=تصويب • R=شنطة • E=تفاعل</p>
  <button id="start-btn">ابدئي المهمة</button>
</div></div>

<div class="scr" id="sc-elevator"><div class="cd">
  <h1>⬇️ النزول للقبو</h1>
  <p>استخدمتِ كرت الدكتور أغاسا...</p>
  <p style="color:#8bd0ff">الأبواب المعدنية تُغلق ببطء، والأصنصير ينزل نحو الطابق السفلي...</p>
  <p style="color:#ff8a8a;font-weight:700;margin-top:10px">الظلام يشتد، والرائحة... كريهة.</p>
  <button id="next-btn">انزلي للقبو</button>
</div></div>

<div class="scr" id="sc-dead"><div class="cd">
  <h1 style="color:#ff4b4b">متتِ 💔</h1>
  <p>حاولي مجدداً</p>
  <button id="retry-btn">حاولي مجدداً</button>
</div></div>

<div class="scr" id="sc-win"><div class="cd">
  <h1 style="color:#4ade80">🎉 نجحتِ!</h1>
  <p>عبدالرحمن يخرج من الزنزانة...</p>
  <p style="color:#ffd700;font-weight:900;font-size:14px;margin-top:12px">"شكراً يا حبيبتي... كنت أعلم أنك لن تتركيني."</p>
  <button id="win-restart">العب مرة أخرى</button>
</div></div>

<div id="rot"><p style="font-size:14px;font-weight:700">أرجو تدوير الجوال للوضع العرضي</p></div>
<div id="err"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
window.onerror = function(msg, u, l){
  var e=document.getElementById('err');
  if(e){e.style.display='block';e.textContent='خطأ: '+msg+' (سطر '+l+')';}
  return false;
};

class AudioEngine{
  constructor(){this.ctx=null;this.bgm=null;this.tension=0;this.step=0;this.timer=null;this.muted=false;}
  init(){
    if(this.ctx)return;
    const AC=window.AudioContext||window.webkitAudioContext;
    this.ctx=new AC();
    const bs=this.ctx.sampleRate*2;
    const nb=this.ctx.createBuffer(1,bs,this.ctx.sampleRate);
    const d=nb.getChannelData(0);let lo=0;
    for(let i=0;i<bs;i++){const w=Math.random()*2-1;d[i]=(lo+0.02*w)/1.02;lo=d[i];d[i]*=0.13;}
    const rs=this.ctx.createBufferSource();rs.buffer=nb;rs.loop=true;
    const rf=this.ctx.createBiquadFilter();rf.type='lowpass';rf.frequency.value=1800;
    const rg=this.ctx.createGain();rg.gain.value=0.15;
    rs.connect(rf).connect(rg).connect(this.ctx.destination);rs.start();
    const o1=this.ctx.createOscillator();o1.type='sine';o1.frequency.value=55;
    const g1=this.ctx.createGain();g1.gain.value=0.06;
    o1.connect(g1).connect(this.ctx.destination);o1.start();
    this.bgm=this.ctx.createGain();this.bgm.gain.value=0.35;
    this.bgm.connect(this.ctx.destination);
    this.startBGM();
  }
  note(freq,dur,vol,when){
    if(!this.ctx||this.muted)return;
    const t=this.ctx.currentTime+(when||0);
    const o=this.ctx.createOscillator();o.type='sine';o.frequency.value=freq;
    const g=this.ctx.createGain();
    g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(vol,t+0.02);
    g.gain.exponentialRampToValueAtTime(0.001,t+dur);
    o.connect(g).connect(this.bgm);o.start(t);o.stop(t+dur+0.05);
    const o2=this.ctx.createOscillator();o2.type='triangle';o2.frequency.value=freq*2;
    const g2=this.ctx.createGain();
    g2.gain.setValueAtTime(0,t);g2.gain.linearRampToValueAtTime(vol*0.2,t+0.02);
    g2.gain.exponentialRampToValueAtTime(0.001,t+dur*0.6);
    o2.connect(g2).connect(this.bgm);o2.start(t);o2.stop(t+dur*0.6+0.05);
  }
  startBGM(){
    const mel=[220,0,261.63,0,246.94,0,220,0,174.61,0,196,0,0,0,220,0,261.63,0,246.94,0,196,0,174.61,0,164.81,0,0,0];
    const bass=[110,110,98,98,87.31,87.31,82.41,82.41,110,110,98,98,87.31,87.31,82.41,82.41];
    const dur=0.55;let time=this.ctx.currentTime+0.5;
    const sched=()=>{if(!this.ctx)return;const now=this.ctx.currentTime;
      while(time<now+6){const i=this.step%mel.length;const n=mel[i],b=bass[Math.floor(this.step/2)%bass.length];
        if(n>0)this.note(n,dur*1.6,0.09+this.tension*0.07,time-now);
        if(this.step%2===0)this.note(b,dur*2.4,0.06+this.tension*0.04,time-now);
        time+=dur;this.step++;}
      this.timer=setTimeout(sched,4000);};
    sched();
  }
  play(kind){
    if(!this.ctx||this.muted)return;
    const t=this.ctx.currentTime;
    if(kind==='shoot'){
      const c=this.ctx.createOscillator();const cg=this.ctx.createGain();
      c.type='square';c.frequency.setValueAtTime(1900,t);
      cg.gain.setValueAtTime(0.5,t);cg.gain.exponentialRampToValueAtTime(0.001,t+0.04);
      c.connect(cg).connect(this.ctx.destination);c.start(t);c.stop(t+0.05);
      const bs=this.ctx.sampleRate*0.3;
      const nb=this.ctx.createBuffer(1,bs,this.ctx.sampleRate);
      const d=nb.getChannelData(0);
      for(let i=0;i<bs;i++)d[i]=(Math.random()*2-1)*Math.exp(-i/(this.ctx.sampleRate*0.05));
      const ns=this.ctx.createBufferSource();ns.buffer=nb;
      const ng=this.ctx.createGain();ng.gain.setValueAtTime(0.7,t);ng.gain.exponentialRampToValueAtTime(0.001,t+0.28);
      ns.connect(ng).connect(this.ctx.destination);ns.start(t);
      const sub=this.ctx.createOscillator();const sg=this.ctx.createGain();
      sub.type='sine';sub.frequency.setValueAtTime(140,t);sub.frequency.exponentialRampToValueAtTime(35,t+0.2);
      sg.gain.setValueAtTime(0.55,t);sg.gain.exponentialRampToValueAtTime(0.001,t+0.25);
      sub.connect(sg).connect(this.ctx.destination);sub.start(t);sub.stop(t+0.26);
    } else if(kind==='empty'){
      [320,240].forEach((f,i)=>{const s=t+i*0.05;const o=this.ctx.createOscillator();o.type='square';o.frequency.setValueAtTime(f,s);const g=this.ctx.createGain();g.gain.setValueAtTime(0.15,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.05);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.06);});
    } else if(kind==='reload'){
      [1400,900,1800].forEach((f,i)=>{const s=t+i*0.22;const o=this.ctx.createOscillator();o.type='square';o.frequency.setValueAtTime(f,s);const g=this.ctx.createGain();g.gain.setValueAtTime(0.18,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.06);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.07);});
    } else if(kind==='doorCreak'){
      for(let i=0;i<12;i++){const s=t+i*0.09;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(45+Math.random()*22,s);o.frequency.linearRampToValueAtTime(25+Math.random()*15,s+0.4);const f=this.ctx.createBiquadFilter();f.type='lowpass';f.frequency.value=250;const g=this.ctx.createGain();g.gain.setValueAtTime(0,s);g.gain.linearRampToValueAtTime(0.05,s+0.03);g.gain.exponentialRampToValueAtTime(0.001,s+0.4);o.connect(f).connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.45);}
    } else if(kind==='doorClose'){
      const o=this.ctx.createOscillator();const g=this.ctx.createGain();
      o.type='sine';o.frequency.setValueAtTime(140,t);o.frequency.exponentialRampToValueAtTime(45,t+0.18);
      g.gain.setValueAtTime(0.45,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.25);
      o.connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+0.3);
    } else if(kind==='codeOk'){
      [659,830,1046].forEach((f,i)=>{const s=t+i*0.1;const o=this.ctx.createOscillator();o.type='sine';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0,s);g.gain.linearRampToValueAtTime(0.12,s+0.02);g.gain.exponentialRampToValueAtTime(0.001,s+0.4);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.45);});
    } else if(kind==='codeFail'){
      [220,180].forEach((f,i)=>{const s=t+i*0.1;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0.12,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.35);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.4);});
    } else if(kind==='pickup'){
      [587,880,1174].forEach((f,i)=>{const s=t+i*0.08;const o=this.ctx.createOscillator();o.type='sine';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0,s);g.gain.linearRampToValueAtTime(0.13,s+0.02);g.gain.exponentialRampToValueAtTime(0.001,s+0.3);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.35);});
    } else if(kind==='transform'){
      [261,329,392,523,659,783,1046].forEach((f,i)=>{const s=t+i*0.09;const o=this.ctx.createOscillator();o.type='triangle';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0,s);g.gain.linearRampToValueAtTime(0.12,s+0.02);g.gain.exponentialRampToValueAtTime(0.001,s+0.5);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.55);});
    } else if(kind==='paper'){
      [1200,1500].forEach((f,i)=>{const s=t+i*0.06;const o=this.ctx.createOscillator();o.type='sine';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0.06,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.15);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.2);});
    } else if(kind==='zombieGrowl'){
      const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(110,t);o.frequency.linearRampToValueAtTime(70,t+0.9);
      const f=this.ctx.createBiquadFilter();f.type='lowpass';f.frequency.value=450;
      const g=this.ctx.createGain();g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(0.13,t+0.2);g.gain.exponentialRampToValueAtTime(0.001,t+1.0);
      o.connect(f).connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+1.1);
    } else if(kind==='zombieHurt'){
      const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(180,t);o.frequency.exponentialRampToValueAtTime(80,t+0.5);
      const g=this.ctx.createGain();g.gain.setValueAtTime(0.11,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.55);
      o.connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+0.6);
    } else if(kind==='zombieDie'){
      const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(140,t);o.frequency.exponentialRampToValueAtTime(30,t+1.2);
      const g=this.ctx.createGain();g.gain.setValueAtTime(0.15,t);g.gain.exponentialRampToValueAtTime(0.001,t+1.3);
      o.connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+1.35);
    } else if(kind==='playerHit'){
      [110,80].forEach((f,i)=>{const s=t+i*0.08;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0.16,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.4);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.45);});
    } else if(kind==='footstep'){
      const o=this.ctx.createOscillator();o.type='sine';o.frequency.setValueAtTime(70+Math.random()*25,t);o.frequency.exponentialRampToValueAtTime(35,t+0.06);
      const g=this.ctx.createGain();g.gain.setValueAtTime(0.025,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.1);
      o.connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+0.12);
    } else if(kind==='talking'){
      const o=this.ctx.createOscillator();o.type='square';o.frequency.value=800+Math.random()*150;
      const g=this.ctx.createGain();g.gain.setValueAtTime(0.012,t);g.gain.exponentialRampToValueAtTime(0.001,t+0.03);
      o.connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+0.04);
    } else if(kind==='win'){
      [523,659,783,1046,1318].forEach((f,i)=>{const s=t+i*0.15;const o=this.ctx.createOscillator();o.type='sine';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0,s);g.gain.linearRampToValueAtTime(0.13,s+0.03);g.gain.exponentialRampToValueAtTime(0.001,s+1.0);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+1.05);});
    } else if(kind==='death'){
      [392,329,261,196,130].forEach((f,i)=>{const s=t+i*0.18;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.value=f;const g=this.ctx.createGain();g.gain.setValueAtTime(0.14,s);g.gain.exponentialRampToValueAtTime(0.001,s+1.2);o.connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+1.25);});
    } else if(kind==='dogBark'){
      [380,300,420].forEach((f,i)=>{const s=t+i*0.12;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(f,s);o.frequency.exponentialRampToValueAtTime(f*0.6,s+0.1);const g=this.ctx.createGain();g.gain.setValueAtTime(0.18,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.15);const f2=this.ctx.createBiquadFilter();f2.type='bandpass';f2.frequency.value=600;f2.Q.value=3;o.connect(f2).connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.18);});
    } else if(kind==='fire'){
      for(let i=0;i<10;i++){const s=t+i*0.08;const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(200+Math.random()*400,s);const g=this.ctx.createGain();g.gain.setValueAtTime(0.06,s);g.gain.exponentialRampToValueAtTime(0.001,s+0.2);const f=this.ctx.createBiquadFilter();f.type='bandpass';f.frequency.value=1200;o.connect(f).connect(g).connect(this.ctx.destination);o.start(s);o.stop(s+0.25);}
    } else if(kind==='gasFill'){
      const o=this.ctx.createOscillator();o.type='sawtooth';o.frequency.setValueAtTime(90,t);o.frequency.linearRampToValueAtTime(140,t+3.8);
      const g=this.ctx.createGain();g.gain.setValueAtTime(0,t);g.gain.linearRampToValueAtTime(0.06,t+0.3);g.gain.setValueAtTime(0.06,t+3.5);g.gain.exponentialRampToValueAtTime(0.001,t+4.0);
      const f=this.ctx.createBiquadFilter();f.type='bandpass';f.frequency.value=400;f.Q.value=5;
      o.connect(f).connect(g).connect(this.ctx.destination);o.start(t);o.stop(t+4.1);
    }
  }
}
const audio=new AudioEngine();

function wallTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.fillStyle='#2a2520';x.fillRect(0,0,256,256);for(let i=0;i<20;i++){const y0=Math.random()*256;x.strokeStyle='rgba(20,15,10,'+(0.2+Math.random()*0.3)+')';x.lineWidth=1+Math.random()*2;x.beginPath();x.moveTo(0,y0);x.bezierCurveTo(80,y0+Math.random()*10-5,180,y0+Math.random()*10-5,256,y0+Math.random()*4-2);x.stroke();}for(let i=0;i<15;i++){const px=Math.random()*256,py=Math.random()*256,r=15+Math.random()*40;const g=x.createRadialGradient(px,py,0,px,py,r);g.addColorStop(0,'rgba(10,5,0,0.25)');g.addColorStop(1,'rgba(10,5,0,0)');x.fillStyle=g;x.beginPath();x.arc(px,py,r,0,Math.PI*2);x.fill();}const t=new THREE.CanvasTexture(cv);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.anisotropy=4;return t;}
function floorWoodTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.fillStyle='#3a2818';x.fillRect(0,0,256,256);for(let y=0;y<256;y+=32){const s=0.85+Math.random()*0.15;x.fillStyle='rgba('+Math.floor(75*s)+','+Math.floor(50*s)+','+Math.floor(28*s)+',0.7)';x.fillRect(0,y,256,30);x.strokeStyle='rgba(15,8,0,0.5)';x.lineWidth=2;x.beginPath();x.moveTo(0,y);x.lineTo(256,y);x.stroke();}for(let i=0;i<20;i++){const px=Math.random()*256,py=Math.random()*256,r=20+Math.random()*60;const g=x.createRadialGradient(px,py,0,px,py,r);g.addColorStop(0,'rgba(0,0,0,0.2)');g.addColorStop(1,'rgba(0,0,0,0)');x.fillStyle=g;x.beginPath();x.arc(px,py,r,0,Math.PI*2);x.fill();}const t=new THREE.CanvasTexture(cv);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.repeat.set(3,3);t.anisotropy=4;return t;}
function floorTileTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.fillStyle='#1e2228';x.fillRect(0,0,256,256);x.strokeStyle='#0e1014';x.lineWidth=3;for(let i=0;i<256;i+=64){x.beginPath();x.moveTo(i,0);x.lineTo(i,256);x.stroke();x.beginPath();x.moveTo(0,i);x.lineTo(256,i);x.stroke();}for(let i=0;i<25;i++){const px=Math.random()*256,py=Math.random()*256,r=5+Math.random()*20;x.fillStyle='rgba(5,3,0,'+(Math.random()*0.35)+')';x.beginPath();x.arc(px,py,r,0,Math.PI*2);x.fill();}const t=new THREE.CanvasTexture(cv);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.repeat.set(4,4);t.anisotropy=4;return t;}
function basementFloorTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.fillStyle='#252830';x.fillRect(0,0,256,256);x.strokeStyle='#101318';x.lineWidth=3;for(let i=0;i<256;i+=80){x.beginPath();x.moveTo(i,0);x.lineTo(i,256);x.stroke();x.beginPath();x.moveTo(0,i);x.lineTo(256,i);x.stroke();}for(let i=0;i<40;i++){const px=Math.random()*256,py=Math.random()*256,r=3+Math.random()*15;x.fillStyle='rgba(5,3,0,'+(Math.random()*0.5)+')';x.beginPath();x.arc(px,py,r,0,Math.PI*2);x.fill();}const t=new THREE.CanvasTexture(cv);t.wrapS=t.wrapT=THREE.RepeatWrapping;t.repeat.set(4,4);t.anisotropy=4;return t;}
function woodDoorTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=512;const x=cv.getContext('2d');x.fillStyle='#4a2a15';x.fillRect(0,0,256,512);for(let i=0;i<30;i++){const y0=Math.random()*512;x.strokeStyle='rgba(15,5,0,'+(0.2+Math.random()*0.3)+')';x.lineWidth=1+Math.random()*2;x.beginPath();x.moveTo(0,y0);x.bezierCurveTo(80,y0+Math.random()*15-7,180,y0+Math.random()*15-7,256,y0+Math.random()*8-4);x.stroke();}x.strokeStyle='#1a0d05';x.lineWidth=10;x.strokeRect(20,30,216,200);x.strokeRect(20,270,216,220);x.fillStyle='#d4af37';x.beginPath();x.arc(220,270,14,0,Math.PI*2);x.fill();x.fillStyle='#ffe87a';x.beginPath();x.arc(216,266,5,0,Math.PI*2);x.fill();return new THREE.CanvasTexture(cv);}
function metalDoorTexture(){const cv=document.createElement('canvas');cv.width=256;cv.height=512;const x=cv.getContext('2d');x.fillStyle='#7a8088';x.fillRect(0,0,256,512);for(let i=0;i<256;i+=8){const v=0.9+Math.random()*0.15;x.fillStyle='rgba('+Math.floor(180*v)+','+Math.floor(185*v)+','+Math.floor(195*v)+',0.35)';x.fillRect(i,0,4,512);}x.fillStyle='#e8b53c';x.fillRect(0,240,256,32);x.fillStyle='#0a0a0a';x.font='bold 22px Arial';x.textAlign='center';x.textBaseline='middle';x.fillText('ELEVATOR',128,256);x.strokeStyle='#3a3f45';x.lineWidth=6;x.beginPath();x.moveTo(128,0);x.lineTo(128,512);x.stroke();return new THREE.CanvasTexture(cv);}
function makeSignTexture(text){const cv=document.createElement('canvas');cv.width=512;cv.height=128;const ctx=cv.getContext('2d');ctx.fillStyle='#2a2a2a';ctx.fillRect(0,0,512,128);ctx.strokeStyle='#111';ctx.lineWidth=8;ctx.strokeRect(4,4,504,120);ctx.strokeStyle='#444';ctx.lineWidth=2;ctx.strokeRect(12,12,488,104);ctx.fillStyle='#e0e0d0';ctx.font='bold 54px Cairo, Arial';ctx.textAlign='center';ctx.textBaseline='middle';ctx.shadowColor='#000';ctx.shadowBlur=6;ctx.shadowOffsetX=2;ctx.shadowOffsetY=2;ctx.fillText(text,256,64);ctx.shadowBlur=0;ctx.shadowOffsetX=0;ctx.shadowOffsetY=0;ctx.fillStyle='rgba(120,10,10,0.85)';for(let i=0;i<5;i++){const px=30+Math.random()*450;const py=10+Math.random()*30;const r=6+Math.random()*18;ctx.beginPath();ctx.arc(px,py,r,0,Math.PI*2);ctx.fill();ctx.beginPath();ctx.moveTo(px-3,py);ctx.lineTo(px+3,py);ctx.lineTo(px+2,py+20+Math.random()*50);ctx.lineTo(px-2,py+20+Math.random()*50);ctx.closePath();ctx.fill();}return new THREE.CanvasTexture(cv);}
function faceNoura(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.clearRect(0,0,256,256);x.fillStyle='rgba(230,120,140,0.5)';x.beginPath();x.arc(55,170,32,0,Math.PI*2);x.fill();x.beginPath();x.arc(201,170,32,0,Math.PI*2);x.fill();x.fillStyle='#f0f0f5';x.beginPath();x.ellipse(85,110,30,24,0,0,Math.PI*2);x.fill();x.beginPath();x.ellipse(171,110,30,24,0,0,Math.PI*2);x.fill();const ig=x.createRadialGradient(85,113,3,85,113,16);ig.addColorStop(0,'#a87040');ig.addColorStop(0.55,'#6a3a10');ig.addColorStop(1,'#2a1005');x.fillStyle=ig;x.beginPath();x.arc(85,113,16,0,Math.PI*2);x.fill();const ig2=x.createRadialGradient(171,113,3,171,113,16);ig2.addColorStop(0,'#a87040');ig2.addColorStop(0.55,'#6a3a10');ig2.addColorStop(1,'#2a1005');x.fillStyle=ig2;x.beginPath();x.arc(171,113,16,0,Math.PI*2);x.fill();x.fillStyle='#000';x.beginPath();x.arc(85,113,7,0,Math.PI*2);x.fill();x.beginPath();x.arc(171,113,7,0,Math.PI*2);x.fill();x.fillStyle='#fff';x.beginPath();x.arc(79,106,5.5,0,Math.PI*2);x.fill();x.beginPath();x.arc(165,106,5.5,0,Math.PI*2);x.fill();x.strokeStyle='#0a0505';x.lineWidth=7;x.lineCap='round';x.beginPath();x.moveTo(55,96);x.quadraticCurveTo(85,88,115,96);x.stroke();x.beginPath();x.moveTo(141,96);x.quadraticCurveTo(171,88,201,96);x.stroke();x.strokeStyle='#2a1808';x.lineWidth=7;x.beginPath();x.moveTo(55,74);x.quadraticCurveTo(85,68,115,74);x.stroke();x.beginPath();x.moveTo(141,74);x.quadraticCurveTo(171,68,201,74);x.stroke();x.strokeStyle='rgba(160,100,70,0.8)';x.lineWidth=3;x.beginPath();x.moveTo(126,140);x.quadraticCurveTo(120,158,128,164);x.stroke();x.fillStyle='#c05868';x.beginPath();x.ellipse(128,196,24,11,0,0,Math.PI*2);x.fill();return new THREE.CanvasTexture(cv);}
function faceAghasa(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.clearRect(0,0,256,256);x.fillStyle='#a8a098';x.beginPath();x.ellipse(85,110,28,16,0,0,Math.PI*2);x.fill();x.beginPath();x.ellipse(171,110,28,16,0,0,Math.PI*2);x.fill();x.fillStyle='#2a1818';x.beginPath();x.arc(85,112,10,0,Math.PI*2);x.fill();x.beginPath();x.arc(171,112,10,0,Math.PI*2);x.fill();x.fillStyle='#000';x.beginPath();x.arc(85,112,5,0,Math.PI*2);x.fill();x.beginPath();x.arc(171,112,5,0,Math.PI*2);x.fill();x.fillStyle='#3a0505';x.beginPath();x.ellipse(128,200,20,10,0,0,Math.PI*2);x.fill();return new THREE.CanvasTexture(cv);}
function faceRahman(){const cv=document.createElement('canvas');cv.width=256;cv.height=256;const x=cv.getContext('2d');x.clearRect(0,0,256,256);x.fillStyle='#c89870';x.beginPath();x.arc(128,128,110,0,Math.PI*2);x.fill();x.fillStyle='#5a3a1a';x.beginPath();x.arc(128,100,112,Math.PI,0);x.fill();x.fillStyle='#f0e8d8';x.beginPath();x.ellipse(85,110,26,22,0,0,Math.PI*2);x.fill();x.beginPath();x.ellipse(171,110,26,22,0,0,Math.PI*2);x.fill();x.fillStyle='#3a2410';x.beginPath();x.arc(85,113,14,0,Math.PI*2);x.fill();x.beginPath();x.arc(171,113,14,0,Math.PI*2);x.fill();x.fillStyle='#000';x.beginPath();x.arc(85,113,6,0,Math.PI*2);x.fill();x.beginPath();x.arc(171,113,6,0,Math.PI*2);x.fill();x.fillStyle='#fff';x.beginPath();x.arc(79,106,4.5,0,Math.PI*2);x.fill();x.beginPath();x.arc(165,106,4.5,0,Math.PI*2);x.fill();x.strokeStyle='#1a0a05';x.lineWidth=6;x.lineCap='round';x.beginPath();x.moveTo(55,90);x.quadraticCurveTo(85,82,115,90);x.stroke();x.beginPath();x.moveTo(141,90);x.quadraticCurveTo(171,82,201,90);x.stroke();x.strokeStyle='#3a2410';x.lineWidth=6;x.beginPath();x.moveTo(60,72);x.quadraticCurveTo(85,68,110,72);x.stroke();x.beginPath();x.moveTo(146,72);x.quadraticCurveTo(171,68,196,72);x.stroke();x.strokeStyle='#5a3a1a';x.lineWidth=3;x.beginPath();x.moveTo(126,140);x.quadraticCurveTo(120,158,128,164);x.stroke();x.fillStyle='#7a4030';x.beginPath();x.ellipse(128,196,22,10,0,0,Math.PI*2);x.fill();return new THREE.CanvasTexture(cv);}

const canvas=document.getElementById('c');
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x08060a);
scene.fog=new THREE.FogExp2(0x08060a,0.045);
const camera=new THREE.PerspectiveCamera(62,innerWidth/innerHeight,0.05,80);
const renderer=new THREE.WebGLRenderer({canvas,antialias:false,powerPreference:'high-performance'});
renderer.setSize(innerWidth,innerHeight);
renderer.setPixelRatio(Math.min(devicePixelRatio,1.5));
renderer.shadowMap.enabled=false;
scene.add(new THREE.AmbientLight(0x1a1e28,0.55));
const moonLight=new THREE.DirectionalLight(0x5060a0,0.15);
moonLight.position.set(-5,8,-3);scene.add(moonLight);

const wallMat=new THREE.MeshStandardMaterial({map:wallTexture(),roughness:0.9});
const floorWoodMat=new THREE.MeshStandardMaterial({map:floorWoodTexture(),roughness:0.75});
const floorTileMat=new THREE.MeshStandardMaterial({map:floorTileTexture(),roughness:0.85});
const basementFloorMat=new THREE.MeshStandardMaterial({map:basementFloorTexture(),roughness:0.9});
const ceilMat=new THREE.MeshStandardMaterial({color:0x1a1612,roughness:0.95});
const woodDoorMat=new THREE.MeshStandardMaterial({map:woodDoorTexture(),roughness:0.7});
const metalDoorMat=new THREE.MeshStandardMaterial({map:metalDoorTexture(),roughness:0.4,metalness:0.75});
const frameMat=new THREE.MeshStandardMaterial({color:0x1a0d05,roughness:0.8});
const metalFrameMat=new THREE.MeshStandardMaterial({color:0x4a4f55,metalness:0.8,roughness:0.35});
const metalMat=new THREE.MeshStandardMaterial({color:0x2a3038,metalness:0.65,roughness:0.45});
const goldMat=new THREE.MeshStandardMaterial({color:0xd4af37,metalness:0.85,roughness:0.25,emissive:0x442200,emissiveIntensity:0.25});
const darkMetalMat=new THREE.MeshStandardMaterial({color:0x1a1e26,metalness:0.9,roughness:0.2});

const state={running:false,transitioning:false,hasGun:false,hasCatSuit:false,catSuitOn:false,hasArchiveKey:false,hasElevatorCard:false,hasWill:false,notes:[],inDialog:false,inNote:false,inCode:false,inBag:false,dlgQueue:[],dlgOnEnd:null,hp:5,time:0,invuln:0,magAmmo:7,reserveAmmo:21,reloading:false,reloadTimer:0,autoReloadTimer:0,paintingPushed:false,safeOpened:false,clinicDoorOpened:false,aiming:false,
  inBasement:false,hasMedicine:false,hasLighter:false,lighterFilled:false,hasNurseKey:false,hasCellKey:false,
  morgueOpened:false,breakRoomOpened:false,cellOpened:false,rahmanTalked:false,dogKilled:false,patientTurned:false,patientKilled:false,
  nurseNoteRead:false,refillingGas:false,gasFillTimer:0,morgueRopeBurnt:false,kitchenDrawerOpened:false,
  medPicked1:false,medPicked2:false,ammoPicked1:false,ammoPicked2:false,morgueEntered:false};
const MAG_SIZE=7;
const player={x:0,z:-20,velX:0,velZ:0,angle:Math.PI,speed:2.8,walkTime:0,moving:false};

let joyActive=false,joyTid=null,joyCX=0,joyCY=0,joyDX=0,joyDY=0;
const JOY_R=58;
window._keys={};
const colliders=[];
const wallMeshes=[];
const DOOR_W=2.0;
const WALL_T=0.35;
const H=3.4;
const DOOR_H=2.9;

function computeCollider(obj){scene.updateMatrixWorld(true);return new THREE.Box3().setFromObject(obj);}
function addBoxCollider(mesh){const box=computeCollider(mesh);colliders.push(box);wallMeshes.push(mesh);}
function addWall(x1,z1,x2,z2){const ww=Math.abs(x2-x1)||WALL_T;const dd=Math.abs(z2-z1)||WALL_T;const m=new THREE.Mesh(new THREE.BoxGeometry(ww,H,dd),wallMat);m.position.set((x1+x2)/2,H/2,(z1+z2)/2);m.castShadow=false;m.receiveShadow=false;scene.add(m);addBoxCollider(m);return m;}
function addFloor(x1,z1,x2,z2,mat){const m=new THREE.Mesh(new THREE.PlaneGeometry(x2-x1,z2-z1),mat);m.rotation.x=-Math.PI/2;m.position.set((x1+x2)/2,0,(z1+z2)/2);m.receiveShadow=false;scene.add(m);}
function addCeil(x1,z1,x2,z2){const m=new THREE.Mesh(new THREE.PlaneGeometry(x2-x1,z2-z1),ceilMat);m.rotation.x=Math.PI/2;m.position.set((x1+x2)/2,H,(z1+z2)/2);scene.add(m);}

// ═════════ القصر ═════════
addFloor(-5,-24,5,-16,floorWoodMat);addCeil(-5,-24,5,-16);
addWall(-5,-24,5,-24);addWall(-5,-24,-5,-16);addWall(5,-24,5,-16);
addWall(-5,-16,-1,-16);addWall(1,-16,5,-16);
(function(){const lintel=new THREE.Mesh(new THREE.BoxGeometry(2,H-DOOR_H-0.15,WALL_T),wallMat);lintel.position.set(0,DOOR_H+0.15+(H-DOOR_H-0.15)/2,-16);scene.add(lintel);})();
const r1Light=new THREE.PointLight(0xffdd99,1.4,10,2);r1Light.position.set(0,2.8,-20);scene.add(r1Light);

addFloor(-2,-16,2,10,floorTileMat);addCeil(-2,-16,2,10);
addWall(-2,-16,-2,-11);addWall(-2,-9,-2,1);addWall(-2,3,-2,10);
addWall(2,-16,2,-7);addWall(2,-5,2,10);
addWall(-2,10,-1,10);addWall(1,10,2,10);
(function(){const lintel=new THREE.Mesh(new THREE.BoxGeometry(2,H-DOOR_H-0.15,WALL_T),wallMat);lintel.position.set(0,DOOR_H+0.15+(H-DOOR_H-0.15)/2,10);scene.add(lintel);})();
for(let z=-12;z<=8;z+=6){const l=new THREE.PointLight(0xffeedd,0.9,8,2);l.position.set(0,3.0,z);scene.add(l);}

addFloor(-10,-14,-2,-6,floorTileMat);addCeil(-10,-14,-2,-6);
addWall(-10,-14,-2,-14);addWall(-10,-14,-10,-6);addWall(-10,-6,-2,-6);
(function(){const lintel=new THREE.Mesh(new THREE.BoxGeometry(WALL_T,H-DOOR_H-0.15,2),wallMat);lintel.position.set(-2,DOOR_H+0.15+(H-DOOR_H-0.15)/2,-10);scene.add(lintel);})();
const clinicLight=new THREE.PointLight(0xccddff,1.0,10,2);clinicLight.position.set(-6,2.8,-10);scene.add(clinicLight);

addFloor(-10,-2,-2,6,floorTileMat);addCeil(-10,-2,-2,6);
addWall(-10,-2,-2,-2);addWall(-10,-2,-10,6);addWall(-10,6,-2,6);
(function(){const lintel=new THREE.Mesh(new THREE.BoxGeometry(WALL_T,H-DOOR_H-0.15,2),wallMat);lintel.position.set(-2,DOOR_H+0.15+(H-DOOR_H-0.15)/2,2);scene.add(lintel);})();
const xrayLight=new THREE.PointLight(0x88aaff,1.0,8,2);xrayLight.position.set(-6,2.8,2);scene.add(xrayLight);

addFloor(2,-10,10,-2,floorWoodMat);addCeil(2,-10,10,-2);
addWall(2,-10,10,-10);addWall(10,-10,10,-2);addWall(2,-2,10,-2);
(function(){const lintel=new THREE.Mesh(new THREE.BoxGeometry(WALL_T,H-DOOR_H-0.15,2),wallMat);lintel.position.set(2,DOOR_H+0.15+(H-DOOR_H-0.15)/2,-6);scene.add(lintel);})();
const archLight=new THREE.PointLight(0xffbb88,0.9,8,2);archLight.position.set(6,2.8,-6);scene.add(archLight);

addFloor(-3,10,3,16,floorTileMat);addCeil(-3,10,3,16);
addWall(-3,10,-3,16);addWall(3,10,3,16);addWall(-3,16,3,16);
const elevLight=new THREE.PointLight(0x66ffaa,1.2,8,2);elevLight.position.set(0,2.8,13);scene.add(elevLight);

const doors=[];
function buildDoor(cfg){
  const g=new THREE.Group();g.position.set(cfg.x,0,cfg.z);if(cfg.axis==='x')g.rotation.y=Math.PI/2;scene.add(g);
  const isMetal=!!cfg.isMetal;const fMat=isMetal?metalFrameMat:frameMat;const dMat=isMetal?metalDoorMat:woodDoorMat;
  const topFrame=new THREE.Mesh(new THREE.BoxGeometry(0.4,0.15,DOOR_W+0.2),fMat);topFrame.position.set(0,DOOR_H+0.075,DOOR_W/2);g.add(topFrame);
  const side1=new THREE.Mesh(new THREE.BoxGeometry(0.4,DOOR_H+0.15,0.12),fMat);side1.position.set(0,(DOOR_H+0.15)/2,-0.06);g.add(side1);
  const side2=new THREE.Mesh(new THREE.BoxGeometry(0.4,DOOR_H+0.15,0.12),fMat);side2.position.set(0,(DOOR_H+0.15)/2,DOOR_W+0.06);g.add(side2);
  const hinge=new THREE.Group();g.add(hinge);
  const panel=new THREE.Mesh(new THREE.BoxGeometry(0.09,DOOR_H-0.1,DOOR_W),dMat);panel.position.set(0,(DOOR_H-0.1)/2+0.05,DOOR_W/2);hinge.add(panel);wallMeshes.push(panel);
  if(!isMetal){const knob=new THREE.Mesh(new THREE.SphereGeometry(0.06,10,10),goldMat);knob.position.set(0.07,DOOR_H*0.48,DOOR_W-0.3);hinge.add(knob);}else{const handle=new THREE.Mesh(new THREE.BoxGeometry(0.05,0.45,0.04),metalFrameMat);handle.position.set(0.07,DOOR_H*0.5,DOOR_W/2);hinge.add(handle);}
  if(cfg.locked){const plate=new THREE.Mesh(new THREE.BoxGeometry(0.04,0.14,0.12),new THREE.MeshBasicMaterial({color:0xff2020}));plate.position.set(0.07,0.95,DOOR_W-0.3);hinge.add(plate);}
  let bx1,bx2,bz1,bz2;
  if(cfg.axis==='x'){bx1=cfg.x;bx2=cfg.x+DOOR_W;bz1=cfg.z-WALL_T/2;bz2=cfg.z+WALL_T/2;}
  else{bx1=cfg.x-WALL_T/2;bx2=cfg.x+WALL_T/2;bz1=cfg.z;bz2=cfg.z+DOOR_W;}
  const d={group:g,hinge,panel,x:cfg.x,z:cfg.z,axis:cfg.axis,label:cfg.label,code:cfg.code,keyName:cfg.keyName,locked:!!cfg.locked,open:false,openAngle:cfg.openAngle||Math.PI/2,bx1,bx2,bz1,bz2,needsCard:!!cfg.needsCard,isMetal:isMetal,isBasement:!!cfg.isBasement};
  doors.push(d);return d;
}

const dStart=buildDoor({x:-1,z:-16,axis:'x',openAngle:-Math.PI/2,label:'باب الغرفة',keyName:'roomKey',locked:true});
const dClinic=buildDoor({x:-2,z:-11,axis:'z',openAngle:-Math.PI/2,label:'عيادة د. أغاسا',code:'8317',locked:true});
const dXray=buildDoor({x:-2,z:1,axis:'z',openAngle:-Math.PI/2,label:'غرفة الأشعة'});
const dArchive=buildDoor({x:2,z:-7,axis:'z',openAngle:Math.PI/2,label:'الأرشيف',keyName:'archiveKey',locked:true});
const dElevator=buildDoor({x:-1,z:10,axis:'x',openAngle:-Math.PI/2,label:'الأصنصير',needsCard:true,locked:true,isMetal:true});

function addSignAt(text,x,y,z,rotY){const tex=makeSignTexture(text);const mat=new THREE.MeshStandardMaterial({map:tex,roughness:0.7});const m=new THREE.Mesh(new THREE.PlaneGeometry(1.5,0.38),mat);m.position.set(x,y,z);m.rotation.y=rotY||0;scene.add(m);}
addSignAt('الغرفة',1.7,1.9,-15.75,Math.PI);
addSignAt('عيادة د. أغاسا',-2.06,1.9,-12.3,Math.PI/2);
addSignAt('غرفة الأشعة',-2.06,1.9,-0.3,Math.PI/2);
addSignAt('الأرشيف',2.06,1.9,-8.3,-Math.PI/2);
addSignAt('الأصنصير',1.7,1.9,10.15,Math.PI);

const catSuitMat=new THREE.MeshStandardMaterial({color:0x0a0a0e,roughness:0.3,metalness:0.25,emissive:0x080810,emissiveIntensity:0.3});
const girlSkinMat=new THREE.MeshStandardMaterial({color:0xe8b888,roughness:0.75});
const girlDressMat=new THREE.MeshStandardMaterial({color:0x1a2540,roughness:0.85});
const girlTrimMat=new THREE.MeshStandardMaterial({color:0x5a7faa,emissive:0x152840,emissiveIntensity:0.25});
const girlHairMat=new THREE.MeshStandardMaterial({color:0x0a0505,roughness:0.9});
const shoeMat=new THREE.MeshStandardMaterial({color:0x0a0a0a,roughness:0.75});

const noura=new THREE.Group();scene.add(noura);const parts={};
(function buildNoura(){
  const hips=new THREE.Group();hips.position.y=0.9;noura.add(hips);parts.hips=hips;
  function leg(side){const hipG=new THREE.Group();hipG.position.set(side*0.11,0,0);hips.add(hipG);const th=new THREE.Mesh(new THREE.CylinderGeometry(0.085,0.075,0.45,10),girlSkinMat);th.position.y=-0.225;hipG.add(th);const kneeG=new THREE.Group();kneeG.position.y=-0.45;hipG.add(kneeG);const sh=new THREE.Mesh(new THREE.CylinderGeometry(0.075,0.06,0.42,10),girlSkinMat);sh.position.y=-0.21;kneeG.add(sh);const ft=new THREE.Mesh(new THREE.BoxGeometry(0.14,0.09,0.24),shoeMat);ft.position.set(0,-0.45,0.06);kneeG.add(ft);return {hip:hipG,knee:kneeG,thigh:th,shin:sh,foot:ft};}
  parts.legL=leg(-1);parts.legR=leg(1);
  const torso=new THREE.Mesh(new THREE.CylinderGeometry(0.22,0.34,0.85,12),girlDressMat);torso.position.y=0.42;hips.add(torso);parts.torso=torso;
  const collar=new THREE.Mesh(new THREE.TorusGeometry(0.16,0.035,8,16),girlTrimMat);collar.position.y=0.84;collar.rotation.x=Math.PI/2;hips.add(collar);
  const belt=new THREE.Mesh(new THREE.TorusGeometry(0.24,0.02,8,16),girlTrimMat);belt.position.y=0.35;belt.rotation.x=Math.PI/2;hips.add(belt);
  function arm(side){const shG=new THREE.Group();shG.position.set(side*0.28,0.78,0);hips.add(shG);const ua=new THREE.Mesh(new THREE.CylinderGeometry(0.055,0.05,0.35,10),girlSkinMat);ua.position.y=-0.175;shG.add(ua);const elG=new THREE.Group();elG.position.y=-0.35;shG.add(elG);const fa=new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.045,0.32,10),girlSkinMat);fa.position.y=-0.16;elG.add(fa);const hd=new THREE.Mesh(new THREE.SphereGeometry(0.058,10,10),girlSkinMat);hd.position.y=-0.34;elG.add(hd);return {shoulder:shG,elbow:elG,upper:ua,fore:fa,hand:hd};}
  parts.armL=arm(-1);parts.armR=arm(1);
  const pistol=new THREE.Group();
  const pFrame=new THREE.Mesh(new THREE.BoxGeometry(0.04,0.05,0.16),darkMetalMat);pFrame.position.set(0,-0.01,-0.02);pistol.add(pFrame);
  const pSlide=new THREE.Mesh(new THREE.BoxGeometry(0.038,0.038,0.2),darkMetalMat);pSlide.position.set(0,0.035,-0.03);pistol.add(pSlide);
  const pBarrel=new THREE.Mesh(new THREE.CylinderGeometry(0.012,0.012,0.04,8),new THREE.MeshStandardMaterial({color:0x050608,metalness:0.95,roughness:0.2}));pBarrel.rotation.x=Math.PI/2;pBarrel.position.set(0,0.035,-0.13);pistol.add(pBarrel);
  const pGrip=new THREE.Mesh(new THREE.BoxGeometry(0.036,0.12,0.05),new THREE.MeshStandardMaterial({color:0x1a0f08,roughness:0.85}));pGrip.position.set(0,-0.06,0.045);pGrip.rotation.x=0.22;pistol.add(pGrip);
  const muzzleLight=new THREE.PointLight(0xffaa22,0,5,2);muzzleLight.position.set(0,0.035,-0.18);pistol.add(muzzleLight);parts.muzzleLight=muzzleLight;
  pistol.position.set(0,-0.36,0);pistol.rotation.x=-Math.PI/2;pistol.visible=false;parts.armR.elbow.add(pistol);parts.pistol=pistol;
  const earL=new THREE.Mesh(new THREE.ConeGeometry(0.06,0.16,8),catSuitMat);earL.position.set(-0.14,0.16,0);
  const earR=new THREE.Mesh(new THREE.ConeGeometry(0.06,0.16,8),catSuitMat);earR.position.set(0.14,0.16,0);
  const neck=new THREE.Mesh(new THREE.CylinderGeometry(0.055,0.065,0.1,8),girlSkinMat);neck.position.y=0.92;hips.add(neck);
  const headGrp=new THREE.Group();headGrp.position.y=1.13;hips.add(headGrp);
  const headSphere=new THREE.Mesh(new THREE.SphereGeometry(0.18,20,16),girlSkinMat);headGrp.add(headSphere);parts.headSphere=headSphere;
  const facePlane=new THREE.Mesh(new THREE.PlaneGeometry(0.32,0.32),new THREE.MeshBasicMaterial({map:faceNoura(),transparent:true,depthWrite:false}));facePlane.position.set(0,0,0.176);headGrp.add(facePlane);
  const hairTop=new THREE.Mesh(new THREE.SphereGeometry(0.2,20,16,0,Math.PI*2,0,Math.PI*0.6),girlHairMat);hairTop.position.y=0.02;headGrp.add(hairTop);
  const hairBack=new THREE.Mesh(new THREE.CylinderGeometry(0.13,0.18,0.75,10),girlHairMat);hairBack.position.set(0,-0.32,0.1);hips.add(hairBack);
  headGrp.add(earL);headGrp.add(earR);parts.earL=earL;parts.earR=earR;earL.visible=false;earR.visible=false;
  const tail=new THREE.Group();const t1=new THREE.Mesh(new THREE.CylinderGeometry(0.03,0.025,0.25,6),catSuitMat);t1.position.y=0.12;t1.rotation.x=-0.7;tail.add(t1);const t2=new THREE.Mesh(new THREE.CylinderGeometry(0.025,0.02,0.3,6),catSuitMat);t2.position.set(0,0.28,-0.15);t2.rotation.x=-0.2;tail.add(t2);tail.position.set(0,0.75,0);tail.visible=false;hips.add(tail);parts.tail=tail;
})();
noura.position.set(player.x,0,player.z);
const playerFlashlight=new THREE.SpotLight(0xfff0d0,3.0,20,Math.PI/3,0.5,1.3);playerFlashlight.castShadow=false;scene.add(playerFlashlight);scene.add(playerFlashlight.target);

function buildWardrobe(x,z,rotY){const g=new THREE.Group();const body=new THREE.Mesh(new THREE.BoxGeometry(1.4,2.4,0.7),new THREE.MeshStandardMaterial({color:0x3a2010,roughness:0.85}));body.position.y=1.2;g.add(body);const split=new THREE.Mesh(new THREE.BoxGeometry(0.02,2.35,0.02),new THREE.MeshStandardMaterial({color:0x1a0d05}));split.position.set(0,1.2,0.36);g.add(split);const k1=new THREE.Mesh(new THREE.SphereGeometry(0.03,8,8),goldMat);k1.position.set(-0.1,1.2,0.36);g.add(k1);const k2=new THREE.Mesh(new THREE.SphereGeometry(0.03,8,8),goldMat);k2.position.set(0.1,1.2,0.36);g.add(k2);g.position.set(x,0,z);if(rotY)g.rotation.y=rotY;scene.add(g);colliders.push(computeCollider(body));wallMeshes.push(body);}
buildWardrobe(-3.5,-22.5,-0.35);

function buildFallenChair(x,z,rotY){const g=new THREE.Group();const seat=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.06,0.5),new THREE.MeshStandardMaterial({color:0x4a2a15,roughness:0.85}));seat.position.set(0,0.1,0);g.add(seat);const back=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.5,0.06),new THREE.MeshStandardMaterial({color:0x4a2a15,roughness:0.85}));back.position.set(0,0.15,0.25);back.rotation.x=-0.9;g.add(back);[[-0.2,-0.2],[0.2,-0.2],[-0.2,0.2],[0.2,0.2]].forEach(p=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(0.05,0.4,0.05),new THREE.MeshStandardMaterial({color:0x2a1810,roughness:0.9}));leg.position.set(p[0],-0.1,p[1]);leg.rotation.z=0.15;g.add(leg);});g.position.set(x,0,z);g.rotation.y=rotY||0;scene.add(g);}
buildFallenChair(-4,-21,0.8);buildFallenChair(3.5,-22.5,2.1);buildFallenChair(4,-17.5,-0.4);buildFallenChair(-3.2,-18,1.4);

function buildPaperOnFloor(x,z,rotY){const cv=document.createElement('canvas');cv.width=64;cv.height=64;const cx=cv.getContext('2d');cx.fillStyle='#d8c8a8';cx.fillRect(0,0,64,64);cx.fillStyle='#3a2510';for(let i=0;i<5;i++)cx.fillRect(8,10+i*10,Math.random()*40+8,2);const t=new THREE.CanvasTexture(cv);const paper=new THREE.Mesh(new THREE.PlaneGeometry(0.4,0.4),new THREE.MeshStandardMaterial({map:t,roughness:0.9,side:THREE.DoubleSide}));paper.rotation.x=-Math.PI/2;paper.rotation.z=rotY||0;paper.position.set(x,0.02,z);scene.add(paper);}
buildPaperOnFloor(0.5,-19,0.3);buildPaperOnFloor(-1.5,-21,1.2);buildPaperOnFloor(2,-18.5,-0.6);buildPaperOnFloor(-2.5,-17.2,0.9);

function buildBrokenBox(x,z,rotY){const g=new THREE.Group();const box=new THREE.Mesh(new THREE.BoxGeometry(0.6,0.5,0.6),new THREE.MeshStandardMaterial({color:0x7a4a15,roughness:0.9}));box.position.y=0.25;g.add(box);const lid=new THREE.Mesh(new THREE.BoxGeometry(0.62,0.04,0.6),new THREE.MeshStandardMaterial({color:0x5a3515,roughness:0.9}));lid.position.set(0.2,0.52,0);lid.rotation.z=0.4;g.add(lid);g.position.set(x,0,z);g.rotation.y=rotY||0;scene.add(g);colliders.push(computeCollider(box));wallMeshes.push(box);}
buildBrokenBox(2,-22,0.5);buildBrokenBox(-2,-17,-0.3);

function buildFireplace(x,z,rotY){const g=new THREE.Group();const body=new THREE.Mesh(new THREE.BoxGeometry(1.6,1.4,0.6),new THREE.MeshStandardMaterial({color:0x3a2818,roughness:0.9}));body.position.y=0.7;g.add(body);const opening=new THREE.Mesh(new THREE.BoxGeometry(1.1,0.9,0.1),new THREE.MeshBasicMaterial({color:0x0a0505}));opening.position.set(0,0.65,0.31);g.add(opening);const mantel=new THREE.Mesh(new THREE.BoxGeometry(1.8,0.15,0.7),new THREE.MeshStandardMaterial({color:0x2a1810,roughness:0.9}));mantel.position.y=1.45;g.add(mantel);g.position.set(x,0,z);if(rotY)g.rotation.y=rotY;scene.add(g);colliders.push(computeCollider(body));wallMeshes.push(body);}
buildFireplace(2,-23.6,0);

const tableGroup=new THREE.Group();
const tableTop=new THREE.Mesh(new THREE.BoxGeometry(1.4,0.06,0.8),new THREE.MeshStandardMaterial({color:0x5a3825,roughness:0.75}));tableTop.position.y=0.9;tableGroup.add(tableTop);
[[-0.6,-0.3],[0.6,-0.3],[-0.6,0.3],[0.6,0.3]].forEach(p=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(0.06,0.9,0.06),new THREE.MeshStandardMaterial({color:0x3a2010,roughness:0.85}));leg.position.set(p[0],0.45,p[1]);tableGroup.add(leg);});
tableGroup.position.set(3,0,-20);tableGroup.rotation.y=-0.6;scene.add(tableGroup);
colliders.push(computeCollider(tableTop));wallMeshes.push(tableTop);
for(let i=0;i<3;i++){const bottle=new THREE.Mesh(new THREE.CylinderGeometry(0.04,0.04,0.18,8),new THREE.MeshStandardMaterial({color:0x3a5a3a,roughness:0.6,transparent:true,opacity:0.75}));bottle.position.set(3+0.3-i*0.2,0.99,-20+(i-1)*0.15);bottle.rotation.z=(Math.random()-0.5)*1.5;scene.add(bottle);}

const tablePistol=new THREE.Group();
const tpSlide=new THREE.Mesh(new THREE.BoxGeometry(0.06,0.05,0.24),darkMetalMat);tpSlide.position.y=0.02;tablePistol.add(tpSlide);
const tpGrip=new THREE.Mesh(new THREE.BoxGeometry(0.055,0.16,0.06),new THREE.MeshStandardMaterial({color:0x1a0f08,roughness:0.85}));tpGrip.position.set(0,-0.06,0.06);tpGrip.rotation.x=0.25;tablePistol.add(tpGrip);
const tpBarrel=new THREE.Mesh(new THREE.CylinderGeometry(0.014,0.014,0.05,8),new THREE.MeshStandardMaterial({color:0x050608,metalness:0.95,roughness:0.2}));tpBarrel.rotation.x=Math.PI/2;tpBarrel.position.set(0,0.02,-0.14);tablePistol.add(tpBarrel);
tablePistol.position.set(3.1,0.95,-20);tablePistol.rotation.y=0.4;scene.add(tablePistol);

const bedGroup=new THREE.Group();
const bedFrame=new THREE.Mesh(new THREE.BoxGeometry(1.2,0.15,2.4),metalMat);bedFrame.position.y=0.75;bedGroup.add(bedFrame);
const mattress=new THREE.Mesh(new THREE.BoxGeometry(1.1,0.15,2.3),new THREE.MeshStandardMaterial({color:0x6a7280,roughness:0.9}));mattress.position.y=0.87;bedGroup.add(mattress);
[[-0.5,-1.05],[0.5,-1.05],[-0.5,1.05],[0.5,1.05]].forEach(p=>{const leg=new THREE.Mesh(new THREE.CylinderGeometry(0.03,0.03,0.7,6),metalMat);leg.position.set(p[0],0.35,p[1]);bedGroup.add(leg);});
bedGroup.position.set(-7,0,2);bedGroup.rotation.y=0.4;scene.add(bedGroup);
colliders.push(computeCollider(bedFrame));wallMeshes.push(bedFrame);

buildFallenChair(-4,0,0.7);buildFallenChair(-9,4,2.5);buildPaperOnFloor(-8,1,0.6);buildPaperOnFloor(-5,3,-1.1);

const corpseMat=new THREE.MeshStandardMaterial({color:0x506050,roughness:0.95});
const corpseCloth=new THREE.MeshStandardMaterial({color:0x0e0e15,roughness:0.9});
function buildCorpse(x,z,rotY){const g=new THREE.Group();const torso=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.18,1.25),corpseCloth);torso.position.set(0,0.09,0);g.add(torso);const head=new THREE.Mesh(new THREE.SphereGeometry(0.16,12,10),corpseMat);head.position.set(0,0.14,-0.75);g.add(head);const armL=new THREE.Mesh(new THREE.BoxGeometry(0.13,0.13,0.75),corpseMat);armL.position.set(-0.42,0.07,-0.15);g.add(armL);const armR=new THREE.Mesh(new THREE.BoxGeometry(0.13,0.13,0.75),corpseMat);armR.position.set(0.45,0.07,0.1);g.add(armR);const legL=new THREE.Mesh(new THREE.BoxGeometry(0.19,0.15,0.95),corpseCloth);legL.position.set(-0.14,0.075,0.9);g.add(legL);const legR=new THREE.Mesh(new THREE.BoxGeometry(0.19,0.15,0.95),corpseCloth);legR.position.set(0.14,0.075,0.9);g.add(legR);const pool=new THREE.Mesh(new THREE.CircleGeometry(0.85,16),new THREE.MeshStandardMaterial({color:0x1a0505,roughness:0.7}));pool.rotation.x=-Math.PI/2;pool.position.set(0,0.008,0.15);g.add(pool);g.position.set(x,0,z);g.rotation.y=rotY||0;scene.add(g);return g;}
buildCorpse(-6.5,3,1.2);

const xrayKey=new THREE.Group();
const k1=new THREE.Mesh(new THREE.CylinderGeometry(0.015,0.015,0.14,8),goldMat);xrayKey.add(k1);
const k2=new THREE.Mesh(new THREE.BoxGeometry(0.06,0.015,0.02),goldMat);k2.position.y=0.09;xrayKey.add(k2);
xrayKey.position.set(-6.5,0.35,3);scene.add(xrayKey);

const deskGroup=new THREE.Group();
const deskTop=new THREE.Mesh(new THREE.BoxGeometry(1.6,0.08,0.9),new THREE.MeshStandardMaterial({color:0x4a2a15,roughness:0.8}));deskTop.position.y=0.85;deskGroup.add(deskTop);
[[-0.75,0.4],[0.75,0.4],[-0.75,-0.4],[0.75,-0.4]].forEach(p=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(0.08,0.85,0.08),new THREE.MeshStandardMaterial({color:0x2a1810,roughness:0.85}));leg.position.set(p[0],0.425,p[1]);deskGroup.add(leg);});
deskGroup.position.set(-7,0,-11);deskGroup.rotation.y=0.3;scene.add(deskGroup);
colliders.push(computeCollider(deskTop));wallMeshes.push(deskTop);
for(let i=0;i<4;i++)buildPaperOnFloor(-7+(Math.random()-0.5)*1.2,-11+(Math.random()-0.5)*0.6,Math.random()*3);

const aghasaCorpse=new THREE.Group();
const acTorso=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.18,1.25),new THREE.MeshStandardMaterial({color:0x0e1a1a,roughness:0.9}));acTorso.position.set(0,0.09,0);aghasaCorpse.add(acTorso);
const acHead=new THREE.Mesh(new THREE.SphereGeometry(0.16,12,10),new THREE.MeshStandardMaterial({color:0xa8a098,roughness:0.9}));acHead.position.set(0,0.14,-0.75);aghasaCorpse.add(acHead);
const acFace=new THREE.Mesh(new THREE.PlaneGeometry(0.28,0.28),new THREE.MeshBasicMaterial({map:faceAghasa(),transparent:true,depthWrite:false}));acFace.position.set(0,0.14,-0.755);acFace.rotation.x=-0.3;aghasaCorpse.add(acFace);
const acArmL=new THREE.Mesh(new THREE.BoxGeometry(0.13,0.13,0.75),new THREE.MeshStandardMaterial({color:0xa8a098,roughness:0.9}));acArmL.position.set(-0.42,0.07,-0.15);aghasaCorpse.add(acArmL);
const acArmR=new THREE.Mesh(new THREE.BoxGeometry(0.13,0.13,0.75),new THREE.MeshStandardMaterial({color:0xa8a098,roughness:0.9}));acArmR.position.set(0.45,0.07,0.1);aghasaCorpse.add(acArmR);
const acPool=new THREE.Mesh(new THREE.CircleGeometry(0.9,16),new THREE.MeshStandardMaterial({color:0x1a0505,roughness:0.7}));acPool.rotation.x=-Math.PI/2;acPool.position.set(0,0.008,0.15);aghasaCorpse.add(acPool);
aghasaCorpse.position.set(-6,0,-10);aghasaCorpse.rotation.y=-1.0;scene.add(aghasaCorpse);

const paintingGroup=new THREE.Group();
const paintingFrame=new THREE.Mesh(new THREE.BoxGeometry(1.4,1.0,0.08),new THREE.MeshStandardMaterial({color:0x8a6a2a,metalness:0.6,roughness:0.4}));paintingFrame.position.y=1.8;paintingGroup.add(paintingFrame);
const paintingCv=document.createElement('canvas');paintingCv.width=256;paintingCv.height=192;const pCtx=paintingCv.getContext('2d');
const pGrad=pCtx.createLinearGradient(0,0,0,192);pGrad.addColorStop(0,'#2a4468');pGrad.addColorStop(0.6,'#5a6a88');pGrad.addColorStop(1,'#1a1a2a');
pCtx.fillStyle=pGrad;pCtx.fillRect(0,0,256,192);
pCtx.fillStyle='#0a0a15';pCtx.beginPath();pCtx.moveTo(0,150);pCtx.lineTo(40,100);pCtx.lineTo(90,130);pCtx.lineTo(140,80);pCtx.lineTo(200,120);pCtx.lineTo(256,90);pCtx.lineTo(256,192);pCtx.lineTo(0,192);pCtx.closePath();pCtx.fill();
pCtx.fillStyle='#ffe870';pCtx.beginPath();pCtx.arc(200,40,15,0,Math.PI*2);pCtx.fill();
const paintingTex=new THREE.CanvasTexture(paintingCv);
const paintingMesh=new THREE.Mesh(new THREE.PlaneGeometry(1.2,0.85),new THREE.MeshBasicMaterial({map:paintingTex}));paintingMesh.position.set(0,1.8,0.05);paintingGroup.add(paintingMesh);
paintingGroup.position.set(-9.85,0,-10);paintingGroup.rotation.y=Math.PI/2;scene.add(paintingGroup);

const safeGroup=new THREE.Group();
const safeBody=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.7,0.4),new THREE.MeshStandardMaterial({color:0x2a2a2a,metalness:0.7,roughness:0.4}));safeGroup.add(safeBody);
const safeDial=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.08,0.04,12),new THREE.MeshStandardMaterial({color:0x888888,metalness:0.9,roughness:0.2}));safeDial.rotation.z=Math.PI/2;safeDial.position.set(0,0,0.22);safeGroup.add(safeDial);
safeGroup.position.set(-9.9,1.5,-10);safeGroup.visible=false;scene.add(safeGroup);

const floatArrows=[];
function buildFloatArrow(x,y,z,id){const g=new THREE.Group();const cone=new THREE.Mesh(new THREE.ConeGeometry(0.12,0.24,4),new THREE.MeshBasicMaterial({color:0xffd700}));cone.position.y=0.15;cone.rotation.x=Math.PI;g.add(cone);const glow=new THREE.Mesh(new THREE.SphereGeometry(0.14,8,8),new THREE.MeshBasicMaterial({color:0xffd700,transparent:true,opacity:0.25}));glow.position.y=0.15;g.add(glow);g.position.set(x,y,z);g.userData.baseY=y;g.userData.glow=glow;g.userData.id=id;scene.add(g);floatArrows.push(g);return g;}

const catSuitInside=new THREE.Group();
const csBody=new THREE.Mesh(new THREE.CylinderGeometry(0.18,0.22,0.5,10),catSuitMat);csBody.position.y=0.55;catSuitInside.add(csBody);
const csHead=new THREE.Mesh(new THREE.SphereGeometry(0.13,12,10),catSuitMat);csHead.position.y=0.95;catSuitInside.add(csHead);
const csEarL=new THREE.Mesh(new THREE.ConeGeometry(0.05,0.13,6),catSuitMat);csEarL.position.set(-0.09,1.1,0);csEarL.rotation.z=0.3;catSuitInside.add(csEarL);
const csEarR=new THREE.Mesh(new THREE.ConeGeometry(0.05,0.13,6),catSuitMat);csEarR.position.set(0.09,1.1,0);csEarR.rotation.z=-0.3;catSuitInside.add(csEarR);
catSuitInside.position.set(-3.5,0.2,-22.5);catSuitInside.rotation.y=-0.35;catSuitInside.visible=false;scene.add(catSuitInside);

let archiveNoteSprite=(function(){const cv=document.createElement('canvas');cv.width=96;cv.height=128;const x=cv.getContext('2d');x.fillStyle='#c8bca0';x.fillRect(0,0,96,128);x.fillStyle='#2a1a0a';for(let i=0;i<9;i++)x.fillRect(8,14+i*12,Math.random()*40+20,2);const t=new THREE.CanvasTexture(cv);const pm=new THREE.Mesh(new THREE.PlaneGeometry(0.3,0.4),new THREE.MeshStandardMaterial({map:t,roughness:0.9,side:THREE.DoubleSide}));pm.rotation.x=-Math.PI/2;pm.position.set(8,0.05,-6);pm.visible=false;scene.add(pm);return pm;})();

const elevatorCardMesh=new THREE.Group();
const ecBody=new THREE.Mesh(new THREE.BoxGeometry(0.18,0.008,0.12),new THREE.MeshStandardMaterial({color:0x2266dd,metalness:0.4,roughness:0.3,emissive:0x002244,emissiveIntensity:0.4}));
elevatorCardMesh.add(ecBody);elevatorCardMesh.position.set(-9.9,1.5,-10);elevatorCardMesh.visible=false;scene.add(elevatorCardMesh);

let willMesh=(function(){const cv=document.createElement('canvas');cv.width=96;cv.height=128;const x=cv.getContext('2d');x.fillStyle='#e8dcc0';x.fillRect(0,0,96,128);x.fillStyle='#2a1a0a';for(let i=0;i<10;i++)x.fillRect(8,14+i*11,Math.random()*50+20,2);const t=new THREE.CanvasTexture(cv);const pm=new THREE.Mesh(new THREE.PlaneGeometry(0.25,0.33),new THREE.MeshStandardMaterial({map:t,roughness:0.9,side:THREE.DoubleSide}));pm.rotation.x=-Math.PI/2;pm.position.set(-6,0.08,-10);pm.visible=false;scene.add(pm);return pm;})();

const zombieSkinMat=new THREE.MeshStandardMaterial({color:0x3a5a3a,roughness:0.95});
const zombieClothMat=new THREE.MeshStandardMaterial({color:0x2a1010,roughness:0.95});
const zombiePantsMat=new THREE.MeshStandardMaterial({color:0x0a0a0a,roughness:0.95});
const zombieEyeMat=new THREE.MeshBasicMaterial({color:0xff1818});

function buildZombie(crawling){
  const g=new THREE.Group();const p={};
  const hips=new THREE.Group();hips.position.y=crawling?0.35:0.85;g.add(hips);p.hips=hips;
  function leg(side){const hg=new THREE.Group();hg.position.set(side*0.13,0,0);hips.add(hg);const th=new THREE.Mesh(new THREE.CylinderGeometry(0.09,0.08,0.45,8),zombiePantsMat);th.position.y=-0.225;hg.add(th);const kg=new THREE.Group();kg.position.y=-0.45;hg.add(kg);const sh=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.07,0.42,8),zombiePantsMat);sh.position.y=-0.21;kg.add(sh);const ft=new THREE.Mesh(new THREE.BoxGeometry(0.14,0.08,0.22),new THREE.MeshStandardMaterial({color:0}));ft.position.set(0,-0.45,0.05);kg.add(ft);return {hip:hg,knee:kg};}
  p.legL=leg(-1);p.legR=leg(1);
  const torso=new THREE.Mesh(new THREE.BoxGeometry(0.55,0.8,0.32),zombieClothMat);torso.position.y=0.4;hips.add(torso);
  const rip=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.25,0.02),zombieSkinMat);rip.position.set(0.15,0.45,0.165);hips.add(rip);
  function arm(side){const sg=new THREE.Group();sg.position.set(side*0.3,0.72,0);sg.rotation.x=crawling?-1.5:-1.35;hips.add(sg);const ua=new THREE.Mesh(new THREE.CylinderGeometry(0.06,0.055,0.35,8),zombieSkinMat);ua.position.y=-0.175;sg.add(ua);const eg=new THREE.Group();eg.position.y=-0.35;sg.add(eg);const fa=new THREE.Mesh(new THREE.CylinderGeometry(0.055,0.05,0.32,8),zombieSkinMat);fa.position.y=-0.16;eg.add(fa);const hd=new THREE.Mesh(new THREE.SphereGeometry(0.055,8,8),zombieSkinMat);hd.position.y=-0.34;eg.add(hd);return {shoulder:sg,elbow:eg};}
  p.armL=arm(-1);p.armR=arm(1);
  const neck=new THREE.Mesh(new THREE.CylinderGeometry(0.055,0.06,0.1,8),zombieSkinMat);neck.position.y=0.88;hips.add(neck);
  const head=new THREE.Mesh(new THREE.SphereGeometry(0.18,14,12),zombieSkinMat);head.position.y=1.08;hips.add(head);
  const el=new THREE.Mesh(new THREE.SphereGeometry(0.025,8,8),zombieEyeMat);el.position.set(-0.06,1.1,0.16);hips.add(el);
  const er=new THREE.Mesh(new THREE.SphereGeometry(0.025,8,8),zombieEyeMat);er.position.set(0.06,1.1,0.16);hips.add(er);
  const mouth=new THREE.Mesh(new THREE.BoxGeometry(0.08,0.03,0.02),new THREE.MeshBasicMaterial({color:0x2a0505}));mouth.position.set(0,1.02,0.17);hips.add(mouth);
  g.userData.parts=p;return g;
}

const crawlZombie=buildZombie(true);
crawlZombie.position.set(0.5,0,4);crawlZombie.rotation.y=Math.PI;scene.add(crawlZombie);
const crawlState={obj:crawlZombie,active:true,hp:3,knock:0,attackCd:0,dead:false};

const crawlBlood=new THREE.Mesh(new THREE.CircleGeometry(0.9,16),new THREE.MeshStandardMaterial({color:0x1a0505,roughness:0.7}));
crawlBlood.rotation.x=-Math.PI/2;crawlBlood.position.set(1.7,0.01,4);crawlBlood.visible=false;scene.add(crawlBlood);

const archZombie=buildZombie(false);
archZombie.position.set(8.5,0.15,-8.5);
archZombie.rotation.set(-Math.PI/2, 0, 0);
archZombie.visible=true;
scene.add(archZombie);
const archState={obj:archZombie,active:false,hp:6,knock:0,stagger:0,attackCd:0,awake:false,dead:false,entryTime:null};

const archBlood=new THREE.Mesh(new THREE.CircleGeometry(0.75,16),new THREE.MeshStandardMaterial({color:0x1a0505,roughness:0.7}));
archBlood.rotation.x=-Math.PI/2;archBlood.position.set(8.5,0.01,-8.5);scene.add(archBlood);

function buildShelf(x,z,rotY){const g=new THREE.Group();const mt=new THREE.MeshStandardMaterial({color:0x1e1810,roughness:0.9});for(let i=0;i<4;i++){const s=new THREE.Mesh(new THREE.BoxGeometry(1.8,0.04,0.5),mt);s.position.y=0.4+i*0.5;g.add(s);}[[-0.85,-0.2],[0.85,-0.2],[-0.85,0.2],[0.85,0.2]].forEach(p=>{const leg=new THREE.Mesh(new THREE.BoxGeometry(0.05,2,0.05),mt);leg.position.set(p[0],1,p[1]);g.add(leg);});g.position.set(x,0,z);if(rotY)g.rotation.y=rotY;scene.add(g);colliders.push(computeCollider(g));}
buildShelf(9.5,-9.5,0);buildShelf(9.5,-2.5,0);

// ═══════════════════════════════════════════════════
// ═══════════════ القبو (BZ=100 إزاحة Z) ═══════════════
// ═══════════════════════════════════════════════════
const BZ=100;
const bloodMat=new THREE.MeshStandardMaterial({color:0x2a0505,roughness:0.9,transparent:true,opacity:0.85});
const barMat=new THREE.MeshStandardMaterial({color:0x5a5a5a,metalness:0.85,roughness:0.3});
const medMat=new THREE.MeshStandardMaterial({color:0xdd2020,roughness:0.5,emissive:0x551010,emissiveIntensity:0.5});
const ammoBoxMat=new THREE.MeshStandardMaterial({color:0xd4a020,metalness:0.5,roughness:0.5,emissive:0x553300,emissiveIntensity:0.5});
const lighterMat=new THREE.MeshStandardMaterial({color:0xcc4422,metalness:0.6,roughness:0.4,emissive:0x331100,emissiveIntensity:0.3});
const ropeMat=new THREE.MeshStandardMaterial({color:0x8a6a2a,roughness:0.95});
const oxygenMachineMat=new THREE.MeshStandardMaterial({color:0x8a9098,metalness:0.7,roughness:0.4});
const drawerMat=new THREE.MeshStandardMaterial({color:0x3a2818,roughness:0.8});
const pipeMat=new THREE.MeshStandardMaterial({color:0x5a5045,metalness:0.6,roughness:0.5});
const sheetMat=new THREE.MeshStandardMaterial({color:0xf0f0e8,roughness:0.95,side:THREE.DoubleSide});

function bw(x1,z1,x2,z2){return addWall(x1,z1+BZ,x2,z2+BZ);}
function bf(x1,z1,x2,z2){return addFloor(x1,z1+BZ,x2,z2+BZ,basementFloorMat);}
function bc(x1,z1,x2,z2){return addCeil(x1,z1+BZ,x2,z2+BZ);}
function bLit(x,y,z,c,i,r){const l=new THREE.PointLight(c||0xffaa55,i||1.0,r||10,2);l.position.set(x,y,z+BZ);scene.add(l);return l;}
function bBlood(x,z,s){const b=new THREE.Mesh(new THREE.CircleGeometry(s||0.5,12),bloodMat);b.rotation.x=-Math.PI/2;b.position.set(x,0.015,z+BZ);scene.add(b);return b;}

// 1. قاعة الأصنصير
bf(-3,0,3,6);bc(-3,0,3,6);
bw(-3,0,3,0);bw(-3,0,-3,6);bw(3,0,3,6);
bw(-3,6,-1,6);bw(1,6,3,6);
(function(){const l=new THREE.Mesh(new THREE.BoxGeometry(2,H-DOOR_H-0.15,WALL_T),wallMat);l.position.set(0,DOOR_H+0.15+(H-DOOR_H-0.15)/2,BZ+6);scene.add(l);})();
bLit(0,2.8,3,0xff9933,1.6,12);
bBlood(-1.8,2,0.5);bBlood(1.5,1,0.4);bBlood(0.3,4.5,0.35);
const nurseCorpse1=buildCorpse(-2.2,4.5,0.6);nurseCorpse1.position.z+=BZ;
const med1=new THREE.Group();
const med1Body=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.06,0.08),medMat);med1.add(med1Body);
const med1Cross1=new THREE.Mesh(new THREE.BoxGeometry(0.04,0.07,0.001),new THREE.MeshBasicMaterial({color:0xffffff}));med1Cross1.position.set(0,0.005,0.04);med1.add(med1Cross1);
const med1Cross2=new THREE.Mesh(new THREE.BoxGeometry(0.001,0.07,0.04),new THREE.MeshBasicMaterial({color:0xffffff}));med1Cross2.position.set(0,0.005,0.001);med1.add(med1Cross2);
med1.position.set(1.5,0.05,BZ+2.5);scene.add(med1);
const ammoBox1=new THREE.Group();
const ab1=new THREE.Mesh(new THREE.BoxGeometry(0.22,0.1,0.14),ammoBoxMat);ammoBox1.add(ab1);
const ab1lid=new THREE.Mesh(new THREE.BoxGeometry(0.23,0.02,0.15),new THREE.MeshStandardMaterial({color:0xb08010,metalness:0.5,roughness:0.5}));ab1lid.position.y=0.06;ammoBox1.add(ab1lid);
ammoBox1.position.set(-1.8,0.05,BZ+3.5);scene.add(ammoBox1);

// 2. الممر الرئيسي
bf(-2,6,2,45);bc(-2,6,2,45);
bw(-2,6,-2,10);bw(-2,12,-2,22);bw(-2,24,-2,45);
bw(2,6,2,22);bw(2,24,2,45);
for(let z=8;z<=42;z+=8){const l=bLit(0,3,z,0xffaa55,0.9,10);l.userData.flicker=true;l.userData.baseI=0.9;}
bBlood(-1,12,0.7);bBlood(1.5,18,0.6);bBlood(-0.5,25,0.8);bBlood(1,32,0.5);bBlood(-1.5,38,0.6);

// 3. ممر السجن
bf(-20,10,-2,14);bc(-20,10,-2,14);
bw(-20,10,-2,10);bw(-20,14,-2,14);
bw(-20,6,-20,11);bw(-20,13,-20,18);
bw(-30,6,-20,6);bw(-30,18,-20,18);
bLit(-10,3,12,0xff7722,0.9,10);
bBlood(-8,11.5,0.5);bBlood(-15,12.5,0.6);

// 4. زنزانة عبدالرحمن - مغلقة بأحكام (قضبان تمتد للجدار)
bf(-30,8,-20,16);bc(-30,8,-20,16);
bw(-30,8,-20,8);bw(-30,16,-20,16);
bw(-30,8,-30,16);
bw(-20,6,-20,11);bw(-20,13,-20,18);
bw(-30,6,-20,6);bw(-30,18,-20,18);
bLit(-25,3,12,0x66ddff,1.0,10);
// ⚡ قضبان الزنزانة - تغطي كامل الجدار من Z=8 إلى Z=16
for(let zPos=8.3; zPos<=15.8; zPos+=0.5){
  const bar=new THREE.Mesh(new THREE.CylinderGeometry(0.045,0.045,DOOR_H-0.15,6),barMat);
  bar.position.set(-27,(DOOR_H-0.15)/2,BZ+zPos);
  scene.add(bar);
}
// عارضات أفقية (وسط + علوي + سفلي)
[0.15, DOOR_H*0.5, DOOR_H-0.15].forEach(h=>{
  const rail=new THREE.Mesh(new THREE.BoxGeometry(0.09,0.09,8.2),barMat);
  rail.position.set(-27,h,BZ+12);
  scene.add(rail);
});
// عارضة علوية thick
const topBar=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.12,8.3),barMat);
topBar.position.set(-27,DOOR_H+0.05,BZ+12);
scene.add(topBar);
// عارضة سفلية
const bottomBar=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.12,8.3),barMat);
bottomBar.position.set(-27,0.08,BZ+12);
scene.add(bottomBar);
// عبدالرحمن
const rahmanGroup=new THREE.Group();
const rahmanBody=new THREE.Mesh(new THREE.BoxGeometry(0.6,1.0,0.4),new THREE.MeshStandardMaterial({color:0x2a3a5a,roughness:0.85}));rahmanBody.position.set(0,0.9,0);rahmanGroup.add(rahmanBody);
const rahmanHead=new THREE.Mesh(new THREE.SphereGeometry(0.19,16,14),new THREE.MeshStandardMaterial({color:0xc89870,roughness:0.85}));rahmanHead.position.set(0,1.55,0);rahmanGroup.add(rahmanHead);
const rahmanFace=new THREE.Mesh(new THREE.PlaneGeometry(0.34,0.34),new THREE.MeshBasicMaterial({map:faceRahman(),transparent:true,depthWrite:false}));rahmanFace.position.set(0,1.55,0.191);rahmanGroup.add(rahmanFace);
const rahmanHair=new THREE.Mesh(new THREE.SphereGeometry(0.2,16,12,0,Math.PI*2,0,Math.PI*0.55),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));rahmanHair.position.set(0,1.58,0);rahmanGroup.add(rahmanHair);
rahmanGroup.position.set(-29,0,BZ+13.5);rahmanGroup.rotation.y=Math.PI/2;scene.add(rahmanGroup);
const crateR=new THREE.Mesh(new THREE.BoxGeometry(0.8,0.6,0.8),new THREE.MeshStandardMaterial({color:0x4a3520,roughness:0.9}));crateR.position.set(-29.5,0.3,BZ+10.5);scene.add(crateR);colliders.push(computeCollider(crateR));wallMeshes.push(crateR);

// 5. المشرحة - 3 جثث مغطاة بالملاءات + دم حول الأسرّة
bf(-10,20,-2,28);bc(-10,20,-2,28);
bw(-10,20,-2,20);bw(-10,28,-2,28);bw(-10,20,-10,28);
bw(-2,20,-2,22);bw(-2,24,-2,28);
bLit(-6,3,24,0x88ddff,1.4,12);
// بقع دم كثيفة حول الأسرّة
bBlood(-5,22,1.0);bBlood(-7,26,0.9);bBlood(-4,25,0.8);bBlood(-6,23.5,0.7);bBlood(-8,24,0.6);bBlood(-5,27,0.7);bBlood(-3,23,0.5);
// 3 أسرّة طبية مع 3 جثث مغطاة
for(let i=0;i<3;i++){
  const bedZ=BZ+21.5+i*3;
  const bed=new THREE.Group();
  // قاعدة السرير
  const bedBase=new THREE.Mesh(new THREE.BoxGeometry(1.0,0.15,2.0),new THREE.MeshStandardMaterial({color:0x6a7078,metalness:0.4,roughness:0.6}));bedBase.position.y=0.7;bed.add(bedBase);
  // الأرجل
  [[-0.4,-0.9],[0.4,-0.9],[-0.4,0.9],[0.4,0.9]].forEach(p=>{const l=new THREE.Mesh(new THREE.CylinderGeometry(0.03,0.03,0.7,6),metalMat);l.position.set(p[0],0.35,p[1]);bed.add(l);});
  // عجلات
  [[-0.4,-0.9],[0.4,-0.9],[-0.4,0.9],[0.4,0.9]].forEach(p=>{const w=new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,0.04,8),new THREE.MeshStandardMaterial({color:0x1a1a1a,roughness:0.7}));w.rotation.z=Math.PI/2;w.position.set(p[0],0.02,p[1]);bed.add(w);});
  bed.position.set(-8.5,0,bedZ);bed.rotation.y=Math.PI/2;scene.add(bed);
  // الجثة
  const c=buildCorpse(-8.5,bedZ,Math.PI/2);c.position.z+=BZ;
  // الملاءة البيضاء (فوق الجثة)
  const sheet=new THREE.Mesh(new THREE.PlaneGeometry(1.1,2.2),sheetMat);
  sheet.rotation.x=-Math.PI/2;sheet.rotation.z=Math.PI/2;
  sheet.position.set(-8.5,0.52,bedZ);
  scene.add(sheet);
  // انبعاج الملاءة عند الصدر (ارتفاع خفيف)
  const bump=new THREE.Mesh(new THREE.SphereGeometry(0.15,10,8),sheetMat);
  bump.scale.set(1,0.4,1.3);
  bump.position.set(-8.5,0.55,bedZ-0.2);
  scene.add(bump);
}
// حبل على الباب (يعني أن الباب مقفل)
const ropeMesh=new THREE.Group();
for(let i=0;i<3;i++){const r=new THREE.Mesh(new THREE.CylinderGeometry(0.04,0.04,2.2,8),ropeMat);r.rotation.x=Math.PI/2;r.position.set(-2.0,BZ+1.2+i*0.15,BZ+23);ropeMesh.add(r);}
scene.add(ropeMesh);
// مفتاح الاستراحة
const nurseKeyMesh=new THREE.Group();
const nk1=new THREE.Mesh(new THREE.CylinderGeometry(0.015,0.015,0.14,8),goldMat);nurseKeyMesh.add(nk1);
const nk2=new THREE.Mesh(new THREE.BoxGeometry(0.06,0.015,0.02),goldMat);nk2.position.y=0.09;nurseKeyMesh.add(nk2);
nurseKeyMesh.position.set(-8.5,0.68,BZ+21.5);nurseKeyMesh.visible=false;scene.add(nurseKeyMesh);

// 6. استراحة الممرضين
bf(2,20,10,28);bc(2,20,10,28);
bw(2,20,10,20);bw(2,28,10,28);bw(10,20,10,28);
bw(2,20,2,22);bw(2,24,2,28);
bLit(6,3,24,0xffaacc,1.0,10);
const patientBed=new THREE.Group();
const pbBase=new THREE.Mesh(new THREE.BoxGeometry(1.0,0.15,2.0),new THREE.MeshStandardMaterial({color:0x8a8a90,roughness:0.6}));pbBase.position.y=0.75;patientBed.add(pbBase);
[[-0.4,-0.9],[0.4,-0.9],[-0.4,0.9],[0.4,0.9]].forEach(p=>{const l=new THREE.Mesh(new THREE.CylinderGeometry(0.03,0.03,0.75,6),metalMat);l.position.set(p[0],0.375,p[1]);patientBed.add(l);});
patientBed.position.set(5,0,BZ+24.5);patientBed.rotation.y=Math.PI/2;scene.add(patientBed);
colliders.push(computeCollider(pbBase));wallMeshes.push(pbBase);
const patientZombie=buildZombie(false);
patientZombie.position.set(5,0.6,BZ+24.5);
patientZombie.rotation.set(-Math.PI/2,0,Math.PI/2);
scene.add(patientZombie);
const patientState={obj:patientZombie,hp:4,knock:0,attackCd:0,dead:false};
const cellKeyMesh=new THREE.Group();
const ck1=new THREE.Mesh(new THREE.CylinderGeometry(0.02,0.02,0.18,8),new THREE.MeshStandardMaterial({color:0xc0c0c0,metalness:0.85,roughness:0.2}));cellKeyMesh.add(ck1);
const ck2=new THREE.Mesh(new THREE.BoxGeometry(0.08,0.02,0.025),new THREE.MeshStandardMaterial({color:0xc0c0c0,metalness:0.85,roughness:0.2}));ck2.position.y=0.11;cellKeyMesh.add(ck2);
cellKeyMesh.position.set(5,0.95,BZ+24.5);cellKeyMesh.visible=false;scene.add(cellKeyMesh);

// 7. المطبخ
bf(-4,45,4,53);bc(-4,45,4,53);
bw(-4,45,-1,45);bw(1,45,4,45);
bw(-4,45,-4,53);bw(4,45,4,48);bw(4,50,4,53);
bw(-4,53,4,53);
(function(){const l=new THREE.Mesh(new THREE.BoxGeometry(2,H-DOOR_H-0.15,WALL_T),wallMat);l.position.set(0,DOOR_H+0.15+(H-DOOR_H-0.15)/2,BZ+45);scene.add(l);})();
(function(){const l=new THREE.Mesh(new THREE.BoxGeometry(WALL_T,H-DOOR_H-0.15,2),wallMat);l.position.set(4,DOOR_H+0.15+(H-DOOR_H-0.15)/2,BZ+49);scene.add(l);})();
bLit(0,3,49,0xff8833,1.1,12);
bBlood(-1,47,0.6);bBlood(2,50,0.4);
const kitchenCounter=new THREE.Group();
const kcTop=new THREE.Mesh(new THREE.BoxGeometry(3,0.08,0.7),new THREE.MeshStandardMaterial({color:0x5a5a58,metalness:0.4,roughness:0.6}));kcTop.position.y=0.9;kitchenCounter.add(kcTop);
const kcBody=new THREE.Mesh(new THREE.BoxGeometry(3,0.85,0.68),drawerMat);kcBody.position.y=0.45;kitchenCounter.add(kcBody);
const drawer=new THREE.Mesh(new THREE.BoxGeometry(0.6,0.2,0.05),new THREE.MeshStandardMaterial({color:0x2a1a0a,roughness:0.85}));drawer.position.set(0,0.7,0.36);kitchenCounter.add(drawer);
kitchenCounter.position.set(-2,0,BZ+51);scene.add(kitchenCounter);
colliders.push(computeCollider(kcBody));wallMeshes.push(kcBody);
const fridge=new THREE.Mesh(new THREE.BoxGeometry(0.7,1.8,0.7),new THREE.MeshStandardMaterial({color:0x8a9098,metalness:0.6,roughness:0.5}));fridge.position.set(3,0.9,BZ+51);scene.add(fridge);
colliders.push(computeCollider(fridge));wallMeshes.push(fridge);
const kitchenCrawler=buildZombie(true);kitchenCrawler.position.set(1,0,BZ+47);kitchenCrawler.rotation.y=0;scene.add(kitchenCrawler);
const kitchenCrawlerState={obj:kitchenCrawler,hp:3,knock:0,attackCd:0,dead:false};
const lighterMesh=new THREE.Group();
const liBody=new THREE.Mesh(new THREE.BoxGeometry(0.05,0.1,0.03),lighterMat);lighterMesh.add(liBody);
const liTop=new THREE.Mesh(new THREE.CylinderGeometry(0.015,0.015,0.02,8),new THREE.MeshStandardMaterial({color:0xc0c0c0,metalness:0.85,roughness:0.3}));liTop.position.y=0.06;lighterMesh.add(liTop);
lighterMesh.position.set(-2,0.7,BZ+51);lighterMesh.visible=false;scene.add(lighterMesh);
const ammoBox2=new THREE.Group();
const ab2=new THREE.Mesh(new THREE.BoxGeometry(0.22,0.1,0.14),ammoBoxMat);ammoBox2.add(ab2);
const ab2lid=new THREE.Mesh(new THREE.BoxGeometry(0.23,0.02,0.15),new THREE.MeshStandardMaterial({color:0xb08010,metalness:0.5,roughness:0.5}));ab2lid.position.y=0.06;ammoBox2.add(ab2lid);
ammoBox2.position.set(2,0.05,BZ+47);scene.add(ammoBox2);
const med2=new THREE.Group();
const med2Body=new THREE.Mesh(new THREE.BoxGeometry(0.15,0.06,0.08),medMat);med2.add(med2Body);
const med2C1=new THREE.Mesh(new THREE.BoxGeometry(0.04,0.07,0.001),new THREE.MeshBasicMaterial({color:0xffffff}));med2C1.position.set(0,0.005,0.04);med2.add(med2C1);
med2.position.set(2.7,0.05,BZ+47.5);scene.add(med2);

// 8. غرفة الأكسجين
bf(4,45,12,53);bc(4,45,12,53);
bw(4,45,12,45);bw(4,53,12,53);bw(12,45,12,53);
bLit(8,3,49,0x66ccff,1.3,12);
const oxygenMachine=new THREE.Group();
const omBody=new THREE.Mesh(new THREE.BoxGeometry(1.4,2.0,0.8),oxygenMachineMat);omBody.position.y=1.0;oxygenMachine.add(omBody);
const omPanel=new THREE.Mesh(new THREE.PlaneGeometry(0.8,0.6),new THREE.MeshBasicMaterial({color:0x113344}));omPanel.position.set(0,1.4,0.41);oxygenMachine.add(omPanel);
const omBtn=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.08,0.05,12),new THREE.MeshStandardMaterial({color:0x22ff44,emissive:0x22ff44,emissiveIntensity:0.5}));omBtn.rotation.x=Math.PI/2;omBtn.position.set(0,1.0,0.42);oxygenMachine.add(omBtn);
const omTank=new THREE.Mesh(new THREE.CylinderGeometry(0.2,0.2,1.6,16),new THREE.MeshStandardMaterial({color:0x88ccee,metalness:0.7,roughness:0.3,transparent:true,opacity:0.6}));omTank.position.set(-0.9,1.0,0);oxygenMachine.add(omTank);
oxygenMachine.position.set(10,0,BZ+48);scene.add(oxygenMachine);
colliders.push(computeCollider(omBody));wallMeshes.push(omBody);
for(let i=0;i<3;i++){const pipe=new THREE.Mesh(new THREE.CylinderGeometry(0.05,0.05,3,8),pipeMat);pipe.position.set(6,3-i*0.3,BZ+52+i*0.3);pipe.rotation.z=Math.PI/2;scene.add(pipe);}

// كلب مسعور
const dogGroup=new THREE.Group();
const dogBody=new THREE.Mesh(new THREE.BoxGeometry(0.35,0.4,0.9),new THREE.MeshStandardMaterial({color:0x2a1a10,roughness:0.9}));dogBody.position.y=0.55;dogGroup.add(dogBody);
const dogHead=new THREE.Mesh(new THREE.BoxGeometry(0.32,0.32,0.4),new THREE.MeshStandardMaterial({color:0x2a1a10,roughness:0.9}));dogHead.position.set(0,0.7,0.55);dogGroup.add(dogHead);
const dogMouth=new THREE.Mesh(new THREE.BoxGeometry(0.2,0.06,0.2),new THREE.MeshBasicMaterial({color:0x1a0505}));dogMouth.position.set(0,0.6,0.72);dogGroup.add(dogMouth);
const teethMat=new THREE.MeshStandardMaterial({color:0xf0e8d0,roughness:0.4});
for(let i=0;i<3;i++){
  const t1=new THREE.Mesh(new THREE.ConeGeometry(0.02,0.06,4),teethMat);t1.position.set(-0.06+i*0.06,0.62,0.78);t1.rotation.x=Math.PI;dogGroup.add(t1);
  const t2=new THREE.Mesh(new THREE.ConeGeometry(0.02,0.06,4),teethMat);t2.position.set(-0.06+i*0.06,0.58,0.78);dogGroup.add(t2);
}
const dogEyeL=new THREE.Mesh(new THREE.SphereGeometry(0.03,8,8),new THREE.MeshBasicMaterial({color:0xff0000}));dogEyeL.position.set(-0.09,0.78,0.72);dogGroup.add(dogEyeL);
const dogEyeR=new THREE.Mesh(new THREE.SphereGeometry(0.03,8,8),new THREE.MeshBasicMaterial({color:0xff0000}));dogEyeR.position.set(0.09,0.78,0.72);dogGroup.add(dogEyeR);
const dogEarL=new THREE.Mesh(new THREE.ConeGeometry(0.06,0.14,4),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));dogEarL.position.set(-0.13,0.92,0.5);dogGroup.add(dogEarL);
const dogEarR=new THREE.Mesh(new THREE.ConeGeometry(0.06,0.14,4),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));dogEarR.position.set(0.13,0.92,0.5);dogGroup.add(dogEarR);
const dogLegs=[];
function dogLeg(side,fz){
  const hip=new THREE.Group();hip.position.set(side*0.13,0.4,fz);dogGroup.add(hip);
  const th=new THREE.Mesh(new THREE.BoxGeometry(0.08,0.35,0.08),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));th.position.y=-0.175;hip.add(th);
  const kn=new THREE.Group();kn.position.y=-0.35;hip.add(kn);
  const sh=new THREE.Mesh(new THREE.BoxGeometry(0.07,0.3,0.07),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));sh.position.y=-0.15;kn.add(sh);
  const paw=new THREE.Mesh(new THREE.BoxGeometry(0.11,0.06,0.15),new THREE.MeshStandardMaterial({color:0x1a0a05,roughness:0.9}));paw.position.set(0,-0.32,0.03);kn.add(paw);
  dogLegs.push({hip,kn});
}
dogLeg(-1,0.35);dogLeg(1,0.35);dogLeg(-1,-0.35);dogLeg(1,-0.35);
dogGroup.position.set(0,0,BZ+30);scene.add(dogGroup);
const dogState={obj:dogGroup,hp:4,knock:0,attackCd:0,dead:false,awake:false,legs:dogLegs};

// إضاءة حمراء تحذيرية
const redFlash1=new THREE.PointLight(0xff2200,0.6,10,2);redFlash1.position.set(0,3,BZ+25);scene.add(redFlash1);
const redFlash2=new THREE.PointLight(0xff2200,0.6,10,2);redFlash2.position.set(0,3,BZ+40);scene.add(redFlash2);

// أبواب القبو
const dMorgue=buildDoor({x:-2,z:BZ+22,axis:'z',openAngle:-Math.PI/2,label:'المشرحة',locked:true,isBasement:true});
const dNurse=buildDoor({x:2,z:BZ+22,axis:'z',openAngle:Math.PI/2,label:'استراحة الممرضين',locked:true,isBasement:true});
const dCell=buildDoor({x:-20,z:BZ+11,axis:'z',openAngle:-Math.PI/2,label:'زنزانة عبدالرحمن',locked:true,isBasement:true});

// ═══════════════ دالة النار ═══════════════
function spawnFireEffect(x, y, z, duration){
  const fireGroup = new THREE.Group();
  fireGroup.position.set(x, y, z);
  scene.add(fireGroup);
  const particles = [];
  for(let i=0;i<40;i++){
    const geo = new THREE.PlaneGeometry(0.25, 0.4);
    const col = [0xff2200, 0xff6600, 0xffaa00, 0xffdd44][i%4];
    const mat = new THREE.MeshBasicMaterial({color:col,transparent:true,opacity:0.9,side:THREE.DoubleSide,depthWrite:false,blending:THREE.AdditiveBlending});
    const p = new THREE.Mesh(geo, mat);
    p.position.set((Math.random()-0.5)*0.4, Math.random()*0.3, (Math.random()-0.5)*0.4);
    p.userData.vy = 0.6 + Math.random()*0.8;
    p.userData.delay = Math.random()*0.8;
    fireGroup.add(p);
    particles.push(p);
  }
  const fireLight = new THREE.PointLight(0xff6600, 3, 8, 2);
  fireLight.position.set(0, 0.8, 0);
  fireGroup.add(fireLight);
  const startTime = performance.now();
  function animate(){
    const t = (performance.now() - startTime)/1000;
    if(t >= duration){scene.remove(fireGroup);return;}
    particles.forEach(p=>{
      const lt = t - p.userData.delay;
      if(lt < 0){p.visible=false;return;}
      p.visible=true;
      p.position.y += p.userData.vy * 0.016;
      p.material.opacity = Math.max(0, 0.9 - lt*1.2);
      p.scale.setScalar(Math.max(0.15, 1.3 - lt*0.9));
      p.lookAt(camera.position);
    });
    fireLight.intensity = 2 + Math.random()*2;
    requestAnimationFrame(animate);
  }
  animate();
}

// ═══════════════ الكود الأساسي ═══════════════

let camYaw=Math.PI,camPitch=0.35;
const CAM_DIST=3.6,CAM_HEIGHT=1.55,CAM_SMOOTH=10;
const PITCH_MIN=-0.05,PITCH_MAX=0.5;
const camRay=new THREE.Raycaster();

function camBlocked(x,z){for(let i=0;i<colliders.length;i++){const b=colliders[i];if(x>b.min.x-0.15&&x<b.max.x+0.15&&z>b.min.z-0.15&&z<b.max.z+0.15)return true;}return false;}

function updateCamera(dt){
  if(state.aiming){
    const eyeY=1.6;const fwdX=-Math.sin(camYaw),fwdZ=-Math.cos(camYaw);
    camera.position.set(player.x,eyeY,player.z);
    const lookY=eyeY+Math.sin(camPitch)*8;
    camera.lookAt(player.x+fwdX*10,lookY,player.z+fwdZ*10);
    playerFlashlight.position.set(player.x,eyeY,player.z);
    playerFlashlight.target.position.set(player.x+fwdX*10,lookY,player.z+fwdZ*10);
    playerFlashlight.target.updateMatrixWorld();
    noura.visible=false;return;
  }
  const px=player.x,pz=player.z;
  let bestDist=0.8;
  for(let d=CAM_DIST;d>=0.8;d-=0.15){
    const tx=px+Math.sin(camYaw)*Math.cos(camPitch)*d;
    const tz=pz+Math.cos(camYaw)*Math.cos(camPitch)*d;
    if(!camBlocked(tx,tz)){bestDist=d;break;}
  }
  const horiz=bestDist*Math.cos(camPitch);
  const vert=bestDist*Math.sin(camPitch);
  const iX=px+Math.sin(camYaw)*horiz;
  const iY=CAM_HEIGHT+vert;
  const iZ=pz+Math.cos(camYaw)*horiz;
  const o=new THREE.Vector3(px,CAM_HEIGHT+0.3,pz);
  const dir=new THREE.Vector3(iX-o.x,iY-o.y,iZ-o.z);
  const iDist=dir.length();dir.normalize();
  camRay.set(o,dir);camRay.far=iDist;
  const hits=camRay.intersectObjects(wallMeshes,false);
  let fX=iX,fY=iY,fZ=iZ;
  if(hits.length>0){const sd=Math.max(0.6,hits[0].distance-0.2);fX=o.x+dir.x*sd;fY=o.y+dir.y*sd;fZ=o.z+dir.z*sd;}
  if(fY>H-0.15)fY=H-0.15;if(fY<0.4)fY=0.4;
  const sm=Math.min(1,dt*CAM_SMOOTH);
  camera.position.x+=(fX-camera.position.x)*sm;
  camera.position.y+=(fY-camera.position.y)*sm;
  camera.position.z+=(fZ-camera.position.z)*sm;
  const fwdX=-Math.sin(camYaw),fwdZ=-Math.cos(camYaw);
  camera.lookAt(px+fwdX*5,1.3,pz+fwdZ*5);
  playerFlashlight.position.set(px,1.5,pz);
  playerFlashlight.target.position.set(px+fwdX*10,1.0,pz+fwdZ*10);
  playerFlashlight.target.updateMatrixWorld();
  noura.visible=Math.hypot(camera.position.x-px,camera.position.z-pz)>0.9;
}

const PLAYER_R=0.3;
function isBlocked(x,z){
  for(let i=0;i<colliders.length;i++){const b=colliders[i];if(x+PLAYER_R>b.min.x&&x-PLAYER_R<b.max.x&&z+PLAYER_R>b.min.z&&z-PLAYER_R<b.max.z)return true;}
  for(let i=0;i<doors.length;i++){const d=doors[i];if(d.open)continue;if(x+PLAYER_R>d.bx1&&x-PLAYER_R<d.bx2&&z+PLAYER_R>d.bz1&&z-PLAYER_R<d.bz2)return true;}
  return false;
}

const dlgEl=document.getElementById('dlg');
let dlgActive=false,dlgText='',dlgIdx=0,dlgTimer=null;
function startDialog(lines,onEnd){state.dlgQueue=lines.slice();state.inDialog=true;state.dlgOnEnd=onEnd||null;dlgActive=true;dlgEl.classList.add('on');nextLine();}
function nextLine(){if(state.dlgQueue.length===0){dlgActive=false;state.inDialog=false;dlgEl.classList.remove('on');if(state.dlgOnEnd){const f=state.dlgOnEnd;state.dlgOnEnd=null;f();}return;}const l=state.dlgQueue.shift();document.getElementById('dsp').textContent=l.speaker;dlgText=l.text;dlgIdx=0;document.getElementById('dtx').textContent='';clearInterval(dlgTimer);dlgTimer=setInterval(()=>{if(dlgIdx<dlgText.length){document.getElementById('dtx').textContent+=dlgText[dlgIdx];dlgIdx++;if(dlgIdx%3===0)audio.play('talking');}else clearInterval(dlgTimer);},28);}
function advanceDlg(){if(!dlgActive)return;if(dlgIdx<dlgText.length){clearInterval(dlgTimer);document.getElementById('dtx').textContent=dlgText;dlgIdx=dlgText.length;}else nextLine();}

const ntEl=document.getElementById('nt');
function showNote(note){state.inNote=true;document.getElementById('ntt').textContent=note.title;document.getElementById('ntx').textContent=note.text;document.getElementById('ntc').textContent=note.code?'🔢 الرمز: '+note.code:'';ntEl.classList.add('on');}
document.getElementById('ntb').addEventListener('click',()=>{ntEl.classList.remove('on');state.inNote=false;});

const ceEl=document.getElementById('ce');
const cin=document.getElementById('cin');
let currentDoorForCode=null;
function askCode(door){currentDoorForCode=door;state.inCode=true;cin.value='';cin.classList.remove('wr');document.getElementById('ctt').textContent='🔒 '+door.label+' - أدخلي الرمز';ceEl.classList.add('on');setTimeout(()=>cin.focus(),200);}
function closeCode(){ceEl.classList.remove('on');state.inCode=false;currentDoorForCode=null;}
function submitCode(){
  if(!currentDoorForCode)return;
  if(currentDoorForCode.label==='الخزنة'){
    if(cin.value.trim()==='1104'){audio.play('codeOk');closeCode();state.safeOpened=true;safeGroup.visible=false;setTimeout(()=>{audio.play('pickup');state.hasElevatorCard=true;elevatorCardMesh.visible=false;showToast('💳 كرت الأصنصير!');updateBagUI();},800);return;}
    else{audio.play('codeFail');cin.classList.add('wr');setTimeout(()=>cin.classList.remove('wr'),500);return;}
  }
  if(cin.value.trim()===currentDoorForCode.code){audio.play('codeOk');const d=currentDoorForCode;closeCode();d.locked=false;if(d===dClinic)state.clinicDoorOpened=true;setTimeout(()=>openDoorAnim(d,()=>{}),300);}
  else{audio.play('codeFail');cin.classList.add('wr');setTimeout(()=>cin.classList.remove('wr'),500);}
}
document.getElementById('cok').addEventListener('click',submitCode);
document.getElementById('cc2').addEventListener('click',closeCode);
cin.addEventListener('keydown',e=>{if(e.key==='Enter')submitCode();});

const invEl=document.getElementById('inv');
function toggleBag(open){const isOpen=open!==undefined?open:!invEl.classList.contains('on');if(isOpen){state.inBag=true;invEl.classList.add('on');updateBagUI();}else{state.inBag=false;invEl.classList.remove('on');}}
function updateBagUI(){
  const g=document.getElementById('inv-gun');g.className=state.hasGun?'inv-item has':'inv-item';
  document.getElementById('inv-gun-q').textContent=state.hasGun?state.magAmmo+' / '+state.reserveAmmo:'—';
  const k=document.getElementById('inv-key');k.className=state.hasArchiveKey?'inv-item has':'inv-item';k.querySelector('.qty').textContent=state.hasArchiveKey?'✓':'—';
  const c=document.getElementById('inv-card');c.className=state.hasElevatorCard?'inv-item has':'inv-item';c.querySelector('.qty').textContent=state.hasElevatorCard?'✓':'—';
  const med=document.getElementById('inv-med');med.className=state.hasMedicine?'inv-item has':'inv-item';document.getElementById('inv-med-q').textContent=state.hasMedicine?'استخدم':'—';
  const li=document.getElementById('inv-lighter');li.className=state.hasLighter?'inv-item has':'inv-item';document.getElementById('inv-lighter-q').textContent=state.hasLighter?(state.lighterFilled?'ممتلئة':'فارغة'):'—';
  const nk=document.getElementById('inv-nurse-key');nk.className=state.hasNurseKey?'inv-item has':'inv-item';document.getElementById('inv-nurse-key-q').textContent=state.hasNurseKey?'✓':'—';
  const ck=document.getElementById('inv-cell-key');ck.className=state.hasCellKey?'inv-item has':'inv-item';document.getElementById('inv-cell-key-q').textContent=state.hasCellKey?'✓':'—';
  const nc=document.getElementById('inv-notes');nc.innerHTML='';
  if(state.notes.length===0){nc.innerHTML='<div class="inv-item"><span>لا توجد ملفات</span></div>';}
  else{state.notes.forEach((n)=>{const el=document.createElement('div');el.className='inv-item has';el.innerHTML='<span>📜 '+n.title+'</span><span class="qty">اقرأ</span>';el.addEventListener('click',()=>{toggleBag(false);setTimeout(()=>showNote(n),200);});nc.appendChild(el);});}
}
document.getElementById('inv-close').addEventListener('click',()=>toggleBag(false));
document.getElementById('inv-med').addEventListener('click',()=>{
  if(!state.hasMedicine)return;
  if(state.hp>=5){showToast('❤️ صحتك كاملة');return;}
  state.hasMedicine=false;state.hp=Math.min(5,state.hp+1);
  audio.play('pickup');updateHP();updateBagUI();
  showToast('💊 +1 صحة');
});

function removeArrow(id){for(let i=floatArrows.length-1;i>=0;i--){if(floatArrows[i].userData.id===id){if(floatArrows[i].parent)scene.remove(floatArrows[i]);floatArrows.splice(i,1);}}}

function openDoorAnim(door,onComplete){
  if(door.open){if(onComplete)onComplete();return;}
  state.transitioning=true;
  document.getElementById('dt-lbl').textContent=door.label;
  const dtEl=document.getElementById('dt');
  dtEl.classList.remove('open','show-label');
  dtEl.classList.add('on');
  audio.play('doorCreak');
  let endX=player.x, endZ=player.z;
  if(door.axis==='x'){
    if(player.z < door.z){endZ = door.z + DOOR_W + 1.0;}else{endZ = door.z - 1.0;}
  } else {
    if(player.x < door.x){endX = door.x + DOOR_W + 1.0;}else{endX = door.x - 1.0;}
  }
  setTimeout(()=>{
    door.open=true;
    const dur=700;const startT=performance.now();let teleported=false;
    audio.play('doorClose');
    (function anim(){
      const t=Math.min(1,(performance.now()-startT)/dur);
      let rot;
      if(t<0.4){const p=t/0.4;rot=door.openAngle*(p*p*(3-2*p));}
      else{const p=(t-0.4)/0.6;rot=door.openAngle*(1-p*p*(3-2*p));}
      door.hinge.rotation.y=rot;
      if(!teleported && t>0.5){teleported=true;player.x=endX;player.z=endZ;player.velX=0;player.velZ=0;}
      if(t<1)requestAnimationFrame(anim);
      else{door.hinge.rotation.y=0;door.open=false;}
    })();
  },800);
  setTimeout(()=>{dtEl.classList.add('show-label');},1550);
  setTimeout(()=>{dtEl.classList.remove('show-label');dtEl.classList.add('open');},1900);
  setTimeout(()=>{dtEl.classList.remove('on','open');state.transitioning=false;if(onComplete)onComplete();},2700);
}

function findInter(){
  if(state.inBasement){
    if(!state.medPicked1 && Math.hypot(player.x-1.5,player.z-(BZ+2.5))<1.4) return {type:'med1',label:'خذي الدواء 💊'};
    if(!state.ammoPicked1 && Math.hypot(player.x-(-1.8),player.z-(BZ+3.5))<1.4) return {type:'ammo1',label:'خذي الرصاص 🔫'};
    if(!state.nurseNoteRead && Math.hypot(player.x-(-2.2),player.z-(BZ+4.5))<1.8) return {type:'nurseBody',label:'تفحصي جثة الممرض 🔍'};
    if(!state.rahmanTalked && Math.hypot(player.x-(-29),player.z-(BZ+13.5))<2.5) return {type:'rahman',label:'تحدثي مع عبدالرحمن 🗣️'};
    if(state.rahmanTalked && !state.hasCellKey && Math.hypot(player.x-(-29),player.z-(BZ+13.5))<2.5) return {type:'rahman2',label:'تحدثي مع عبدالرحمن 🗣️'};
    if(!state.kitchenDrawerOpened && Math.hypot(player.x-(-2),player.z-(BZ+51))<1.6) return {type:'drawer',label:'افتحي الدرج 🗄️'};
    if(state.kitchenDrawerOpened && !state.hasLighter && Math.hypot(player.x-(-2),player.z-(BZ+51))<1.6) return {type:'lighter',label:'خذي الولاعة 🔥'};
    if(!state.ammoPicked2 && Math.hypot(player.x-2,player.z-(BZ+47))<1.4) return {type:'ammo2',label:'خذي الرصاص 🔫'};
    if(!state.medPicked2 && Math.hypot(player.x-2.7,player.z-(BZ+47.5))<1.4) return {type:'med2',label:'خذي الدواء 💊'};
    if(state.hasLighter && !state.lighterFilled && Math.hypot(player.x-10,player.z-(BZ+48))<2.2) return {type:'fillGas',label:'عبّئي الولاعة ⛽'};
    if(state.hasLighter && state.lighterFilled && !state.morgueRopeBurnt && !state.morgueOpened && Math.hypot(player.x-(-2),player.z-(BZ+23))<2.2) return {type:'burnRope',label:'احرقي الحبل 🔥'};
    // 🔑 فحص الجثث للعثور على المفتاح
    if(state.morgueRopeBurnt && !state.hasNurseKey && state.morgueOpened){
      // البحث في الجثث الثلاث
      for(let i=0;i<3;i++){
        const bedZ=BZ+21.5+i*3;
        if(Math.hypot(player.x-(-8.5),player.z-bedZ)<1.6){
          return {type:'checkBody',label:'افحصي الجثة 🔍',bodyIndex:i};
        }
      }
    }
    if(state.hasNurseKey && !state.breakRoomOpened && Math.hypot(player.x-2,player.z-(BZ+23))<2.2) return {type:'unlockNurse',label:'افتحي بالمفتاح 🗝️'};
    if(state.breakRoomOpened && !state.patientTurned && Math.hypot(player.x-5,player.z-(BZ+24.5))<2.5) return {type:'patientTalk',label:'تحدثي مع المريض 🗣️'};
    if(state.hasCellKey && !state.cellOpened && Math.hypot(player.x-(-25),player.z-(BZ+13))<3.0) return {type:'openCell',label:'افتحي الزنزانة 🔐'};
    if(!state.hasCellKey && !state.cellOpened && Math.hypot(player.x-(-25),player.z-(BZ+13))<2.5) return {type:'cellLocked',label:'🔒 الزنزانة مقفلة'};
    for(let i=0;i<doors.length;i++){
      const d=doors[i];if(d.open||!d.isBasement)continue;
      let cx,cz;
      if(d.axis==='x'){cx=d.x+DOOR_W/2;cz=d.z;}else{cx=d.x;cz=d.z+DOOR_W/2;}
      const dist=Math.hypot(player.x-cx,player.z-cz);
      if(dist<1.8){
        if(d===dMorgue && d.locked)return{type:'doorLocked',door:d,label:'🔒 مقفل بحبل'};
        if(d===dNurse && d.locked)return{type:'doorLocked',door:d,label:'🔒 مقفل (يحتاج مفتاح)'};
        return{type:'doorOpen',door:d,label:'افتحي الباب'};
      }
    }
    return null;
  }
  if(!state.hasCatSuit){if(Math.hypot(player.x-(-3.5),player.z-(-22.5))<1.6)return{type:'wardrobe',label:'افتحي الدولاب 🚪'};}
  if(!state.hasGun){if(Math.hypot(player.x-3.1,player.z-(-20))<1.6)return{type:'pistol',label:'خذي المسدس 🔫'};}
  if(!state.hasArchiveKey){if(Math.hypot(player.x-(-6.5),player.z-3)<1.6)return{type:'key',label:'خذي المفتاح 🔑'};}
  if(archState && archState.dead && archiveNoteSprite.visible){if(Math.hypot(player.x-8,player.z-(-6))<1.8 && !state.notes.find(n=>n.id==='archive')){return{type:'paperArchive',label:'خذي الورقة 📜'};}}
  if(state.clinicDoorOpened&&!state.hasWill){if(Math.hypot(player.x-(-6),player.z-(-10))<1.8)return{type:'aghasaBody',label:'تفحصي الجثة 🔍'};}
  if(state.hasWill&&!state.paintingPushed){if(Math.hypot(player.x-(-9),player.z-(-10))<1.8)return{type:'painting',label:'ادفعي اللوحة 🖼️'};}
  if(state.paintingPushed&&!state.safeOpened){if(Math.hypot(player.x-(-9),player.z-(-10))<1.8)return{type:'safe',label:'افتحي الخزنة 🔒'};}
  for(let i=0;i<doors.length;i++){
    const d=doors[i];if(d.open||d.isBasement)continue;
    let cx,cz;
    if(d.axis==='x'){cx=d.x+DOOR_W/2;cz=d.z;}else{cx=d.x;cz=d.z+DOOR_W/2;}
    const dist=Math.hypot(player.x-cx,player.z-cz);
    if(dist<1.8){
      if(d===dStart&&d.locked)return{type:'startDoorLocked',door:d,label:'🔒 الباب مقفل'};
      if(d.needsCard)return{type:'elevator',door:d,label:state.hasElevatorCard?'استخدمي الكرت 💳':'الأصنصير مقفل 💳'};
      if(d.keyName==='archiveKey'&&d.locked)return{type:'archiveDoor',door:d,label:state.hasArchiveKey?'افتحي بالمفتاح 🔑':'الأرشيف مقفل 🔑'};
      if(d.code&&d.locked)return{type:'codeDoor',door:d,label:'🔒 أدخلي الرمز'};
      return{type:'doorOpen',door:d,label:'افتحي الباب'};
    }
  }
  return null;
}
function updateIBtn(){const i=findInter();const btn=document.getElementById('ib');const txt=document.getElementById('it');if(i){txt.textContent=i.label;btn.classList.add('on');}else{txt.textContent='تفاعل';btn.classList.remove('on');}}

function handleInteract(){
  if(!state.running||state.inDialog||state.inNote||state.inCode||state.transitioning||state.inBag)return;
  audio.init();
  const i=findInter();if(!i)return;
  if(state.inBasement){
    if(i.type==='doorLocked'){audio.play('codeFail');
      if(i.door===dMorgue)showToast('🔥 مقفل بحبل — يحتاج ولاعة');
      else if(i.door===dNurse)showToast('🗝️ يحتاج مفتاح الاستراحة');
      return;}
    if(i.type==='doorOpen'){openDoorAnim(i.door,()=>{});return;}
    if(i.type==='med1'||i.type==='med2'){state.hasMedicine=true;audio.play('pickup');
      if(i.type==='med1'){state.medPicked1=true;med1.visible=false;}else{state.medPicked2=true;med2.visible=false;}
      showToast('💊 دواء الإسعاف (استخدميه من الشنطة)');updateBagUI();return;}
    if(i.type==='ammo1'||i.type==='ammo2'){audio.play('pickup');
      if(i.type==='ammo1'){state.ammoPicked1=true;ammoBox1.visible=false;}else{state.ammoPicked2=true;ammoBox2.visible=false;}
      state.reserveAmmo+=14;updateAmmoUI();updateBagUI();showToast('🔫 +14 رصاص');return;}
    if(i.type==='nurseBody'){audio.play('paper');state.nurseNoteRead=true;
      const note={id:'nurse',title:'ملاحظة الممرض',text:'بخط مرتجف:\n\n"الوباء انتشر خلال ساعات.\nعبدالرحمن في زنزانة القبو... أرادوا إخفاءه.\nالمفتاح الاحتياطي مخبأ مع أحد الجثث في المشرحة.\n\nاستراحة الممرضين مقفلة..."',code:''};
      if(!state.notes.find(n=>n.id==='nurse'))state.notes.push(note);updateBagUI();setTimeout(()=>showNote(note),400);return;}
    if(i.type==='rahman'){state.rahmanTalked=true;
      startDialog([
        {speaker:'عبدالرحمن',text:'كات! أنتِ هنا... أخذوني وأنا فاقد الوعي!'},
        {speaker:'كات',text:'عبدالرحمن! من فعل هذا بك؟'},
        {speaker:'عبدالرحمن',text:'ممرض... قفلك الزنزانة ثم اختفى.'},
        {speaker:'عبدالرحمن',text:'المفتاح عنده. اسألي في استراحة الممرضين.'},
        {speaker:'🎯 الهدف',text:'ابحثي في المطبخ أولاً... قد تجدين شيئاً.'}
      ]);return;}
    if(i.type==='rahman2'){startDialog([{speaker:'عبدالرحمن',text:'الباب مقفل... ابحثي في المطبخ.'},{speaker:'عبدالرحمن',text:'كوني حذرة يا كات...'}]);return;}
    if(i.type==='drawer'){state.kitchenDrawerOpened=true;audio.play('doorCreak');lighterMesh.visible=true;
      showToast('🗄️ في الدرج ولاعة قديمة');return;}
    if(i.type==='lighter'){state.hasLighter=true;audio.play('pickup');lighterMesh.visible=false;
      showToast('🔥 ولاعة (فارغة)');updateBagUI();return;}
    if(i.type==='fillGas'){state.refillingGas=true;state.gasFillTimer=0;audio.play('gasFill');
      showToast('⛽ جاري التعبئة... (4 ثوان)');return;}
    if(i.type==='burnRope'){audio.play('fire');state.morgueRopeBurnt=true;ropeMesh.visible=false;
      spawnFireEffect(-2.05, 1.2, BZ+23, 2);
      const burnMark=new THREE.Mesh(new THREE.CircleGeometry(0.3,12),new THREE.MeshStandardMaterial({color:0x0a0a0a,roughness:0.9}));
      burnMark.rotation.x=-Math.PI/2;burnMark.position.set(-2.05,0.02,BZ+23);scene.add(burnMark);
      setTimeout(()=>{dMorgue.locked=false;openDoorAnim(dMorgue,()=>{state.morgueOpened=true;});},500);
      showToast('🔥 احترق الحبل!');return;}
    // 🔑 فحص الجثث الثلاث للعثور على المفتاح
    if(i.type==='checkBody'){
      const idx=i.bodyIndex;
      audio.play('paper');
      if(idx===0){
        // الجثة الأولى - تحتوي على المفتاح
        showToast('🗝️ وجدتِ المفتاح!');
        state.hasNurseKey=true;
        nurseKeyMesh.visible=false;
        updateBagUI();
      } else {
        // الجثث الأخرى - لا شيء
        const msgs=['لا شيء... مجرد جثة أخرى.','جثة أخرى... لا شيء.'];
        showToast(msgs[idx-1]||'لا شيء');
      }
      return;
    }
    if(i.type==='unlockNurse'){audio.play('codeOk');dNurse.locked=false;state.breakRoomOpened=true;
      setTimeout(()=>openDoorAnim(dNurse,()=>{}),300);showToast('🗝️ فُتحت الاستراحة!');return;}
    if(i.type==='patientTalk'){
      startDialog([
        {speaker:'كات',text:'من أنت؟'},
        {speaker:'المريض',text:'لا... لا تقربي... سأتحول في أي لحظة!'},
        {speaker:'المريض',text:'أنا ممرض... أصابني أحدهم... خذي هذا المفتاح!'},
        {speaker:'كات',text:'مفتاح الزنزانة؟!'},
        {speaker:'المريض',text:'اذهبي! أنا... لا أستطيع السيطرة...'}
      ],()=>{
        state.hasCellKey=true;
        showToast('🔐 مفتاح الزنزانة!');
        updateBagUI();
        setTimeout(()=>{
          state.patientTurned=true;audio.play('zombieGrowl');
          patientZombie.rotation.set(0,0,0);patientZombie.position.y=0.05;
          showToast('🧟 المريض تحوّل!');
        }, 900);
      });return;}
    if(i.type==='cellKeyPick'){state.hasCellKey=true;audio.play('pickup');cellKeyMesh.visible=false;
      showToast('🔐 مفتاح الزنزانة!');updateBagUI();return;}
    if(i.type==='cellLocked'){audio.play('codeFail');showToast('🔒 القضبان مقفلة — تحتاجين مفتاحاً');return;}
    if(i.type==='openCell'){audio.play('codeOk');state.cellOpened=true;
      startDialog([
        {speaker:'كات',text:'عبدالرحمن!'},
        {speaker:'عبدالرحمن',text:'شكراً يا حبيبتي... كنت أعلم أنك لن تتركيني.'},
        {speaker:'كات',text:'هيا، لنخرج من هنا.'}
      ],()=>{audio.play('win');setTimeout(()=>{document.getElementById('sc-win').classList.add('on');state.running=false;},800);});return;}
    return;
  }
  if(i.type==='startDoorLocked'){audio.play('codeFail');showToast('الباب مقفل — ابحثي في الغرفة');return;}
  if(i.type==='wardrobe'){
    audio.play('transform');state.hasCatSuit=true;state.catSuitOn=true;catSuitInside.visible=false;removeArrow('wardrobe');
    parts.torso.material=catSuitMat;parts.legL.thigh.material=catSuitMat;parts.legL.shin.material=catSuitMat;parts.legR.thigh.material=catSuitMat;parts.legR.shin.material=catSuitMat;
    parts.armL.upper.material=catSuitMat;parts.armL.fore.material=catSuitMat;parts.armR.upper.material=catSuitMat;parts.armR.fore.material=catSuitMat;parts.headSphere.material=catSuitMat;
    parts.earL.visible=true;parts.earR.visible=true;parts.tail.visible=true;dStart.locked=false;
    showToast('🐱 CATWOMAN! — فُتح باب الغرفة');
    setTimeout(()=>startDialog([{speaker:'كات',text:'بدلة سوداء... غريبة لكن مريحة.'},{speaker:'كات',text:'وباب الغرفة فُتح!'}]),400);
  }
  else if(i.type==='pistol'){audio.play('pickup');state.hasGun=true;tablePistol.visible=false;removeArrow('pistol');parts.pistol.visible=true;document.getElementById('ab').style.display='flex';document.getElementById('fb').style.display='flex';document.getElementById('bg').style.display='flex';showToast('🔫 المسدس بيدك!');updateBagUI();}
  else if(i.type==='key'){audio.play('pickup');state.hasArchiveKey=true;xrayKey.visible=false;removeArrow('key');showToast('🔑 مفتاح الأرشيف');updateBagUI();}
  else if(i.type==='paperArchive'){audio.play('paper');const note={id:'archive',title:'ورقة العامل',text:'"8317 هو رقمي المفضل..."',code:'8317'};if(!state.notes.find(n=>n.id==='archive')){state.notes.push(note);showToast('📜 حصلتِ على ورقة!');}archiveNoteSprite.visible=false;removeArrow('archiveNote');updateBagUI();setTimeout(()=>showNote(note),400);}
  else if(i.type==='aghasaBody'){audio.play('paper');state.hasWill=true;willMesh.visible=true;removeArrow('aghasa');const note={id:'will',title:'وصية الدكتور أغاسا',text:'بخط مرتجف... يكاد لا يُقرأ:\n"إن كنتِ تقرأين هذا...\nفقد فات الأوان.\nالأصوات التي تسمعينها في الأسفل\nليست بشراً.\nلا تنزلي. لا تفتحي.\nالخزنة خلف اللوحة.\nالكارت داخلها.\nالرمز: 1104\nسامحيني."',code:'1104'};if(!state.notes.find(n=>n.id==='will'))state.notes.push(note);showToast('📜 وجدتِ وصية!');updateBagUI();setTimeout(()=>showNote(note),400);}
  else if(i.type==='painting'){audio.play('pickup');state.paintingPushed=true;paintingGroup.rotation.y=Math.PI/2-0.8;paintingGroup.position.x-=0.4;safeGroup.visible=true;removeArrow('painting');showToast('🖼️ خزنة وراء اللوحة!');}
  else if(i.type==='safe'){askCode({code:'1104',label:'الخزنة'});}
  else if(i.type==='archiveDoor'){if(state.hasArchiveKey){audio.play('codeOk');i.door.locked=false;i.door.keyName=null;state.hasArchiveKey=false;updateBagUI();showToast('🔑 فُتح الأرشيف!');setTimeout(()=>openDoorAnim(i.door,()=>{}),300);}else showToast('الأرشيف مقفل');}
  else if(i.type==='codeDoor'){askCode(i.door);}
  else if(i.type==='elevator'){if(state.hasElevatorCard){audio.play('codeOk');state.hasElevatorCard=false;updateBagUI();dElevator.locked=false;removeArrow('elevator');setTimeout(()=>openDoorAnim(dElevator,()=>{setTimeout(()=>{document.getElementById('sc-elevator').classList.add('on');state.running=false;},800);}),300);}else showToast('يحتاج كرت');}
  else if(i.type==='doorOpen'){openDoorAnim(i.door,()=>{});}
}

function shoot(){
  if(!state.running||!state.hasGun||state.transitioning||state.inBag||state.inDialog||state.inNote||state.inCode)return;
  if(state.reloading)return;
  if(state.magAmmo<=0){audio.play('empty');showToast('الخزنة فارغة');return;}
  state.magAmmo--;updateAmmoUI();
  audio.play('shoot');
  if(parts.muzzleLight)parts.muzzleLight.intensity=8;
  setTimeout(()=>{if(parts.muzzleLight)parts.muzzleLight.intensity=0;},60);
  const mf=document.getElementById('muzzleFx');mf.classList.add('on');setTimeout(()=>mf.classList.remove('on'),60);
  const ray=new THREE.Raycaster();
  if(state.aiming){ray.setFromCamera(new THREE.Vector2(0,0),camera);}
  else{const fwdX=-Math.sin(camYaw),fwdZ=-Math.cos(camYaw);const oV=new THREE.Vector3(player.x,1.4,player.z);const tV=new THREE.Vector3(player.x+fwdX*20,1.4+Math.sin(camPitch)*5,player.z+fwdZ*20);ray.set(oV,tV.clone().sub(oV).normalize());}
  let bestTarget=null;let bestDist=Infinity;
  const targets=[];
  if(archState&&archState.active&&!archState.dead)targets.push({obj:archZombie,state:archState,kind:'arch',yCenter:0.9,size:1.5});
  if(crawlState&&crawlState.active&&!crawlState.dead)targets.push({obj:crawlZombie,state:crawlState,kind:'crawl',yCenter:0.4,size:1.3});
  if(state.inBasement){
    if(dogState.awake&&!dogState.dead)targets.push({obj:dogState.obj,state:dogState,kind:'dog',yCenter:0.5,size:1.0});
    if(state.patientTurned&&!patientState.dead)targets.push({obj:patientState.obj,state:patientState,kind:'patient',yCenter:0.9,size:1.4});
    if(!kitchenCrawlerState.dead)targets.push({obj:kitchenCrawlerState.obj,state:kitchenCrawlerState,kind:'kitchen',yCenter:0.4,size:1.3});
  }
  for(const t of targets){
    const box=new THREE.Box3().setFromCenterAndSize(new THREE.Vector3(t.obj.position.x,t.yCenter,t.obj.position.z),new THREE.Vector3(t.size,t.size*1.6,t.size));
    if(ray.ray.intersectsBox(box)){const d=ray.ray.origin.distanceTo(new THREE.Vector3(t.obj.position.x,t.yCenter,t.obj.position.z));if(d<bestDist){bestDist=d;bestTarget=t;}}
  }
  if(bestTarget){
    bestTarget.state.hp--;audio.play('zombieHurt');bestTarget.state.knock=0.3;flashEnemy(bestTarget.obj);
    if(bestTarget.state.hp<=0){
      if(bestTarget.kind==='arch')killArchZombie();
      else if(bestTarget.kind==='crawl')killCrawlZombie();
      else if(bestTarget.kind==='dog')killDog();
      else if(bestTarget.kind==='patient')killPatient();
      else if(bestTarget.kind==='kitchen')killKitchenCrawler();
    }
  }
}
function flashEnemy(obj){obj.traverse(o=>{if(o.material&&o.material.color){const oc=o.material.color.getHex();o.material.color.setHex(0xff3030);setTimeout(()=>{if(o.material)o.material.color.setHex(oc);},180);}});}
function updateAmmoUI(){document.getElementById('magNum').textContent=state.magAmmo;document.getElementById('resNum').textContent=state.reserveAmmo;document.getElementById('reloadSt').textContent=state.reloading?'⟳':'';}
function startReload(){if(state.reloading||state.reserveAmmo<=0||state.magAmmo>=MAG_SIZE)return;state.reloading=true;state.reloadTimer=0;audio.play('reload');updateAmmoUI();}
function finishReload(){const needed=MAG_SIZE-state.magAmmo;const load=Math.min(needed,state.reserveAmmo);state.magAmmo+=load;state.reserveAmmo-=load;state.reloading=false;updateAmmoUI();updateBagUI();}

function killArchZombie(){archState.active=false;archState.dead=true;audio.play('zombieDie');
  const st=performance.now();const startRotX=archZombie.rotation.x;const startY=archZombie.position.y;
  (function anim(){const t=Math.min(1,(performance.now()-st)/1200);archZombie.rotation.x=startRotX+(-Math.PI/2-startRotX)*t;archZombie.rotation.y=0;archZombie.rotation.z=0;archZombie.position.y=startY+(0.05-startY)*t;if(t<1)requestAnimationFrame(anim);else{archiveNoteSprite.visible=true;buildFloatArrow(8,1.3,-6,'archiveNote');}})();}
function killCrawlZombie(){crawlState.active=false;crawlState.dead=true;audio.play('zombieDie');
  const st=performance.now();const sR=crawlZombie.rotation.x;const sX=crawlZombie.position.x;const sZ=crawlZombie.position.z;const tX=1.7;
  crawlBlood.position.set(tX,0.01,sZ);crawlBlood.visible=true;
  (function anim(){const t=Math.min(1,(performance.now()-st)/1000);const e=t*t*(3-2*t);crawlZombie.rotation.x=sR+(-Math.PI/2-sR)*e;crawlZombie.rotation.y=Math.PI;crawlZombie.rotation.z=0;crawlZombie.position.x=sX+(tX-sX)*e;crawlZombie.position.y=0.05;if(t<1)requestAnimationFrame(anim);})();}
function killDog(){dogState.dead=true;audio.play('zombieDie');const st=performance.now();
  (function anim(){const t=Math.min(1,(performance.now()-st)/900);dogState.obj.rotation.z=Math.PI/2*t;dogState.obj.position.y=0.2*t;if(t<1)requestAnimationFrame(anim);})();
  showToast('🐕 الكلب مات');}
function killPatient(){patientState.dead=true;audio.play('zombieDie');const st=performance.now();const sR=patientState.obj.rotation.x;
  (function anim(){const t=Math.min(1,(performance.now()-st)/1000);patientState.obj.rotation.x=sR+(-Math.PI/2-sR)*t;patientState.obj.position.y=0.05;if(t<1)requestAnimationFrame(anim);})();}
function killKitchenCrawler(){kitchenCrawlerState.dead=true;audio.play('zombieDie');const st=performance.now();const sR=kitchenCrawlerState.obj.rotation.x;
  (function anim(){const t=Math.min(1,(performance.now()-st)/1000);kitchenCrawlerState.obj.rotation.x=sR+(-Math.PI/2-sR)*t;kitchenCrawlerState.obj.position.y=0.05;if(t<1)requestAnimationFrame(anim);})();}

function setAiming(on){
  state.aiming=on;
  const ab=document.getElementById('ab');
  ab.classList.toggle('aim-on',state.aiming);
  ab.innerHTML=state.aiming?'🎯<br>إلغاء':'🎯<br>تصويب';
  const ch=document.getElementById('crosshair');if(ch)ch.classList.toggle('on',state.aiming);
  if(!state.aiming)camPitch=Math.max(PITCH_MIN,Math.min(PITCH_MAX,camPitch));
}
document.getElementById('ab').addEventListener('click',function(){
  if(!state.running||state.inBag||state.inDialog||state.transitioning)return;
  if(!state.hasGun)return;
  setAiming(!state.aiming);
});
let fireLock=false;
document.getElementById('fb').addEventListener('click',function(){
  if(fireLock)return;if(!state.running||state.inBag||state.inDialog||state.transitioning)return;if(!state.hasGun)return;if(state.reloading)return;
  if(!state.aiming){fireLock=true;setAiming(true);setTimeout(function(){shoot();fireLock=false;},250);}else{shoot();}
});
document.getElementById('bg').addEventListener('click',()=>toggleBag());
document.getElementById('ib').addEventListener('click',handleInteract);
document.addEventListener('contextmenu',e=>e.preventDefault());
canvas.addEventListener('mousedown',function(e){
  if(e.button===2&&state.running&&!state.inBag&&!state.inDialog&&!state.transitioning&&state.hasGun){setAiming(!state.aiming);}
});
window.addEventListener('keydown',e=>{window._keys[e.key.toLowerCase()]=true;const k=e.key.toLowerCase();if(k==='r'){e.preventDefault();if(!state.inDialog&&!state.inNote&&!state.inCode&&!state.transitioning)toggleBag();}else if(k==='e'){e.preventDefault();handleInteract();}else if(k===' '){e.preventDefault();shoot();}else if(k==='escape'){if(state.inBag)toggleBag(false);}});
window.addEventListener('keyup',e=>{window._keys[e.key.toLowerCase()]=false;});

const isTouch=('ontouchstart' in window)||navigator.maxTouchPoints>0;
if(isTouch){
  const joyEl=document.getElementById('joy');const stickEl=joyEl.querySelector('.st');
  let camTid=null,camLastX=0,camLastY=0;
  function isUI(t){if(!t||!t.closest)return false;return t.closest('.bb')||t.closest('#mute')||t.closest('.scr')||t.closest('#dlg')||t.closest('#nt')||t.closest('#ce')||t.closest('#inv')||t.closest('#dt');}
  document.addEventListener('touchstart',e=>{if(!state.running||state.inDialog||state.inNote||state.inCode||state.transitioning||state.inBag)return;if(isUI(e.target))return;for(let i=0;i<e.changedTouches.length;i++){const tt=e.changedTouches[i];if(tt.clientX<innerWidth*0.45&&joyTid===null){joyTid=tt.identifier;joyActive=true;joyEl.style.left=tt.clientX+'px';joyEl.style.top=tt.clientY+'px';joyEl.classList.add('on');stickEl.style.transform='translate(0,0)';joyCX=tt.clientX;joyCY=tt.clientY;}else if(tt.clientX>=innerWidth*0.45&&camTid===null){camTid=tt.identifier;camLastX=tt.clientX;camLastY=tt.clientY;}}},{passive:true});
  document.addEventListener('touchmove',e=>{for(let i=0;i<e.changedTouches.length;i++){const tt=e.changedTouches[i];if(tt.identifier===joyTid){let dx=tt.clientX-joyCX,dy=tt.clientY-joyCY;const d=Math.hypot(dx,dy);if(d>JOY_R){dx=dx/d*JOY_R;dy=dy/d*JOY_R;}stickEl.style.transform='translate('+dx+'px,'+dy+'px)';joyDX=dx/JOY_R;joyDY=dy/JOY_R;}else if(tt.identifier===camTid){const dx=tt.clientX-camLastX,dy=tt.clientY-camLastY;if(state.aiming){camYaw-=dx*0.008;camPitch-=dy*0.005;camPitch=Math.max(-1.3,Math.min(1.3,camPitch));}else{camYaw-=dx*0.008;camPitch+=dy*0.005;camPitch=Math.max(PITCH_MIN,Math.min(PITCH_MAX,camPitch));}camLastX=tt.clientX;camLastY=tt.clientY;}}},{passive:true});
  function endT(e){for(let i=0;i<e.changedTouches.length;i++){const id=e.changedTouches[i].identifier;if(id===joyTid){joyTid=null;joyActive=false;joyDX=0;joyDY=0;joyEl.classList.remove('on');}if(id===camTid)camTid=null;}}
  document.addEventListener('touchend',endT);document.addEventListener('touchcancel',endT);
} else {
  let mouseDown=false,lastX=0,lastY=0;
  canvas.addEventListener('mousedown',e=>{if(e.button===0){if(state.inBag||state.inDialog||state.inNote||state.inCode||state.transitioning)return;mouseDown=true;lastX=e.clientX;lastY=e.clientY;}});
  canvas.addEventListener('mousemove',function(e){if(!mouseDown)return;const dx=e.clientX-lastX,dy=e.clientY-lastY;
    if(state.aiming){camYaw-=dx*0.008;camPitch-=dy*0.005;camPitch=Math.max(-1.3,Math.min(1.3,camPitch));}
    else{camYaw-=dx*0.008;camPitch+=dy*0.005;camPitch=Math.max(PITCH_MIN,Math.min(PITCH_MAX,camPitch));}
    lastX=e.clientX;lastY=e.clientY;});
  document.addEventListener('mouseup',()=>mouseDown=false);
  canvas.addEventListener('click',()=>{if(!state.running)return;if(state.inBag||state.inDialog||state.inNote||state.inCode||state.transitioning)return;shoot();});
}

document.addEventListener('click',e=>{if(dlgActive){e.preventDefault();advanceDlg();}});
document.addEventListener('touchstart',e=>{if(dlgActive){e.preventDefault();advanceDlg();}},{passive:false});
document.getElementById('mute').addEventListener('click',e=>{e.stopPropagation();audio.muted=!audio.muted;document.getElementById('mute').textContent=audio.muted?'🔇':'🔊';});

let toastTO=null;
function showToast(text,dur){dur=dur||2200;const el=document.getElementById('toast');el.textContent=text;el.classList.add('on');clearTimeout(toastTO);toastTO=setTimeout(()=>el.classList.remove('on'),dur);}
function flashDamage(){const el=document.getElementById('dmg');el.classList.add('on');setTimeout(()=>el.classList.remove('on'),180);}
function updateHP(){let s='';for(let i=0;i<5;i++)s+=i<state.hp?'❤️':'🖤';document.getElementById('hp').textContent=s;}

let lastTime=performance.now();let footstepT=0;

function updateRoomHUD(){
  const x=player.x,z=state.inBasement?player.z-BZ:player.z;
  let name='القصر';
  if(state.inBasement){
    if(x>=-3&&x<=3&&z>=0&&z<=6)name='قاعة الأصنصير';
    else if(x>=-2&&x<=2&&z>6&&z<45)name='الممر الرئيسي';
    else if(x>=-20&&x<=-2&&z>=10&&z<=14)name='ممر السجن';
    else if(x>=-30&&x<=-20&&z>=8&&z<=16)name='زنزانة عبدالرحمن';
    else if(x>=-10&&x<=-2&&z>=20&&z<=28)name='المشرحة السرية';
    else if(x>=2&&x<=10&&z>=20&&z<=28)name='استراحة الممرضين';
    else if(x>=-4&&x<=4&&z>=45&&z<=53)name='المطبخ';
    else if(x>=4&&x<=12&&z>=45&&z<=53)name='غرفة الأكسجين';
    else name='القبو';
  } else {
    if(x>-5&&x<5&&z>-24&&z<-16)name='غرفة البداية';
    else if(x>-2&&x<2&&z>=-16&&z<10)name='الممر';
    else if(x>-10&&x<-2&&z>-14&&z<-6)name='عيادة د. أغاسا';
    else if(x>-10&&x<-2&&z>-2&&z<6)name='غرفة الأشعة';
    else if(x>2&&x<10&&z>-10&&z<-2)name='الأرشيف';
    else if(x>-3&&x<3&&z>=10&&z<16)name='الأصنصير';
  }
  document.getElementById('roomPill').innerHTML='المنطقة: <strong>'+name+'</strong>';
}

function updateBasement(dt){
  state.time+=dt;if(state.invuln>0)state.invuln-=dt;
  let mx=0,mz=0;
  if(joyActive){mx=joyDX;mz=joyDY;}
  if(window._keys['w']||window._keys['arrowup'])mz=-1;
  if(window._keys['s']||window._keys['arrowdown'])mz=1;
  if(window._keys['a']||window._keys['arrowleft'])mx=-1;
  if(window._keys['d']||window._keys['arrowright'])mx=1;
  const fwdX=-Math.sin(camYaw),fwdZ=-Math.cos(camYaw);
  const rgtX=Math.cos(camYaw),rgtZ=-Math.sin(camYaw);
  const wMX=fwdX*(-mz)+rgtX*mx;const wMZ=fwdZ*(-mz)+rgtZ*mx;
  const mag=Math.hypot(wMX,wMZ);let nMX=0,nMZ=0;
  if(mag>0.01){nMX=wMX/mag;nMZ=wMZ/mag;}
  const str=Math.min(1,mag);
  const speed=state.catSuitOn?3.3:2.8;
  const tvx=nMX*speed*str;const tvz=nMZ*speed*str;
  player.velX+=(tvx-player.velX)*Math.min(1,dt*14);
  player.velZ+=(tvz-player.velZ)*Math.min(1,dt*14);
  const nx=player.x+player.velX*dt;
  if(!isBlocked(nx,player.z))player.x=nx;else player.velX=0;
  const nz=player.z+player.velZ*dt;
  if(!isBlocked(player.x,nz))player.z=nz;else player.velZ=0;
  const sp=Math.hypot(player.velX,player.velZ);player.moving=sp>0.15;
  if(player.moving){const ta=Math.atan2(player.velX,player.velZ);let d=ta-player.angle;while(d>Math.PI)d-=Math.PI*2;while(d<-Math.PI)d+=Math.PI*2;player.angle+=d*Math.min(1,dt*12);}
  noura.position.set(player.x,0,player.z);noura.rotation.y=player.angle;
  if(player.moving){player.walkTime+=dt*sp*3;const sw=Math.sin(player.walkTime*3)*0.7;const sw2=Math.sin(player.walkTime*3+Math.PI)*0.7;parts.legL.hip.rotation.x=sw;parts.legR.hip.rotation.x=sw2;parts.legL.knee.rotation.x=Math.max(0,-sw)*1.2;parts.legR.knee.rotation.x=Math.max(0,-sw2)*1.2;parts.armL.shoulder.rotation.x=sw2*0.7;parts.armR.shoulder.rotation.x=sw*0.5;noura.position.y=Math.abs(Math.sin(player.walkTime*3))*0.04;footstepT+=dt;if(footstepT>0.42){footstepT=0;audio.play('footstep');}}
  else{const tt=state.time*2;parts.legL.hip.rotation.x*=0.82;parts.legR.hip.rotation.x*=0.82;parts.legL.knee.rotation.x*=0.82;parts.legR.knee.rotation.x*=0.82;parts.armL.shoulder.rotation.x=Math.sin(tt)*0.03;parts.armR.shoulder.rotation.x=Math.sin(tt+1)*0.03;noura.position.y*=0.9;}
  if(state.catSuitOn&&parts.tail)parts.tail.rotation.y=Math.sin(state.time*3)*0.3;
  if(state.reloading){state.reloadTimer+=dt;if(state.reloadTimer>=1.4)finishReload();}
  else if(state.magAmmo<=0&&state.reserveAmmo>0){state.autoReloadTimer+=dt;if(state.autoReloadTimer>=0.5){state.autoReloadTimer=0;startReload();}}
  else state.autoReloadTimer=0;
  if(state.refillingGas){
    state.gasFillTimer+=dt;
    if(state.gasFillTimer>=4){
      state.refillingGas=false;state.lighterFilled=true;
      audio.play('codeOk');showToast('🔥 الولاعة ممتلئة!');updateBagUI();
    }
  }
  // ⭐ دخول المشرحة لأول مرة - إظهار رسالة إرشادية
  if(!state.morgueEntered && state.morgueOpened && player.x>=-10 && player.x<=-2 && (player.z-BZ)>=20 && (player.z-BZ)<=28){
    state.morgueEntered=true;
    setTimeout(()=>{
      startDialog([
        {speaker:'كات',text:'مشرحة سرية... ثلاث جثث مغطاة بالملاءات البيضاء.'},
        {speaker:'كات',text:'المفتاح الاحتياطي مخبأ مع أحد هذه الجثث... يجب أن أفحصها.'}
      ]);
    },400);
  }
  // كلب
  {
    const dx=player.x-dogState.obj.position.x;const dz=player.z-dogState.obj.position.z;const dist=Math.hypot(dx,dz);
    if(dogState.knock>0)dogState.knock-=dt;
    if(!dogState.awake&&dist<10){dogState.awake=true;audio.play('dogBark');showToast('🐕 كلب مسعور!');}
    if(dogState.awake&&!dogState.dead&&dogState.knock<=0&&dist>1.0&&dist<15){
      const zs=3.2;const zxn=dogState.obj.position.x+(dx/dist)*zs*dt;const zzn=dogState.obj.position.z+(dz/dist)*zs*dt;
      if(!isBlocked(zxn,dogState.obj.position.z))dogState.obj.position.x=zxn;
      if(!isBlocked(dogState.obj.position.x,zzn))dogState.obj.position.z=zzn;
      const ang=Math.atan2(dx,dz);let d2=ang-dogState.obj.rotation.y;while(d2>Math.PI)d2-=Math.PI*2;while(d2<-Math.PI)d2+=Math.PI*2;dogState.obj.rotation.y+=d2*Math.min(1,dt*5);
      const t=state.time*12;dogState.legs.forEach((l,i)=>{const ph=i%2===0?t:t+Math.PI;l.hip.rotation.x=Math.sin(ph)*0.7;l.kn.rotation.x=Math.max(0,Math.sin(ph))*0.6;});
    }
    if(dogState.awake&&!dogState.dead&&dist<1.3){dogState.attackCd-=dt;if(dogState.attackCd<=0){dogState.attackCd=1.4;if(state.invuln<=0){state.hp--;state.invuln=1.5;audio.play('playerHit');flashDamage();updateHP();if(state.hp<=0){gameOver();return;}}}}
  }
  if(state.patientTurned&&!patientState.dead){
    const dx=player.x-patientState.obj.position.x;const dz=player.z-patientState.obj.position.z;const dist=Math.hypot(dx,dz);
    if(patientState.knock>0)patientState.knock-=dt;
    if(patientState.knock<=0&&dist>0.9&&dist<12){
      const zs=1.5;const zxn=patientState.obj.position.x+(dx/dist)*zs*dt;const zzn=patientState.obj.position.z+(dz/dist)*zs*dt;
      if(!isBlocked(zxn,patientState.obj.position.z))patientState.obj.position.x=zxn;
      if(!isBlocked(patientState.obj.position.x,zzn))patientState.obj.position.z=zzn;
      const ang=Math.atan2(dx,dz);let d2=ang-patientState.obj.rotation.y;while(d2>Math.PI)d2-=Math.PI*2;while(d2<-Math.PI)d2+=Math.PI*2;patientState.obj.rotation.y+=d2*Math.min(1,dt*3);
    }
    if(dist<1.2){patientState.attackCd-=dt;if(patientState.attackCd<=0){patientState.attackCd=1.6;if(state.invuln<=0){state.hp--;state.invuln=1.6;audio.play('playerHit');flashDamage();updateHP();if(state.hp<=0){gameOver();return;}}}}
  }
  if(!kitchenCrawlerState.dead){
    const dx=player.x-kitchenCrawlerState.obj.position.x;const dz=player.z-kitchenCrawlerState.obj.position.z;const dist=Math.hypot(dx,dz);
    if(kitchenCrawlerState.knock>0)kitchenCrawlerState.knock-=dt;
    if(kitchenCrawlerState.knock<=0&&dist>0.8&&dist<8){
      const zs=0.55;const zxn=kitchenCrawlerState.obj.position.x+(dx/dist)*zs*dt;const zzn=kitchenCrawlerState.obj.position.z+(dz/dist)*zs*dt;
      if(!isBlocked(zxn,kitchenCrawlerState.obj.position.z))kitchenCrawlerState.obj.position.x=zxn;
      if(!isBlocked(kitchenCrawlerState.obj.position.x,zzn))kitchenCrawlerState.obj.position.z=zzn;
      const ang=Math.atan2(dx,dz);let d2=ang-kitchenCrawlerState.obj.rotation.y;while(d2>Math.PI)d2-=Math.PI*2;while(d2<-Math.PI)d2+=Math.PI*2;kitchenCrawlerState.obj.rotation.y+=d2*Math.min(1,dt*1.5);
    }
    if(dist<0.9){kitchenCrawlerState.attackCd-=dt;if(kitchenCrawlerState.attackCd<=0){kitchenCrawlerState.attackCd=1.8;if(state.invuln<=0){state.hp--;state.invuln=1.8;audio.play('playerHit');flashDamage();updateHP();if(state.hp<=0){gameOver();return;}}}}
  }
  scene.traverse(o=>{if(o.isPointLight&&o.userData.flicker){o.intensity=o.userData.baseI*(0.7+Math.random()*0.5);}});
  if(redFlash1)redFlash1.intensity=0.4*Math.sin(state.time*3)*0.5+0.4;
  if(redFlash2)redFlash2.intensity=0.4*Math.sin(state.time*3+1.5)*0.5+0.4;
  floatArrows.forEach((a,i)=>{if(a&&a.visible){a.position.y=a.userData.baseY+Math.sin(state.time*2.5+i)*0.12;a.rotation.y+=dt*1.5;if(a.userData.glow)a.userData.glow.material.opacity=0.2+Math.sin(state.time*3+i)*0.1;}});
  updateCamera(dt);updateIBtn();updateRoomHUD();
}

function update(dt){
  if(!state.running)return;
  if(state.inDialog||state.inNote||state.inCode||state.transitioning||state.inBag){updateCamera(dt);return;}
  if(state.inBasement){updateBasement(dt);return;}
  state.time+=dt;if(state.invuln>0)state.invuln-=dt;
  let mx=0,mz=0;
  if(joyActive){mx=joyDX;mz=joyDY;}
  if(window._keys['w']||window._keys['arrowup'])mz=-1;
  if(window._keys['s']||window._keys['arrowdown'])mz=1;
  if(window._keys['a']||window._keys['arrowleft'])mx=-1;
  if(window._keys['d']||window._keys['arrowright'])mx=1;
  const fwdX=-Math.sin(camYaw),fwdZ=-Math.cos(camYaw);
  const rgtX=Math.cos(camYaw),rgtZ=-Math.sin(camYaw);
  const wMX=fwdX*(-mz)+rgtX*mx;const wMZ=fwdZ*(-mz)+rgtZ*mx;
  const mag=Math.hypot(wMX,wMZ);let nMX=0,nMZ=0;
  if(mag>0.01){nMX=wMX/mag;nMZ=wMZ/mag;}
  const str=Math.min(1,mag);
  const speed=state.catSuitOn?3.3:2.8;
  const tvx=nMX*speed*str;const tvz=nMZ*speed*str;
  player.velX+=(tvx-player.velX)*Math.min(1,dt*14);
  player.velZ+=(tvz-player.velZ)*Math.min(1,dt*14);
  const nx=player.x+player.velX*dt;
  if(!isBlocked(nx,player.z))player.x=nx;else player.velX=0;
  const nz=player.z+player.velZ*dt;
  if(!isBlocked(player.x,nz))player.z=nz;else player.velZ=0;
  const sp=Math.hypot(player.velX,player.velZ);player.moving=sp>0.15;
  if(player.moving){const ta=Math.atan2(player.velX,player.velZ);let d=ta-player.angle;while(d>Math.PI)d-=Math.PI*2;while(d<-Math.PI)d+=Math.PI*2;player.angle+=d*Math.min(1,dt*12);}
  noura.position.set(player.x,0,player.z);noura.rotation.y=player.angle;
  if(player.moving){player.walkTime+=dt*sp*3;const sw=Math.sin(player.walkTime*3)*0.7;const sw2=Math.sin(player.walkTime*3+Math.PI)*0.7;parts.legL.hip.rotation.x=sw;parts.legR.hip.rotation.x=sw2;parts.legL.knee.rotation.x=Math.max(0,-sw)*1.2;parts.legR.knee.rotation.x=Math.max(0,-sw2)*1.2;parts.armL.shoulder.rotation.x=sw2*0.7;parts.armR.shoulder.rotation.x=sw*0.5;noura.position.y=Math.abs(Math.sin(player.walkTime*3))*0.04;footstepT+=dt;if(footstepT>0.42){footstepT=0;audio.play('footstep');}}
  else{const tt=state.time*2;parts.legL.hip.rotation.x*=0.82;parts.legR.hip.rotation.x*=0.82;parts.legL.knee.rotation.x*=0.82;parts.legR.knee.rotation.x*=0.82;parts.armL.shoulder.rotation.x=Math.sin(tt)*0.03;parts.armR.shoulder.rotation.x=Math.sin(tt+1)*0.03;noura.position.y*=0.9;}
  if(state.catSuitOn&&parts.tail)parts.tail.rotation.y=Math.sin(state.time*3)*0.3;
  if(state.reloading){state.reloadTimer+=dt;if(state.reloadTimer>=1.4)finishReload();}
  else if(state.magAmmo<=0&&state.reserveAmmo>0){state.autoReloadTimer+=dt;if(state.autoReloadTimer>=0.5){state.autoReloadTimer=0;startReload();}}
  else state.autoReloadTimer=0;
  if(archState && !archState.awake && !archState.dead && player.x > 4){
    if(archState.entryTime === null || archState.entryTime === undefined){archState.entryTime = performance.now();}
    if(performance.now() - archState.entryTime > 4000){
      archState.awake = true;archState.active = true;archState.hp = 6;
      const startTime=performance.now();const startRotX=archZombie.rotation.x;
      (function standUp(){const t=Math.min(1,(performance.now()-startTime)/1000);archZombie.rotation.x=startRotX*(1-t);if(t<1)requestAnimationFrame(standUp);})();
      audio.play('zombieGrowl');showToast('🧟 الجثة تصحى!');
    }
  }
  if(archState&&archState.active&&!archState.dead){
    const dx=player.x-archZombie.position.x;const dz=player.z-archZombie.position.z;const dist=Math.hypot(dx,dz);
    if(archState.knock>0)archState.knock-=dt;if(archState.stagger>0)archState.stagger-=dt;
    if(archState.knock<=0&&archState.stagger<=0&&dist>0.9){const zs=1.1;const zxn=archZombie.position.x+(dx/dist)*zs*dt;const zzn=archZombie.position.z+(dz/dist)*zs*dt;if(!isBlocked(zxn,archZombie.position.z))archZombie.position.x=zxn;if(!isBlocked(archZombie.position.x,zzn))archZombie.position.z=zzn;const ang=Math.atan2(dx,dz);let d2=ang-archZombie.rotation.y;while(d2>Math.PI)d2-=Math.PI*2;while(d2<-Math.PI)d2+=Math.PI*2;archZombie.rotation.y+=d2*Math.min(1,dt*3);}
    const zp=archZombie.userData.parts;const zsw=Math.sin(state.time*3)*0.5;zp.legL.hip.rotation.x=zsw;zp.legR.hip.rotation.x=-zsw;zp.legL.knee.rotation.x=Math.max(0,-zsw)*0.8;zp.legR.knee.rotation.x=Math.max(0,zsw)*0.8;
    if(dist<1.3){archState.attackCd-=dt;if(archState.attackCd<=0){archState.attackCd=1.6;if(state.invuln<=0){state.hp--;state.invuln=1.6;audio.play('playerHit');flashDamage();updateHP();if(state.hp<=0){gameOver();return;}}}}
  }
  if(crawlState&&crawlState.active&&!crawlState.dead){
    const dx=player.x-crawlZombie.position.x;const dz=player.z-crawlZombie.position.z;const dist=Math.hypot(dx,dz);
    if(crawlState.knock>0)crawlState.knock-=dt;
    if(crawlState.knock<=0&&dist>0.8&&dist<10){
      const zs=0.5;const zxn=crawlZombie.position.x+(dx/dist)*zs*dt;const zzn=crawlZombie.position.z+(dz/dist)*zs*dt;
      if(!isBlocked(zxn,crawlZombie.position.z))crawlZombie.position.x=zxn;
      if(!isBlocked(crawlZombie.position.x,zzn))crawlZombie.position.z=zzn;
      const ang=Math.atan2(dx,dz);let d2=ang-crawlZombie.rotation.y;while(d2>Math.PI)d2-=Math.PI*2;while(d2<-Math.PI)d2+=Math.PI*2;crawlZombie.rotation.y+=d2*Math.min(1,dt*1.5);
      const t=state.time*2;const push=Math.sin(t)*0.3;crawlZombie.userData.parts.armL.shoulder.rotation.x=-1.5+push*0.5;crawlZombie.userData.parts.armR.shoulder.rotation.x=-1.5-push*0.5;crawlZombie.position.y=Math.abs(Math.sin(t*2))*0.02;
    }
    if(dist<0.9){crawlState.attackCd-=dt;if(crawlState.attackCd<=0){crawlState.attackCd=1.8;if(state.invuln<=0){state.hp--;state.invuln=1.8;audio.play('playerHit');flashDamage();updateHP();if(state.hp<=0){gameOver();return;}}}}
  }
  floatArrows.forEach((a,i)=>{if(a&&a.visible){a.position.y=a.userData.baseY+Math.sin(state.time*2.5+i)*0.12;a.rotation.y+=dt*1.5;if(a.userData.glow)a.userData.glow.material.opacity=0.2+Math.sin(state.time*3+i)*0.1;}});
  updateCamera(dt);updateIBtn();updateRoomHUD();
}

function gameOver(){state.running=false;audio.play('death');setTimeout(()=>document.getElementById('sc-dead').classList.add('on'),700);}
function loop(){requestAnimationFrame(loop);const now=performance.now();const dt=Math.min(0.05,(now-lastTime)/1000);lastTime=now;update(dt);renderer.render(scene,camera);}

function resetBasement(){
  state.inBasement=false;state.hasMedicine=false;state.hasLighter=false;state.lighterFilled=false;
  state.hasNurseKey=false;state.hasCellKey=false;
  state.morgueOpened=false;state.breakRoomOpened=false;state.cellOpened=false;state.rahmanTalked=false;
  state.dogKilled=false;state.patientTurned=false;state.patientKilled=false;state.nurseNoteRead=false;
  state.refillingGas=false;state.gasFillTimer=0;state.morgueRopeBurnt=false;state.kitchenDrawerOpened=false;
  state.medPicked1=false;state.medPicked2=false;state.ammoPicked1=false;state.ammoPicked2=false;
  state.morgueEntered=false;
  med1.visible=true;med2.visible=true;ammoBox1.visible=true;ammoBox2.visible=true;
  lighterMesh.visible=false;nurseKeyMesh.visible=false;cellKeyMesh.visible=false;ropeMesh.visible=true;
  dogState.dead=false;dogState.awake=false;dogState.knock=0;
  dogState.obj.position.set(0,0,BZ+30);dogState.obj.rotation.set(0,0,0);
  patientState.dead=false;patientState.knock=0;
  patientState.obj.position.set(5,0.6,BZ+24.5);patientState.obj.rotation.set(-Math.PI/2,0,Math.PI/2);
  kitchenCrawlerState.dead=false;kitchenCrawlerState.knock=0;
  kitchenCrawlerState.obj.position.set(1,0,BZ+47);kitchenCrawlerState.obj.rotation.set(0,0,0);kitchenCrawlerState.obj.position.y=0;
  dMorgue.locked=true;dMorgue.open=false;dMorgue.hinge.rotation.y=0;
  dNurse.locked=true;dNurse.open=false;dNurse.hinge.rotation.y=0;
  dCell.locked=true;dCell.open=false;dCell.hinge.rotation.y=0;
}

function descendToBasement(){
  player.x=0;player.z=BZ+2;player.velX=0;player.velZ=0;player.angle=Math.PI;
  camYaw=Math.PI;camPitch=0.35;
  state.inBasement=true;
  scene.fog.color.setHex(0x1a2030);scene.fog.density=0.055;
  camera.position.set(player.x,1.9,player.z+3);camera.lookAt(player.x,1.3,player.z);
  document.querySelectorAll('.scr').forEach(s=>s.classList.remove('on'));
  state.running=true;
  setTimeout(()=>{
    startDialog([
      {speaker:'كات',text:'هذا المكان... رائحته كريهة.'},
      {speaker:'كات',text:'الأصوات قادمة من الأمام.'},
      {speaker:'🎯 الهدف',text:'ابحثي عن عبدالرحمن في القبو.'}
    ]);
  },500);
}

function startGame(){
  audio.init();
  state.hasGun=false;state.hasCatSuit=false;state.catSuitOn=false;state.hasArchiveKey=false;state.hasElevatorCard=false;state.hasWill=false;
  state.notes=[];
  state.inDialog=false;state.inNote=false;state.inCode=false;state.inBag=false;state.transitioning=false;
  state.hp=5;state.time=0;state.invuln=0;state.magAmmo=7;state.reserveAmmo=21;state.reloading=false;state.reloadTimer=0;state.autoReloadTimer=0;
  state.paintingPushed=false;state.safeOpened=false;state.clinicDoorOpened=false;state.aiming=false;
  resetBasement();
  const abBtn=document.getElementById('ab');
  if(abBtn){abBtn.classList.remove('aim-on');abBtn.innerHTML='🎯<br>تصويب';}
  const chEl=document.getElementById('crosshair');if(chEl)chEl.classList.remove('on');
  player.x=0;player.z=-20;player.velX=0;player.velZ=0;player.angle=Math.PI;
  camYaw=Math.PI;camPitch=0.35;
  parts.torso.material=girlDressMat;parts.legL.thigh.material=girlSkinMat;parts.legL.shin.material=girlSkinMat;parts.legR.thigh.material=girlSkinMat;parts.legR.shin.material=girlSkinMat;
  parts.armL.upper.material=girlSkinMat;parts.armL.fore.material=girlSkinMat;parts.armR.upper.material=girlSkinMat;parts.armR.fore.material=girlSkinMat;parts.headSphere.material=girlSkinMat;
  parts.earL.visible=false;parts.earR.visible=false;parts.tail.visible=false;parts.pistol.visible=false;
  catSuitInside.visible=true;tablePistol.visible=true;
  xrayKey.visible=true;xrayKey.position.set(-6.5,0.35,3);
  archiveNoteSprite.visible=false;archiveNoteSprite.position.set(8,0.05,-6);
  willMesh.visible=false;willMesh.position.set(-6,0.08,-10);
  paintingGroup.rotation.y=Math.PI/2;paintingGroup.position.set(-9.85,0,-10);
  safeGroup.visible=false;safeGroup.position.set(-9.9,1.5,-10);
  elevatorCardMesh.visible=false;elevatorCardMesh.position.set(-9.9,1.5,-10);
  deskGroup.position.set(-7,0,-11);deskGroup.rotation.y=0.3;
  bedGroup.position.set(-7,0,2);bedGroup.rotation.y=0.4;
  aghasaCorpse.position.set(-6,0,-10);aghasaCorpse.rotation.y=-1.0;
  for(let i=floatArrows.length-1;i>=0;i--){
    const id=floatArrows[i].userData.id;
    if(id&&(id==='wardrobe'||id==='pistol'||id==='key'||id==='aghasa'||id==='painting'||id==='elevator'||id==='archiveNote')){
      if(floatArrows[i].parent)scene.remove(floatArrows[i]);floatArrows.splice(i,1);
    }
  }
  buildFloatArrow(-3.5,2.7,-22.5,'wardrobe');
  buildFloatArrow(3.1,1.7,-20,'pistol');
  buildFloatArrow(-6.5,1.2,3,'key');
  buildFloatArrow(-6,1.2,-10,'aghasa');
  buildFloatArrow(-9.4,2.5,-10,'painting');
  buildFloatArrow(0,2.5,13,'elevator');
  archZombie.visible=true;archZombie.position.set(8.5,0.15,-8.5);archZombie.rotation.set(-Math.PI/2,0,0);
  archState.active=false;archState.hp=6;archState.awake=false;archState.dead=false;archState.entryTime=null;
  archBlood.visible=true;
  archZombie.traverse(o=>{if(o.isMesh){o.visible=true;}});
  if(!crawlZombie.parent)scene.add(crawlZombie);
  crawlZombie.visible=true;
  crawlZombie.traverse(o=>{if(o.isMesh){o.visible=true;}});
  crawlZombie.position.set(0.5,0,4);crawlZombie.rotation.set(0,Math.PI,0);
  crawlState.active=true;crawlState.hp=3;crawlState.dead=false;
  crawlBlood.visible=false;crawlBlood.position.set(1.7,0.01,4);
  doors.forEach(d=>{d.open=false;d.hinge.rotation.y=0;
    if(d===dArchive){d.locked=true;d.keyName='archiveKey';}
    else if(d===dClinic){d.locked=true;d.code='8317';}
    else if(d===dElevator){d.locked=true;d.needsCard=true;}
    else if(d===dStart){d.locked=true;d.keyName='roomKey';}
    else if(!d.isBasement){d.locked=false;}
  });
  document.getElementById('ab').style.display='none';
  document.getElementById('fb').style.display='none';
  document.getElementById('bg').style.display='none';
  scene.fog.color.setHex(0x08060a);scene.fog.density=0.045;
  updateHP();updateAmmoUI();updateBagUI();
  camera.position.set(player.x+Math.sin(camYaw)*3,1.9,player.z+Math.cos(camYaw)*3);
  camera.lookAt(player.x,1.3,player.z);
  state.running=true;
  document.querySelectorAll('.scr').forEach(s=>s.classList.remove('on'));
  setTimeout(()=>{startDialog([{speaker:'كات',text:'... أين أنا؟ المدينة كلها هادئة.'},{speaker:'كات',text:'سمعت صوت عبدالرحمن من مكان ما...'},{speaker:'كات',text:'لابد أنه في الأسفل. سأبحث عن طريقة.'},{speaker:'🎯 الهدف',text:'الباب مقفل. تفقدي الغرفة.'}]);},700);
}

document.getElementById('start-btn').addEventListener('click',e=>{e.preventDefault();startGame();});
document.getElementById('retry-btn').addEventListener('click',e=>{e.preventDefault();document.getElementById('sc-dead').classList.remove('on');startGame();});
document.getElementById('next-btn').addEventListener('click',e=>{e.preventDefault();document.getElementById('sc-elevator').classList.remove('on');descendToBasement();});
document.getElementById('win-restart').addEventListener('click',e=>{e.preventDefault();document.getElementById('sc-win').classList.remove('on');startGame();});

window.addEventListener('resize',()=>{camera.aspect=innerWidth/innerHeight;camera.updateProjectionMatrix();renderer.setSize(innerWidth,innerHeight);checkOrient();});
function checkOrient(){const rot=document.getElementById('rot');if(innerHeight>innerWidth&&innerWidth<900)rot.classList.add('on');else rot.classList.remove('on');}
window.addEventListener('orientationchange',()=>setTimeout(checkOrient,200));
checkOrient();

updateHP();updateAmmoUI();updateBagUI();
camera.position.set(0,1.9,-17);camera.lookAt(0,1.3,-20);
requestAnimationFrame(loop);
</script>
</body>
</html>

Game Source: مدينة الراكون: القصر المهجور

Creator: CyberGecko23

Libraries: three

Complexity: complex (1642 lines, 144.4 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-cybergecko23-mv1f18z8" to link back to the original. Then publish at arcadelab.ai/publish.