{"id":123,"date":"2026-06-06T17:58:24","date_gmt":"2026-06-06T14:58:24","guid":{"rendered":"https:\/\/urasuzay.com\/?page_id=123"},"modified":"2026-08-16T16:26:37","modified_gmt":"2026-08-16T13:26:37","slug":"arena-build","status":"publish","type":"page","link":"https:\/\/urasuzay.com\/index.php\/arena-build\/","title":{"rendered":"arena build"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"123\" class=\"elementor elementor-123\">\n\t\t\t\t<div class=\"elementor-element elementor-element-1e373ac e-flex e-con-boxed e-con e-parent\" data-id=\"1e373ac\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t\t<a href=\"https:\/\/urasuzay.com\/\" target=\"_self\" class=\"e-button-base\" data-interaction-id=\"91f83a5\">\n\t\tAna Sayfa\n\t<\/a>\t\t\t\t<div class=\"elementor-element elementor-element-0bf4d0f elementor-widget elementor-widget-html\" data-id=\"0bf4d0f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"tr\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n<title>Arena Build \u2013 1v1 \/ 2v2 \/ 3v3 \/ Parkur<\/title>\n\n<style>\n*{box-sizing:border-box;margin:0;padding:0}\nhtml,body{width:100%;height:100%;overflow:hidden;background:#07111f;font-family:Arial,sans-serif;color:white;touch-action:none;user-select:none;-webkit-user-select:none}\ncanvas{display:block;position:relative;z-index:1}\n#menu,#hud,#hint,#mobileControls{user-select:none}\ninput,textarea{user-select:text !important;-webkit-user-select:text !important;touch-action:manipulation !important}\n\n#menu{\n  position:fixed;inset:0;z-index:20;display:flex;align-items:center;justify-content:center;\n  background:\n  radial-gradient(circle at 20% 20%,rgba(56,189,248,.35),transparent 30%),\n  radial-gradient(circle at 80% 20%,rgba(250,204,21,.28),transparent 28%),\n  linear-gradient(135deg,#1552d4,#0b1b4d 55%,#07111f)\n}\n.lobby{\n  width:min(96vw,1040px);height:min(92vh,680px);\n  display:grid;grid-template-columns:260px 1fr 290px;grid-template-rows:82px 1fr 82px;gap:14px\n}\n.topBar,.leftPanel,.centerPanel,.rightPanel,.bottomBar,.panel{\n  border:2px solid rgba(255,255,255,.16);\n  box-shadow:0 18px 45px rgba(0,0,0,.35);\n  background:rgba(15,23,42,.74);\n  backdrop-filter:blur(8px);\n  border-radius:22px\n}\n.topBar{grid-column:1\/4;display:flex;align-items:center;justify-content:space-between;padding:14px 18px}\n.logo{font-size:30px;font-weight:900;text-shadow:0 4px 0 rgba(0,0,0,.35)}\n.badgeRow{display:flex;gap:10px;align-items:center;flex-wrap:wrap}\n.badge{padding:10px 14px;border-radius:999px;background:#0f172a;border:1px solid rgba(255,255,255,.18);font-weight:bold}\n.coinBadge{background:linear-gradient(135deg,#f59e0b,#facc15);color:#111827}\n\n.leftPanel{grid-column:1;grid-row:2;padding:16px;display:flex;flex-direction:column;gap:14px;overflow:auto}\n.profileCard{padding:14px;border-radius:18px;background:linear-gradient(135deg,#2563eb,#7c3aed);border:1px solid rgba(255,255,255,.2)}\n.avatar{width:74px;height:74px;border-radius:18px;background:linear-gradient(135deg,#fde68a,#f97316);display:flex;align-items:center;justify-content:center;font-size:40px;margin-bottom:10px;box-shadow:inset 0 -8px 0 rgba(0,0,0,.18)}\n.smallCard,.passCard{padding:12px;border-radius:16px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);line-height:1.45}\n.passCard h3{margin-bottom:8px;color:#facc15}\n.passBar{height:16px;border-radius:99px;background:rgba(0,0,0,.35);overflow:hidden;border:1px solid rgba(255,255,255,.18);margin:8px 0}\n.passFill{height:100%;width:0;background:linear-gradient(90deg,#f59e0b,#facc15)}\n\n.centerPanel{grid-column:2;grid-row:2;padding:18px;display:flex;align-items:center;justify-content:center;position:relative;overflow:hidden}\n.characterStage{\n  width:100%;height:100%;border-radius:24px;\n  background:radial-gradient(circle at center,rgba(56,189,248,.25),transparent 38%),linear-gradient(180deg,rgba(255,255,255,.08),rgba(255,255,255,.02));\n  display:flex;align-items:center;justify-content:center;position:relative\n}\n.bigHero{font-size:120px;filter:drop-shadow(0 22px 22px rgba(0,0,0,.45))}\n.modeChip{position:absolute;left:18px;bottom:18px;padding:12px 18px;border-radius:16px;background:#facc15;color:#111827;font-weight:900;box-shadow:0 8px 0 rgba(0,0,0,.25)}\n\n.rightPanel{grid-column:3;grid-row:2;padding:16px;display:flex;flex-direction:column;gap:12px;overflow:auto}\n.deviceBox{padding:12px;border-radius:16px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.13)}\n.deviceButtons{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:8px}\n.deviceBtn{border:0;border-radius:12px;padding:12px;background:#334155;color:white;font-weight:900;cursor:pointer}\n.deviceBtn.active{background:linear-gradient(135deg,#22c55e,#16a34a);box-shadow:0 6px 0 rgba(0,0,0,.28)}\n.modeSelect{width:100%;padding:12px;border-radius:14px;background:#0f172a;color:white;border:1px solid rgba(255,255,255,.2);font-weight:900;margin-top:8px}\n.menuBtn{\n  width:100%;border:0;border-radius:18px;padding:15px 16px;font-size:18px;font-weight:900;color:white;cursor:pointer;\n  box-shadow:0 8px 0 rgba(0,0,0,.28);text-align:left\n}\n.playBtn{background:linear-gradient(135deg,#22c55e,#16a34a);font-size:23px;text-align:center}\n.shopBtn{background:linear-gradient(135deg,#f59e0b,#ef4444)}\n.passBtn{background:linear-gradient(135deg,#facc15,#f97316);color:#111827}\n.infoBtn{background:linear-gradient(135deg,#6366f1,#8b5cf6)}\n.bottomBar{grid-column:1\/4;grid-row:3;display:grid;grid-template-columns:repeat(4,1fr);gap:10px;padding:12px}\n.navCard{border-radius:16px;background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);display:flex;align-items:center;justify-content:center;font-weight:bold}\n\n#shopPanel,#passPanel{\n  display:none;position:fixed;right:20px;top:100px;bottom:110px;z-index:35;width:min(92vw,500px);\n  padding:16px;border-radius:22px;background:rgba(15,23,42,.96);border:2px solid rgba(255,255,255,.18);\n  box-shadow:0 24px 80px rgba(0,0,0,.5);overflow:auto\n}\n.coinText{text-align:center;color:#fde68a;font-weight:bold;margin:10px 0}\n.shopItem,.passTier{\n  display:flex;justify-content:space-between;align-items:center;gap:10px;padding:10px;\n  border-top:1px solid rgba(255,255,255,.12);border-radius:12px;margin-top:8px;background:rgba(255,255,255,.04)\n}\n.passTier.done{background:rgba(34,197,94,.16);border:1px solid rgba(34,197,94,.35)}\n.passTier.locked{opacity:.72}\n#passPanel{width:min(96vw,760px)}\n.brawlPassHeader{\n  margin:10px 0 14px;padding:14px;border-radius:18px;\n  background:linear-gradient(135deg,rgba(250,204,21,.20),rgba(249,115,22,.16));\n  border:1px solid rgba(250,204,21,.35)\n}\n.brawlPassTrack{\n  display:flex;gap:12px;overflow-x:auto;padding:12px 4px 18px;scroll-snap-type:x proximity;\n  border-radius:18px;background:rgba(2,6,23,.36);border:1px solid rgba(255,255,255,.10)\n}\n.brawlPassTrack::-webkit-scrollbar{height:10px}\n.brawlPassTrack::-webkit-scrollbar-thumb{background:#facc15;border-radius:999px}\n.brawlTierCard{\n  min-width:150px;scroll-snap-align:start;border-radius:18px;padding:12px;text-align:center;position:relative;\n  background:linear-gradient(180deg,rgba(30,41,59,.95),rgba(15,23,42,.95));\n  border:2px solid rgba(255,255,255,.14);box-shadow:0 10px 24px rgba(0,0,0,.30)\n}\n.brawlTierCard.done{border-color:rgba(34,197,94,.75);background:linear-gradient(180deg,rgba(22,101,52,.72),rgba(15,23,42,.95))}\n.brawlTierCard.open{border-color:rgba(250,204,21,.75);box-shadow:0 0 0 3px rgba(250,204,21,.12),0 10px 24px rgba(0,0,0,.30)}\n.brawlTierCard.locked{opacity:.62;filter:saturate(.72)}\n.brawlTierNum{\n  position:absolute;top:-10px;left:50%;transform:translateX(-50%);\n  min-width:36px;padding:4px 8px;border-radius:999px;background:#facc15;color:#111827;font-weight:900;border:2px solid rgba(0,0,0,.35)\n}\n.brawlRewardIcon{font-size:34px;margin:12px 0 6px}\n.brawlRewardText{font-weight:900;color:#fde68a;min-height:34px}\n.brawlTaskText{font-size:12px;opacity:.88;line-height:1.25;margin-top:6px;min-height:32px}\n.brawlClaim{margin-top:8px;font-weight:900;font-size:12px}\n.miniButton{min-width:70px;border:0;border-radius:10px;padding:8px 12px;background:#22c55e;color:white;font-weight:bold;cursor:pointer}\n.miniButton:disabled{background:#64748b;opacity:.8;cursor:not-allowed}\n.closePanel{\n  position:sticky;top:0;float:right;z-index:3;border:0;border-radius:12px;padding:8px 12px;\n  background:#ef4444;color:white;font-weight:900;cursor:pointer;box-shadow:0 5px 0 rgba(0,0,0,.25)\n}\n.closePanel:active{transform:translateY(3px);box-shadow:0 2px 0 rgba(0,0,0,.25)}\n.codeRow{display:flex;gap:8px;margin:10px 0}\n.codeRow input{flex:1;padding:10px;border-radius:10px;border:1px solid rgba(255,255,255,.2);background:#0f172a;color:white;outline:none}\n.weaponGrid{display:grid;grid-template-columns:1fr;gap:10px;margin:12px 0}\n.weaponCard{display:grid;grid-template-columns:64px 1fr auto;align-items:center;gap:10px;padding:10px;border-radius:14px;background:rgba(15,23,42,.72);border:1px solid rgba(255,255,255,.14)}\n.weaponIcon{height:54px;border-radius:12px;background:radial-gradient(circle,#1e293b,#020617);display:flex;align-items:center;justify-content:center;font-size:30px}\n.weaponInfo b{display:block;margin-bottom:4px}\n.weaponInfo small{opacity:.84;line-height:1.35}\n\n#gameOverScreen,#winScreen{position:fixed;inset:0;z-index:30;display:none;align-items:center;justify-content:center;background:radial-gradient(circle,#1e3a8a,#0f172a 65%,#020617)}\n.panel{width:min(92vw,540px);max-height:92vh;overflow-y:auto;padding:28px;text-align:center}\n.panel h1{font-size:34px;margin-bottom:10px}\n.button{width:100%;margin-top:8px;border:0;border-radius:16px;padding:14px 22px;font-size:18px;font-weight:bold;color:white;background:linear-gradient(135deg,#38bdf8,#6366f1);cursor:pointer}\n\n#hud{position:fixed;left:16px;top:16px;z-index:10;width:min(380px,calc(100vw - 32px));display:none;flex-direction:column;gap:8px}\n.hudText{font-size:13px;font-weight:bold;text-shadow:0 2px 5px black}\n.barWrap{width:100%;height:20px;border-radius:999px;background:rgba(0,0,0,.45);border:1px solid rgba(255,255,255,.22);overflow:hidden}\n.bar{height:100%;width:100%;transition:width .12s linear}\n#healthBar{background:linear-gradient(90deg,#22c55e,#a3e635)}\n#staminaBar{background:linear-gradient(90deg,#38bdf8,#818cf8)}\n#enemyBar{background:linear-gradient(90deg,#ef4444,#fb7185)}\n#crosshair{position:fixed;left:50%;top:50%;z-index:9;width:18px;height:18px;transform:translate(-50%,-50%);display:none;pointer-events:none}\n#crosshair:before,#crosshair:after{content:\"\";position:absolute;background:white;box-shadow:0 0 8px rgba(0,0,0,.85)}\n#crosshair:before{left:8px;top:0;width:2px;height:18px}\n#crosshair:after{left:0;top:8px;width:18px;height:2px}\n#scopeOverlay{position:fixed;inset:0;z-index:8;pointer-events:none;display:none;background:radial-gradient(circle at center,transparent 0 28%,rgba(0,0,0,.82) 29% 100%)}\n.scopeCircle{position:absolute;left:50%;top:50%;width:min(64vw,64vh);height:min(64vw,64vh);transform:translate(-50%,-50%);border:5px solid rgba(10,10,10,.98);border-radius:50%;box-shadow:inset 0 0 30px rgba(0,0,0,.75)}\n.scopeLine{position:absolute;left:50%;top:50%;background:rgba(255,255,255,.78);transform:translate(-50%,-50%)}\n.scopeLine.horizontal{width:min(42vw,42vh);height:2px}\n.scopeLine.vertical{width:2px;height:min(42vw,42vh)}\n#message{position:fixed;top:52px;left:50%;transform:translateX(-50%);z-index:40;display:none;padding:10px 16px;border-radius:999px;background:rgba(0,0,0,.65);border:1px solid rgba(255,255,255,.15);font-weight:bold}\n#hint{position:fixed;right:16px;top:16px;z-index:10;display:none;max-width:380px;padding:12px 14px;border-radius:14px;background:rgba(0,0,0,.55);border:1px solid rgba(255,255,255,.14);font-size:13px;line-height:1.45}\n\n\n#matchOverlay{\n  position:fixed;inset:0;z-index:60;display:none;align-items:center;justify-content:center;\n  background:radial-gradient(circle at center,rgba(56,189,248,.18),rgba(2,6,23,.86) 62%,rgba(0,0,0,.94));\n  pointer-events:auto;\n}\n.matchBox{\n  width:min(88vw,460px);padding:24px;border-radius:26px;text-align:center;\n  background:rgba(15,23,42,.92);border:2px solid rgba(250,204,21,.38);\n  box-shadow:0 24px 90px rgba(0,0,0,.55),0 0 34px rgba(56,189,248,.18);\n}\n.matchTitle{font-size:30px;font-weight:900;color:#facc15;text-shadow:0 4px 0 rgba(0,0,0,.35);margin-bottom:8px}\n.matchSpinner{\n  width:92px;height:92px;border-radius:50%;margin:14px auto;\n  border:8px solid rgba(255,255,255,.16);border-top-color:#facc15;border-right-color:#38bdf8;\n  animation:matchSpin .8s linear infinite;\n}\n.matchDots{font-weight:900;letter-spacing:2px;animation:matchPulse .9s ease-in-out infinite alternate}\n@keyframes matchSpin{to{transform:rotate(360deg)}}\n@keyframes matchPulse{from{opacity:.45;transform:scale(.98)}to{opacity:1;transform:scale(1.03)}}\n\n#mobileControls{position:fixed;inset:0;z-index:15;display:none;pointer-events:none}\n.joystick{\n  pointer-events:auto;position:absolute;width:150px;height:150px;border-radius:50%;\n  background:rgba(15,23,42,.45);border:2px solid rgba(255,255,255,.22);\n  box-shadow:0 12px 25px rgba(0,0,0,.35)\n}\n.joyKnob{\n  position:absolute;left:48px;top:48px;width:54px;height:54px;border-radius:50%;\n  background:linear-gradient(135deg,#38bdf8,#6366f1);\n  border:2px solid rgba(255,255,255,.7);\n  box-shadow:0 6px 0 rgba(0,0,0,.25)\n}\n#moveJoy{left:18px;bottom:24px}\n#lookJoy{right:18px;bottom:24px}\n\/* Fortnite tarz\u0131: sa\u011f alt ate\u015f b\u00fcy\u00fck, z\u0131pla \u00fcst\u00fcnde, build sol *\/\n.mobileActions{\n  position:absolute;right:16px;bottom:190px;display:grid;\n  grid-template-columns:64px 72px;grid-template-rows:repeat(3,60px);gap:10px;pointer-events:auto;\n  touch-action:none\n}\n.mobileBuilds{\n  position:absolute;left:16px;bottom:190px;display:grid;\n  grid-template-columns:repeat(2,56px);gap:8px;pointer-events:auto;\n  touch-action:none\n}\n#mobileControls.hud-edit .mobileActions,\n#mobileControls.hud-edit .mobileBuilds,\n#mobileControls.hud-edit .joystick{\n  outline:2px dashed #facc15;outline-offset:4px\n}\n.touchBtn{\n  border:0;border-radius:18px;background:rgba(15,23,42,.82);color:white;font-weight:900;font-size:13px;\n  border:1px solid rgba(255,255,255,.22);box-shadow:0 6px 0 rgba(0,0,0,.25);height:58px;min-width:56px\n}\n.touchBtn:active{transform:translateY(4px);box-shadow:0 2px 0 rgba(0,0,0,.25);background:rgba(56,189,248,.78)}\n#mobFire{grid-column:2;grid-row:2\/4;height:128px;font-size:16px;background:rgba(239,68,68,.55);border-color:rgba(239,68,68,.6)}\n#mobJump{grid-column:1;grid-row:3;background:rgba(34,197,94,.45)}\n#mobBuild{background:rgba(250,204,21,.35);color:#111}\n\n#chatBox{\n  position:fixed;left:12px;bottom:12px;z-index:99999;width:min(380px,94vw);\n  display:flex;flex-direction:column;\n  background:rgba(2,6,23,.95);border:2px solid rgba(250,204,21,.5);border-radius:12px;\n  box-shadow:0 8px 28px rgba(0,0,0,.55);pointer-events:auto;user-select:none\n}\n#chatTitle{padding:6px 10px;font-size:11px;font-weight:900;color:#facc15;display:flex;align-items:center;justify-content:space-between;gap:8px}\n#chatOpenBtn{\n  border:0;border-radius:8px;padding:6px 12px;background:#22c55e;color:#fff;\n  font-weight:900;font-size:12px;cursor:pointer;pointer-events:auto\n}\n#chatLog{overflow-y:auto;padding:4px 10px 8px;font-size:12px;line-height:1.4;max-height:120px;color:#e2e8f0}\n#chatLog div{margin-bottom:3px;text-shadow:0 1px 2px #000}\n#chatLog .bot{color:#94a3b8}#chatLog .me{color:#4ade80}#chatLog .enemy{color:#f87171}#chatLog .sys{color:#facc15}#chatLog .ai{color:#38bdf8}#chatLog .global{color:#e2e8f0}\n#chatInputRow{\n  display:none;gap:6px;padding:8px;border-top:1px solid rgba(255,255,255,.15);\n  background:rgba(0,0,0,.55);pointer-events:auto;user-select:text\n}\n#chatInput{\n  flex:1;min-width:0;height:40px;border:2px solid #facc15;border-radius:8px;padding:0 10px;\n  background:#020617;color:#fff;font-size:15px;outline:none;caret-color:#facc15;\n  pointer-events:auto;user-select:text;-webkit-user-select:text\n}\n#chatHint{padding:0 10px 6px;font-size:10px;color:#94a3b8}\n\n@media(max-width:850px){\n  .lobby{height:96vh;grid-template-columns:1fr;grid-template-rows:auto auto 1fr auto}\n  .topBar,.leftPanel,.centerPanel,.rightPanel,.bottomBar{grid-column:1}\n  .leftPanel{display:none}.rightPanel{grid-row:4}.bottomBar{display:none}\n  .centerPanel{min-height:260px}\n  #shopPanel,#passPanel{left:10px;right:10px;top:80px;bottom:80px;width:auto}\n  .logo{font-size:22px}.badge{padding:8px 10px;font-size:13px}\n  #hud{width:270px;gap:5px}\n  .hudText{font-size:11px}\n  .barWrap{height:15px}\n  .bigHero{font-size:86px}\n}\n<\/style>\n<\/head>\n\n<body>\n\n<div id=\"menu\">\n  <div class=\"lobby\">\n    <div class=\"topBar\">\n      <div class=\"logo\">ARENA BUILD<\/div>\n      <div class=\"badgeRow\">\n        <div class=\"badge coinBadge\">\ud83d\udfe1 <span id=\"menuCoins\">0<\/span> U Papel<\/div>\n        <div class=\"badge\">\ud83c\udfc3 <span id=\"menuUnapark\">0<\/span> Unapark<\/div>\n        <div class=\"badge\">\u2b50 Zorluk <span id=\"menuDifficulty\">1<\/span><\/div>\n        <div class=\"badge\">\ud83c\udfab PASS <span id=\"menuPassTier\">1<\/span><\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"leftPanel\">\n      <div class=\"profileCard\">\n        <div class=\"avatar\">\ud83d\ude0e<\/div>\n        <h3>Oyuncu<\/h3>\n        <p>Build ustas\u0131<\/p>\n      <\/div>\n\n      <div class=\"smallCard\"><b>Mod:<\/b><br><span id=\"menuModeText\">1v1<\/span><\/div>\n      <div class=\"smallCard\"><b>Silah:<\/b><br><span id=\"menuWeapon\">Standart T\u00fcfek<\/span><\/div>\n\n      <div class=\"passCard\">\n        <h3>\ud83c\udfab ARENA PASS<\/h3><small>1. Sezon: U Gemisine Kar\u015f\u0131<\/small>\n        <div>Kademe: <b id=\"passTierMini\">1<\/b><\/div>\n        <div class=\"passBar\"><div class=\"passFill\" id=\"passFillMini\"><\/div><\/div>\n        <small id=\"passMiniText\">0 \/ 15 U Papel<\/small>\n      <\/div>\n    <\/div>\n\n    <div class=\"centerPanel\">\n      <div class=\"characterStage\">\n        <div class=\"bigHero\">\ud83e\ude83<\/div>\n        <div class=\"modeChip\">TEAM ARENA<\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"rightPanel\">\n      <div class=\"deviceBox\">\n        <b>Hangi cihazdan oynuyorsun?<\/b>\n        <div class=\"deviceButtons\">\n          <button class=\"deviceBtn active\" id=\"pcBtn\">\ud83d\udcbb Bilgisayar<\/button>\n          <button class=\"deviceBtn\" id=\"mobileBtn\">\ud83d\udcf1 Telefon<\/button>\n        <\/div>\n      <\/div>\n\n      <div class=\"deviceBox\">\n        <b>Oyun modu<\/b>\n        <select id=\"modeSelect\" class=\"modeSelect\">\n          <option value=\"1v1\">1v1<\/option>\n          <option value=\"2v2\">2v2<\/option>\n          <option value=\"3v3\">3v3<\/option>\n          <option value=\"parkour\">\ud83c\udfc3 Parkur Yap<\/option>\n        <\/select>\n      <\/div>\n\n      <div class=\"deviceBox\">\n        <b>Sava\u015f tarz\u0131<\/b>\n        <select id=\"combatSelect\" class=\"modeSelect\">\n          <option value=\"armed\">Silahl\u0131<\/option>\n          <option value=\"unarmed\">Silahs\u0131z<\/option>\n        <\/select>\n      <\/div>\n\n      <div class=\"deviceBox\">\n        <b>Online VS odas\u0131<\/b>\n        <div class=\"deviceButtons\" style=\"grid-template-columns:1fr 1fr;margin-top:8px\">\n          <button class=\"deviceBtn\" id=\"createRoomBtn\">Oda Kur<\/button>\n          <button class=\"deviceBtn\" id=\"joinRoomBtn\">Kat\u0131l<\/button>\n        <\/div>\n        <input id=\"roomCodeInput\" class=\"modeSelect\" placeholder=\"Oda kodu\" style=\"margin-top:8px\" \/>\n        <small style=\"opacity:.8;display:block;margin-top:6px\" id=\"onlineStatusText\">Firebase ayarlar\u0131n\u0131 koda yaz \u2192 Oda Kur \/ Kat\u0131l. \u00dccretsiz Realtime Database kullan\u0131r.<\/small>\n      <\/div>\n\n      <button class=\"menuBtn playBtn\" id=\"startBtn\" type=\"button\">OYNA<\/button>\n      <button class=\"menuBtn shopBtn\" id=\"shopOpenBtn\">\ud83d\uded2 MA\u011eAZA<\/button>\n      <button class=\"menuBtn passBtn\" id=\"passOpenBtn\">\ud83c\udfab PASS<\/button>\n      <button class=\"menuBtn infoBtn\" onclick=\"alert('0 ile 1. \u015fah\u0131s \/ 3. \u015fah\u0131s de\u011fi\u015fir. Silahl\u0131 modda silahlar, silahs\u0131z modda yumruk sald\u0131r\u0131s\u0131 vard\u0131r. Bumerang h\u00e2l\u00e2 Space ile \u0131\u015f\u0131nlan\u0131r. Parkur modunda ilerledik\u00e7e Unapark kazan\u0131rs\u0131n.')\">\u2139\ufe0f NASIL OYNANIR<\/button>\n    <\/div>\n\n    <div class=\"bottomBar\">\n      <div class=\"navCard\">\ud83c\udfe0 Men\u00fc<\/div>\n      <div class=\"navCard\">\ud83d\udd2b Silahlar<\/div>\n      <div class=\"navCard\">\ud83e\ude83 Bumerang<\/div>\n      <div class=\"navCard\">\ud83d\udc65 Tak\u0131m<\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<div id=\"shopPanel\">\n  <button class=\"closePanel\" id=\"shopCloseBtn\">\u2715 Kapat<\/button>\n  <h2 style=\"text-align:center;\">Ma\u011faza<\/h2>\n  <p class=\"coinText\">U Papel: <span id=\"coinCount\">0<\/span><\/p>\n\n  <div class=\"codeRow\">\n    <input id=\"codeInput\" placeholder=\"Kod gir\">\n    <button class=\"miniButton\" id=\"codeBtn\">Gir<\/button>\n  <\/div>\n\n  <h3 style=\"margin:12px 0 6px;text-align:center;color:#facc15;\">Silahlar<\/h3>\n  <div id=\"weaponShop\" class=\"weaponGrid\"><\/div>\n\n  <h3 style=\"margin:14px 0 6px;text-align:center;color:#38bdf8;\">Karakterler ve \u00d6zellikler<\/h3>\n  <div id=\"characterShop\" class=\"weaponGrid\"><\/div>\n\n  <div class=\"shopItem\">\n    <div><b>Mavi Silah Kaplamas\u0131<\/b><br><small>20 U Papel<\/small><\/div>\n    <button class=\"miniButton\" id=\"buyBlueGunBtn\">Al<\/button>\n  <\/div>\n\n  <div class=\"shopItem\">\n    <div><b>H\u0131zl\u0131 \u015earj\u00f6r<\/b><br><small>35 U Papel<\/small><\/div>\n    <button class=\"miniButton\" id=\"buyFastReloadBtn\">Al<\/button>\n  <\/div>\n\n  <div class=\"shopItem\">\n    <div><b>+10 Can Ba\u015flang\u0131c\u0131<\/b><br><small>50 U Papel<\/small><\/div>\n    <button class=\"miniButton\" id=\"buyExtraHealthBtn\">Al<\/button>\n  <\/div>\n<\/div>\n\n<div id=\"passPanel\">\n  <button class=\"closePanel\" id=\"passCloseBtn\">\u2715 Kapat<\/button>\n  <h2 style=\"text-align:center;\">\ud83c\udfab Arena Pass<\/h2>\n  <p class=\"coinText\">15 U Papel = 1 Kademe<\/p>\n\n  <div class=\"passCard\">\n    <div>Kademe: <b id=\"passTierPanel\">1<\/b><\/div>\n    <div class=\"passBar\"><div class=\"passFill\" id=\"passFillPanel\"><\/div><\/div>\n    <small id=\"passPanelText\">0 \/ 15 U Papel<\/small>\n  <\/div>\n\n  <div id=\"passTierList\"><\/div>\n<\/div>\n\n<div id=\"gameOverScreen\">\n  <div class=\"panel\">\n    <h1>Kaybettin<\/h1>\n    <p id=\"loseRewardText\">Ma\u00e7 \u00f6d\u00fcl\u00fc kazand\u0131n.<\/p>\n    <button class=\"button\" id=\"restartBtn1\">Tekrar Oyna<\/button>\n  <\/div>\n<\/div>\n\n<div id=\"winScreen\">\n  <div class=\"panel\">\n    <h1>Kazand\u0131n<\/h1>\n    <p id=\"winRewardText\">Ma\u00e7 \u00f6d\u00fcl\u00fc kazand\u0131n.<\/p>\n    <button class=\"button\" id=\"restartBtn2\">Tekrar Oyna<\/button>\n  <\/div>\n<\/div>\n\n<div id=\"hud\">\n  <div class=\"hudText\">Can<\/div><div class=\"barWrap\"><div class=\"bar\" id=\"healthBar\"><\/div><\/div>\n  <div class=\"hudText\">Ko\u015fma<\/div><div class=\"barWrap\"><div class=\"bar\" id=\"staminaBar\"><\/div><\/div>\n  <div class=\"hudText\">D\u00fc\u015fman Tak\u0131m\u0131<\/div><div class=\"barWrap\"><div class=\"bar\" id=\"enemyBar\"><\/div><\/div>\n  <div class=\"hudText\" id=\"teamText\">Mod: 1v1<\/div>\n  <div class=\"hudText\" id=\"ammoText\">Cephane: 12 \/ 36<\/div>\n  <div class=\"hudText\" id=\"itemText\">Item: Sis Bombas\u0131 x2 | 2 ile kullan<\/div>\n  <div class=\"hudText\" id=\"buildText\">Build: Kapal\u0131<\/div>\n  <div class=\"hudText\" id=\"difficultyText\">Zorluk: 1<\/div>\n<\/div>\n\n<div id=\"hint\">\n<b>Kontrol:<\/b><br>\nWASD: hareket<br>\nMouse: bak\u0131\u015f<br>\nSol t\u0131k: ate\u015f \/ build \/ bumerang \/ yumruk<br>\nSa\u011f t\u0131k: yak\u0131n sald\u0131r\u0131<br>\n0: 1. \u015fah\u0131s \/ 3. \u015fah\u0131s de\u011fi\u015ftir<br>\nSpace: z\u0131pla veya bumeranga \u0131\u015f\u0131nlan<br>\n2: build kapal\u0131yken sis bombas\u0131<br>\nE: sniper d\u00fcrb\u00fcn, baz\u0131 silahlarda namlu<br>\nR: cephane doldur<br>\nB: build modu<br>\n1 duvar, 2 zemin, 3 rampa\n<\/div>\n\n<div id=\"mobileControls\">\n  <div class=\"joystick\" id=\"moveJoy\"><div class=\"joyKnob\" id=\"moveKnob\"><\/div><\/div>\n  <div class=\"joystick\" id=\"lookJoy\"><div class=\"joyKnob\" id=\"lookKnob\"><\/div><\/div>\n\n  <div class=\"mobileActions\">\n    <button class=\"touchBtn\" id=\"mobReload\">R<\/button>\n    <button class=\"touchBtn\" id=\"mobFire\">ATE\u015e<\/button>\n    <button class=\"touchBtn\" id=\"mobMelee\">YAKIN<\/button>\n    <button class=\"touchBtn\" id=\"mobJump\">ZIP<\/button>\n    <button class=\"touchBtn\" id=\"mobScope\">E<\/button>\n    <button class=\"touchBtn\" id=\"mobItem\">ITEM<\/button>\n    <button class=\"touchBtn\" id=\"mobRun\">KO\u015e<\/button>\n  <\/div>\n\n  <div class=\"mobileBuilds\" id=\"mobileBuilds\">\n    <button class=\"touchBtn\" id=\"mobBuild\">BUILD<\/button>\n    <button class=\"touchBtn\" id=\"mobWall\">DUVAR<\/button>\n    <button class=\"touchBtn\" id=\"mobFloor\">ZEM\u0130N<\/button>\n    <button class=\"touchBtn\" id=\"mobRamp\">RAMPA<\/button>\n    <button class=\"touchBtn\" id=\"mobEdit\">EDIT<\/button>\n    <button class=\"touchBtn\" id=\"mobHudEdit\">HUD<\/button>\n  <\/div>\n<\/div>\n\n<div id=\"chatBox\">\n  <div id=\"chatTitle\">\n    <span>\ud83d\udcac Chat<\/span>\n    <button type=\"button\" id=\"chatOpenBtn\">YAZ (T)<\/button>\n  <\/div>\n  <div id=\"chatLog\"><\/div>\n  <div id=\"chatHint\">T veya YAZ butonu \u00b7 Enter gonder \u00b7 Esc kapat<\/div>\n  <div id=\"chatInputRow\">\n    <input id=\"chatInput\" type=\"text\" autocomplete=\"off\" spellcheck=\"false\" placeholder=\"Mesaj yaz, Enter ile gonder...\" maxlength=\"120\" \/>\n  <\/div>\n<\/div>\n\n<div id=\"crosshair\"><\/div>\n\n<div id=\"scopeOverlay\">\n  <div class=\"scopeCircle\"><\/div>\n  <div class=\"scopeLine horizontal\"><\/div>\n  <div class=\"scopeLine vertical\"><\/div>\n<\/div>\n\n\n<div id=\"matchOverlay\">\n  <div class=\"matchBox\">\n    <div class=\"matchTitle\">MA\u00c7 BULUNUYOR<\/div>\n    <div class=\"matchSpinner\"><\/div>\n    <div class=\"matchDots\">ARENA HAZIRLANIYOR...<\/div>\n    <small>Botlar, sand\u0131klar ve U gemisi yerle\u015ftiriliyor<\/small>\n  <\/div>\n<\/div>\n\n<div id=\"message\"><\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/three@0.160.0\/build\/three.min.js\"><\/script>\n<script src=\"https:\/\/www.gstatic.com\/firebasejs\/10.12.2\/firebase-app-compat.js\"><\/script>\n<script src=\"https:\/\/www.gstatic.com\/firebasejs\/10.12.2\/firebase-database-compat.js\"><\/script>\n\n<script>\n'use strict';\n\n\/\/ ========== FIREBASE AYARLARI (\u00dcCRETS\u0130Z) ==========\n\/\/ 1) https:\/\/console.firebase.google.com \u2192 Proje olu\u015ftur\n\/\/ 2) Realtime Database \u2192 Test mode ile olu\u015ftur\n\/\/ 3) Proje ayarlar\u0131 \u2192 Web uygulamas\u0131 ekle \u2192 config de\u011ferlerini a\u015fa\u011f\u0131 yap\u0131\u015ft\u0131r\nconst firebaseConfig = {\n  apiKey: \"AIzaSyBLxfTpqUMJcENfujhr8IKYrPc8VflVrHM\",\n  authDomain: \"uras-1dbdb.firebaseapp.com\",\n  databaseURL: \"https:\/\/uras-1dbdb-default-rtdb.firebaseio.com\",\n  projectId: \"uras-1dbdb\",\n  storageBucket: \"uras-1dbdb.firebasestorage.app\",\n  messagingSenderId: \"978179799813\",\n  appId: \"1:978179799813:web:f27806a516f470b28ad56c\",\n  measurementId: \"G-6RR81DK7DN\"\n};\n\nlet firebaseReady=false;\nlet db=null;\nlet onlineRoomCode=null;\nlet onlinePlayerId=null;\nlet isOnlineHost=false;\nlet onlineMode=false;\nlet remotePlayers={}; \/\/ id -> {mesh, data}\nlet lastSyncTime=0;\nconst SYNC_INTERVAL=60; \/\/ ms\nlet roomRef=null;\nlet playersRef=null;\n\ntry{\n  if(firebaseConfig.apiKey && firebaseConfig.apiKey!=='BURAYA_API_KEY'){\n    firebase.initializeApp(firebaseConfig);\n    db=firebase.database();\n    firebaseReady=true;\n  }\n}catch(e){\n  console.warn('Firebase ba\u015flat\u0131lamad\u0131:',e);\n}\n\nlet scene,camera,renderer,player,gunGroup,buildGhost,groundMesh;\nlet uShip=null,uShipParts=[],uShipCooldown=0,uShipBeams=[];\nlet thirdPersonParts=null,lastMoveAmount=0,lastRunState=false;\nlet bullets=[],walls=[],buildPieces=[],smokes=[],lootCrates=[],keys={};\nlet bulletUid=0;\nlet soundParticles=[];\nlet audioCtx=null;\nlet sfxEnabled=true;\nlet lastSfxTime={};\nlet shakeUntil=0;\nlet shakePower=0;\nlet temporaryWeaponId=null;\nlet bots=[];\nlet allies=[];\nlet enemies=[];\nlet drones=[];\nlet darkDronesSpawned=false;\nlet bossRoundActive=false;\nlet currentBossType=null;\nlet totalMatches=parseInt(localStorage.getItem('arena_totalMatches'))||0;\nlet raycaster=null;\ntry{if(window.THREE)raycaster=new THREE.Raycaster();}catch(e){}\n\nlet gameRunning=false,isPointerLocked=false,isScoped=false,isBarrelAim=false,buildMode=false,buildType='wall',mouseHeld=false;\nlet editMode=false,editStyle='door'; \/\/ door | window\nlet controlMode='pc';\nlet selectedMode='1v1';\nlet combatMode='armed';\nlet parkourActive=false;\nlet parkourPlatforms=[];\nlet parkourGoal=null;\nlet parkourBestIndex=0;\nlet parkourEarnedPreview=0;\nlet parkourStartTime=0;\nlet unapark=parseInt(localStorage.getItem('arena_unapark'))||0;\nlet thirdPerson=false;\nlet thirdPersonModel=null;\nlet lastUnarmedAttackTime=0;\nconst unarmedDamage=25;\nconst unarmedRange=2.8;\nlet moveJoy={x:0,y:0};\nlet lookJoy={x:0,y:0};\nlet mobileRun=false;\n\nconst normalFov=75,scopedFov=24,buildDistance=4.2;\n\nlet coins=parseInt(localStorage.getItem('arena_coins'))||0;\nlet difficultyLevel=parseInt(localStorage.getItem('arena_difficulty'))||1;\nlet passPapel=parseInt(localStorage.getItem('arena_pass_papel'))||0;\nlet claimedPass=JSON.parse(localStorage.getItem('arena_pass_claimed'))||{};\n\nlet upgrades=JSON.parse(localStorage.getItem('arena_upgrades'))||{\n  blueSkin:false,\n  fastReload:false,\n  extraHealth:false\n};\n\nlet currentWeapon=localStorage.getItem('arena_currentWeapon')||'rifle';\n\nlet ownedWeapons=JSON.parse(localStorage.getItem('arena_ownedWeapons'))||{\n  rifle:true,\n  ak47:false,\n  smg:false,\n  sniper:false,\n  shotgun:false,\n  plasma:false,\n  boomerang:false,\n  burst:false,\n  laser:false,\n  minigun:false,\n  ice:false\n};\n\n\/\/ Yeni karakter sistemi. Eski kay\u0131tlar\u0131 bozmamak i\u00e7in eksik karakterleri a\u015fa\u011f\u0131da otomatik ekliyoruz.\nlet currentCharacter=localStorage.getItem('arena_currentCharacter')||'rookie';\nlet ownedCharacters=JSON.parse(localStorage.getItem('arena_ownedCharacters'))||{\n  rookie:true,\n  shadow:false,\n  robot:false,\n  titan:false,\n  medic:false,\n  jet:false\n};\n\nconst passTiers=[\n  {tier:1,task:'1 ma\u00e7 oyna',reward:'+5 U Papel',coins:5},\n  {tier:2,task:'Bumerang\u0131 dene',reward:'+10 U Papel',coins:10},\n  {tier:3,task:'2v2 oyna',reward:'+12 U Papel',coins:12},\n  {tier:4,task:'3v3 oyna',reward:'+15 U Papel',coins:15},\n  {tier:5,task:'Botu yen',reward:'+25 U Papel',coins:25},\n  {tier:6,task:'Build kur',reward:'+30 U Papel',coins:30},\n  {tier:7,task:'Yak\u0131n sald\u0131r\u0131 yap',reward:'+35 U Papel',coins:35},\n  {tier:8,task:'Bumeranga \u0131\u015f\u0131nlan',reward:'+40 U Papel',coins:40}\n];\n\nconst weapons={\n  rifle:{name:'Standart T\u00fcfek',icon:'\ud83d\udd2b',price:0,damage:18,cooldown:250,clip:12,reserve:36,color:0x475569,tag:'Dengeli silah',auto:false,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  ak47:{name:'AK-47',icon:'\ud83e\udeb5',price:60,damage:8,cooldown:70,clip:20,reserve:40,color:0x8b4513,tag:'Otomatik, h\u0131zl\u0131 bo\u015falt\u0131r',auto:true,pellets:1,scope:false,barrelAim:false,type:'gun'},\n  smg:{name:'SMG',icon:'\ud83d\udfe9',price:45,damage:11,cooldown:95,clip:24,reserve:72,color:0x22c55e,tag:'H\u0131zl\u0131 ate\u015f',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  sniper:{name:'Keskin Ni\u015fanc\u0131',icon:'\ud83c\udfaf',price:80,damage:45,cooldown:700,clip:4,reserve:12,color:0xfacc15,tag:'D\u00fcrb\u00fcnl\u00fc, y\u00fcksek hasar',auto:false,pellets:1,scope:true,barrelAim:false,type:'gun'},\n  shotgun:{name:'Pompal\u0131',icon:'\ud83d\udca5',price:70,damage:8,cooldown:780,clip:5,reserve:20,color:0xef4444,tag:'Yak\u0131ndan g\u00fc\u00e7l\u00fc sa\u00e7ma',auto:false,pellets:6,scope:false,barrelAim:false,type:'gun'},\n  plasma:{name:'Plazma Tabanca',icon:'\ud83d\udfe6',price:90,damage:16,cooldown:180,clip:14,reserve:42,color:0x38bdf8,tag:'Enerji mermisi',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  boomerang:{name:'Bumerang',icon:'\ud83e\ude83',price:75,damage:18,cooldown:450,clip:0,reserve:0,color:0xf97316,tag:'F\u0131rlat, geri d\u00f6ner, Space \u0131\u015f\u0131nlan\u0131r',auto:false,pellets:1,scope:false,barrelAim:false,type:'boomerang'},\n  burst:{name:'Burst T\u00fcfek',icon:'\ud83d\udd3a',price:85,damage:14,cooldown:420,clip:18,reserve:54,color:0xa855f7,tag:'3 mermi seri atar',auto:false,pellets:1,burst:3,scope:false,barrelAim:true,type:'gun'},\n  laser:{name:'Lazer T\u00fcfe\u011fi',icon:'\ud83d\udd34',price:120,damage:20,cooldown:210,clip:16,reserve:48,color:0xff3355,tag:'H\u0131zl\u0131 lazer mermisi',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  minigun:{name:'Mini Gun',icon:'\ud83c\udf00',price:150,damage:8,cooldown:45,clip:60,reserve:120,color:0x94a3b8,tag:'\u00c7ok h\u0131zl\u0131, orta hasar',auto:true,pellets:1,scope:false,barrelAim:false,type:'gun'},\n  ice:{name:'Buz Tabancas\u0131',icon:'\u2744\ufe0f',price:110,damage:12,cooldown:230,clip:14,reserve:42,color:0x7dd3fc,tag:'Botu k\u0131sa s\u00fcre yava\u015flat\u0131r',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun',slow:true},\n  rocket:{name:'Roketatar',icon:'\ud83d\ude80',price:160,damage:55,cooldown:1100,clip:1,reserve:6,color:0xf97316,tag:'Patlamal\u0131 roket, alan hasar\u0131',auto:false,pellets:1,scope:false,barrelAim:false,type:'gun',explosive:true,splash:4.5},\n  railgun:{name:'Ray T\u00fcfe\u011fi',icon:'\u26a1',price:175,damage:70,cooldown:900,clip:2,reserve:8,color:0xa78bfa,tag:'Delici y\u00fcksek hasar, d\u00fcrb\u00fcnl\u00fc',auto:false,pellets:1,scope:true,barrelAim:false,type:'gun'},\n  flamethrower:{name:'Alev Makinesi',icon:'\ud83d\udd25',price:140,damage:6,cooldown:55,clip:40,reserve:80,color:0xef4444,tag:'Yak\u0131n menzil s\u00fcrekli alev',auto:true,pellets:1,scope:false,barrelAim:false,type:'gun',shortRange:true},\n  crossbow:{name:'Arbalet',icon:'\ud83c\udff9',price:95,damage:38,cooldown:650,clip:1,reserve:12,color:0x854d0e,tag:'Sessiz, y\u00fcksek hasar',auto:false,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  tactical:{name:'Taktik SMG',icon:'\ud83d\udda4',price:100,damage:13,cooldown:75,clip:30,reserve:90,color:0x1e293b,tag:'Fortnite tarz\u0131 h\u0131zl\u0131 SMG',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  deagle:{name:'Deagle',icon:'\ud83d\udfe8',price:70,damage:32,cooldown:420,clip:7,reserve:21,color:0xeab308,tag:'G\u00fc\u00e7l\u00fc tabanca',auto:false,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  scar:{name:'SCAR',icon:'\ud83d\udfe2',price:110,damage:16,cooldown:160,clip:30,reserve:60,color:0x16a34a,tag:'Dengeli otomatik t\u00fcfek',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  bolt:{name:'Bolt Sniper',icon:'\ud83d\udfe3',price:130,damage:85,cooldown:1200,clip:1,reserve:8,color:0x7c3aed,tag:'Tek at\u0131\u015f dev hasar, d\u00fcrb\u00fcn',auto:false,pellets:1,scope:true,barrelAim:false,type:'gun'},\n  dual:{name:'\u00c7ift Tabanca',icon:'\ud83d\udd2b',price:85,damage:11,cooldown:110,clip:18,reserve:54,color:0xf472b6,tag:'\u00c7ift namlu h\u0131zl\u0131 ate\u015f',auto:true,pellets:2,scope:false,barrelAim:false,type:'gun'},\n  pump:{name:'Pump Shotgun',icon:'\ud83d\udfe0',price:75,damage:12,cooldown:900,clip:5,reserve:15,color:0xea580c,tag:'Fortnite pump, yak\u0131n y\u0131k\u0131m',auto:false,pellets:8,scope:false,barrelAim:false,type:'gun'},\n  ar:{name:'Assault Rifle',icon:'\ud83d\udd35',price:55,damage:15,cooldown:175,clip:30,reserve:90,color:0x2563eb,tag:'Klasik AR, dengeli',auto:true,pellets:1,scope:false,barrelAim:true,type:'gun'},\n  heavy:{name:'Heavy Sniper',icon:'\u2b1b',price:180,damage:110,cooldown:1500,clip:1,reserve:5,color:0x0f172a,tag:'Tek at\u0131\u015f, dev hasar',auto:false,pellets:1,scope:true,barrelAim:false,type:'gun'},\n  drum:{name:'Drum Gun',icon:'\ud83e\udd41',price:125,damage:10,cooldown:90,clip:40,reserve:80,color:0xb45309,tag:'B\u00fcy\u00fck \u015farj\u00f6r, orta hasar',auto:true,pellets:1,scope:false,barrelAim:false,type:'gun'},\n  grappler:{name:'Grappler',icon:'\ud83e\ude9d',price:95,damage:5,cooldown:800,clip:10,reserve:20,color:0x06b6d4,tag:'H\u0131zl\u0131 ate\u015f, d\u00fc\u015f\u00fck hasar, e\u011flenceli',auto:true,pellets:1,scope:false,barrelAim:false,type:'gun'},\n  pickaxe:{name:'Balyoz',icon:'\ud83d\udd28',price:0,damage:22,cooldown:400,clip:0,reserve:0,color:0x94a3b8,tag:'Build k\u0131rar, yak\u0131n hasar',auto:false,pellets:1,scope:false,barrelAim:false,type:'melee',buildDamage:55}\n};\n\nconst lootWeaponPool=['rifle','ak47','smg','sniper','shotgun','plasma','burst','laser','minigun','ice','rocket','railgun','flamethrower','crossbow','tactical','deagle','scar','bolt','dual','pump','ar','heavy','drum','grappler','pickaxe'];\n\n\/\/ Build malzeme sistemi (Ah\u015fap \/ Ta\u015f \/ Metal)\nconst buildMaterials={\n  wood:{name:'Ah\u015fap',hpMul:1,color:{wall:0xb45309,floor:0xa16207,ramp:0xca8a04}},\n  stone:{name:'Ta\u015f',hpMul:1.7,color:{wall:0x64748b,floor:0x57534e,ramp:0x78716c}},\n  metal:{name:'Metal',hpMul:2.5,color:{wall:0x94a3b8,floor:0x64748b,ramp:0xcbd5e1}}\n};\nlet buildMaterial='wood';\nlet stormRadius=58,stormTarget=58,stormMesh=null,stormRing=null,stormDamageTimer=0;\nlet totalWins=parseInt(localStorage.getItem('arena_wins'))||0;\n\nconst characters={\n  rookie:{name:'Standart Sava\u015f\u00e7\u0131',icon:'\ud83d\ude0e',price:0,color:0x2563eb,hand:0xf2b28c,visor:0x111827,tag:'Dengeli karakter',hpBonus:0,speedMul:1,regenMul:1,reloadMul:1,damageMul:1,doubleJump:false},\n  shadow:{name:'Karanl\u0131k Gezgin',icon:'\ud83c\udf0c',price:80,color:0x111827,hand:0x0f172a,visor:0x38bdf8,tag:'+%18 h\u0131z, daha \u00e7evik',hpBonus:0,speedMul:1.18,regenMul:1,reloadMul:1,damageMul:1,doubleJump:false},\n  robot:{name:'Neon Robot',icon:'\ud83e\udd16',price:100,color:0x64748b,hand:0x94a3b8,visor:0x22c55e,tag:'Daha h\u0131zl\u0131 \u015farj\u00f6r doldurur',hpBonus:10,speedMul:1,regenMul:1,reloadMul:.72,damageMul:.92,doubleJump:false},\n  titan:{name:'Demir Titan',icon:'\ud83d\udee1\ufe0f',price:120,color:0x334155,hand:0xcbd5e1,visor:0xfacc15,tag:'+35 can, hasar\u0131 azalt\u0131r ama biraz a\u011f\u0131r',hpBonus:35,speedMul:.95,regenMul:1, reloadMul:1.05,damageMul:.75,doubleJump:false},\n  medic:{name:'\u015eifa Uzman\u0131',icon:'\ud83d\udc9a',price:95,color:0x16a34a,hand:0xf2b28c,visor:0x86efac,tag:'Can yenilemesi daha h\u0131zl\u0131',hpBonus:5,speedMul:1,regenMul:1.9,reloadMul:1,damageMul:1,doubleJump:false},\n  jet:{name:'Jet Avc\u0131s\u0131',icon:'\ud83d\ude80',price:130,color:0xdc2626,hand:0xf2b28c,visor:0xf97316,tag:'\u00c7ift z\u0131plama yapabilir',hpBonus:0,speedMul:1.05,regenMul:1,reloadMul:1,damageMul:1,doubleJump:true}\n};\n\nconst bossTypes={\n  giantRobot:{name:'Dev Robot',icon:'\ud83e\udd16',color:0x64748b,hp:360,scale:2.25,speedMul:.68,damage:15,cooldown:720,reward:55,tag:'Yava\u015f ama \u00e7ok dayan\u0131kl\u0131'},\n  laserTower:{name:'Lazerli Kule Botu',icon:'\ud83d\udd34',color:0xff6b00,hp:280,scale:1.75,speedMul:.08,damage:6,cooldown:180,reward:50,stationary:true,tag:'Sabit durur, h\u0131zl\u0131 turuncu lazer atar'},\n  flyingDroneBoss:{name:'U\u00e7an Dron Boss',icon:'\ud83d\udef8',color:0x38bdf8,hp:240,scale:1.45,speedMul:1.25,damage:9,cooldown:430,reward:60,flying:true,tag:'H\u0131zl\u0131 u\u00e7ar ve konum de\u011fi\u015ftirir'},\n  shieldWarrior:{name:'Kalkanl\u0131 Sava\u015f\u00e7\u0131',icon:'\ud83d\udee1\ufe0f',color:0xfacc15,hp:320,scale:1.8,speedMul:.82,damage:13,cooldown:650,reward:65,shield:.45,tag:'Gelen hasar\u0131 azalt\u0131r'}\n};\n\nfor(const id of Object.keys(weapons)){\n  if(ownedWeapons[id]===undefined)ownedWeapons[id]=false;\n}\nownedWeapons.rifle=true;\nownedWeapons.pickaxe=true;\nfor(const id of Object.keys(characters)){\n  if(ownedCharacters[id]===undefined)ownedCharacters[id]=false;\n}\nownedCharacters.rookie=true;\nif(!characters[currentCharacter])currentCharacter='rookie';\n\nlet playerHealth=100,playerMaxHealth=100,stamina=100,lastPlayerDamageTime=0;\nlet ammoInClip=12,maxClip=12,ammoReserve=36,isReloading=false,reloadDuration=1500,lastPlayerShotTime=0;\nlet reloadAnimStart=0;\nlet smokeBombCount=2;\n\nlet moveSpeed=.16,runSpeedMultiplier=1.75,yaw=0,pitch=0,playerVelocityY=0,isGrounded=true,forwardHoldTime=0;\nconst gravity=-.008,jumpStrength=.22,autoRunDelay=650,healDelay=5000,healSpeed=.16;\nlet jumpsLeft=1;\n\nlet boomerang=null;\nlet boomerangActive=false;\nlet boomerangReturning=false;\nlet boomerangDir=null;\nlet boomerangStart=null;\ntry{\n  if(window.THREE){\n    boomerangDir=new THREE.Vector3();\n    boomerangStart=new THREE.Vector3();\n  }\n}catch(e){}\nlet boomerangHitOut=false;\nlet boomerangHitBack=false;\nlet lastMeleeTime=0;\nlet meleeAnimUntil=0;\nlet throwAnimUntil=0;\nconst meleeCooldown=520;\nconst meleeDamage=25;\n\nconst buildColors={wall:0x60a5fa,floor:0x22c55e,ramp:0xf59e0b};\nconst buildNames={wall:'Duvar',floor:'Zemin',ramp:'Rampa'};\n\nconst menu=document.getElementById('menu');\nconst gameOverScreen=document.getElementById('gameOverScreen');\nconst winScreen=document.getElementById('winScreen');\nconst startBtn=document.getElementById('startBtn');\nconst restartBtn1=document.getElementById('restartBtn1');\nconst restartBtn2=document.getElementById('restartBtn2');\nconst shopOpenBtn=document.getElementById('shopOpenBtn');\nconst passOpenBtn=document.getElementById('passOpenBtn');\nconst shopCloseBtn=document.getElementById('shopCloseBtn');\nconst passCloseBtn=document.getElementById('passCloseBtn');\nconst shopPanel=document.getElementById('shopPanel');\nconst passPanel=document.getElementById('passPanel');\nconst hud=document.getElementById('hud');\nconst hint=document.getElementById('hint');\nconst crosshair=document.getElementById('crosshair');\nconst scopeOverlay=document.getElementById('scopeOverlay');\nconst messageBox=document.getElementById('message');\nconst matchOverlay=document.getElementById('matchOverlay');\nconst coinCountSpan=document.getElementById('coinCount');\nconst menuCoins=document.getElementById('menuCoins');\nconst menuUnapark=document.getElementById('menuUnapark');\nconst menuDifficulty=document.getElementById('menuDifficulty');\nconst menuPassTier=document.getElementById('menuPassTier');\nconst menuWeapon=document.getElementById('menuWeapon');\nconst menuModeText=document.getElementById('menuModeText');\nconst modeSelect=document.getElementById('modeSelect');\nconst combatSelect=document.getElementById('combatSelect');\nconst passTierMini=document.getElementById('passTierMini');\nconst passFillMini=document.getElementById('passFillMini');\nconst passMiniText=document.getElementById('passMiniText');\nconst passTierPanel=document.getElementById('passTierPanel');\nconst passFillPanel=document.getElementById('passFillPanel');\nconst passPanelText=document.getElementById('passPanelText');\nconst passTierList=document.getElementById('passTierList');\nconst weaponShop=document.getElementById('weaponShop');\nconst characterShop=document.getElementById('characterShop');\nconst codeInput=document.getElementById('codeInput');\nconst codeBtn=document.getElementById('codeBtn');\nconst buyBlueGunBtn=document.getElementById('buyBlueGunBtn');\nconst buyFastReloadBtn=document.getElementById('buyFastReloadBtn');\nconst buyExtraHealthBtn=document.getElementById('buyExtraHealthBtn');\nconst healthBar=document.getElementById('healthBar');\nconst staminaBar=document.getElementById('staminaBar');\nconst enemyBar=document.getElementById('enemyBar');\nconst teamText=document.getElementById('teamText');\nconst ammoText=document.getElementById('ammoText');\nconst itemText=document.getElementById('itemText');\nconst buildText=document.getElementById('buildText');\nconst difficultyText=document.getElementById('difficultyText');\nconst pcBtn=document.getElementById('pcBtn');\nconst mobileBtn=document.getElementById('mobileBtn');\nconst mobileControls=document.getElementById('mobileControls');\nconst winRewardText=document.getElementById('winRewardText');\nconst loseRewardText=document.getElementById('loseRewardText');\n\n\/\/ ==================== ONLINE (FIREBASE) ====================\nfunction updateOnlineStatusText(msg){\n  const el=document.getElementById('onlineStatusText');\n  if(el)el.textContent=msg;\n}\n\nfunction generateRoomCode(){\n  const chars='ABCDEFGHJKLMNPQRSTUVWXYZ23456789';\n  let code='';\n  for(let i=0;i<6;i++)code+=chars[Math.floor(Math.random()*chars.length)];\n  return code;\n}\n\nfunction createOnlineRoom(){\n  if(!firebaseReady||!db){\n    showMessage('Firebase ayarlar\u0131 eksik! Kodun ba\u015f\u0131ndaki firebaseConfig de\u011ferlerini doldur.');\n    updateOnlineStatusText('Firebase yap\u0131land\u0131r\u0131lmad\u0131 \u2013 console.firebase.google.com');\n    return;\n  }\n  const code=generateRoomCode();\n  const roomCodeInput=document.getElementById('roomCodeInput');\n  if(roomCodeInput)roomCodeInput.value=code;\n\n  onlinePlayerId='p_'+Math.random().toString(36).slice(2,10);\n  isOnlineHost=true;\n  onlineRoomCode=code;\n  onlineMode=true;\n\n  roomRef=db.ref('rooms\/'+code);\n  playersRef=roomRef.child('players');\n\n  roomRef.set({\n    host:onlinePlayerId,\n    mode:selectedMode||'1v1',\n    combat:combatMode||'armed',\n    created:Date.now(),\n    status:'waiting'\n  }).then(()=>{\n    return playersRef.child(onlinePlayerId).set({\n      name:'Host',\n      x:0,y:2,z:30,\n      yaw:0,pitch:0,\n      health:100,maxHealth:100,\n      weapon:currentWeapon,\n      character:currentCharacter,\n      alive:true,\n      lastSeen:Date.now()\n    });\n  }).then(()=>{\n    playersRef.child(onlinePlayerId).onDisconnect().remove();\n    roomRef.onDisconnect().remove();\n    listenRemotePlayers();\n    listenHits();\n    listenChat();\n    showMessage('Oda kuruldu: '+code+' \u2014 Arkada\u015f\u0131na kodu g\u00f6nder!');\n    updateOnlineStatusText('Oda aktif: '+code+' (Host) \u2014 Bekleniyor...');\n    addChatMessage('sys','Oda kuruldu: '+code);\n  }).catch(err=>{\n    console.error(err);\n    showMessage('Oda kurulamad\u0131: '+err.message);\n    onlineMode=false;\n  });\n}\n\nfunction joinOnlineRoom(){\n  if(!firebaseReady||!db){\n    showMessage('Firebase ayarlar\u0131 eksik! Kodun ba\u015f\u0131ndaki firebaseConfig de\u011ferlerini doldur.');\n    return;\n  }\n  const roomCodeInput=document.getElementById('roomCodeInput');\n  const code=(roomCodeInput&&roomCodeInput.value||'').trim().toUpperCase();\n  if(!code||code.length<4){\n    showMessage('Ge\u00e7erli bir oda kodu gir!');\n    return;\n  }\n\n  onlinePlayerId='p_'+Math.random().toString(36).slice(2,10);\n  isOnlineHost=false;\n  onlineRoomCode=code;\n  onlineMode=true;\n\n  roomRef=db.ref('rooms\/'+code);\n  playersRef=roomRef.child('players');\n\n  roomRef.once('value').then(snap=>{\n    if(!snap.exists()){\n      showMessage('Oda bulunamad\u0131: '+code);\n      onlineMode=false;\n      return;\n    }\n    const data=snap.val();\n    if(data.mode){\n      selectedMode=data.mode;\n      if(modeSelect)modeSelect.value=data.mode;\n      menuModeText.textContent=data.mode;\n    }\n    if(data.combat){\n      combatMode=data.combat;\n      if(combatSelect)combatSelect.value=data.combat;\n    }\n    return playersRef.child(onlinePlayerId).set({\n      name:'Misafir',\n      x:0,y:2,z:-24,\n      yaw:Math.PI,pitch:0,\n      health:100,maxHealth:100,\n      weapon:currentWeapon,\n      character:currentCharacter,\n      alive:true,\n      lastSeen:Date.now()\n    });\n  }).then(()=>{\n    if(!onlineMode)return;\n    playersRef.child(onlinePlayerId).onDisconnect().remove();\n    listenRemotePlayers();\n    listenHits();\n    listenChat();\n    roomRef.update({status:'ready'});\n    showMessage('Odaya kat\u0131ld\u0131n: '+code);\n    updateOnlineStatusText('Oda: '+code+' (Misafir) \u2014 Haz\u0131r!');\n    addChatMessage('sys','Odaya kat\u0131ld\u0131n: '+code);\n  }).catch(err=>{\n    console.error(err);\n    showMessage('Kat\u0131lamad\u0131: '+err.message);\n    onlineMode=false;\n  });\n}\n\nfunction listenRemotePlayers(){\n  if(!playersRef)return;\n  playersRef.on('value',snap=>{\n    const all=snap.val()||{};\n    const seen={};\n\n    for(const id in all){\n      const data=all[id];\n\n      \/\/ Kendi can\u0131m rakip taraf\u0131ndan d\u00fc\u015f\u00fcr\u00fcld\u00fcyse uygula\n      if(id===onlinePlayerId){\n        if(gameRunning&&typeof data.health==='number'&&data.health<playerHealth-0.5){\n          const dmg=playerHealth-data.health;\n          playerHealth=Math.max(0,data.health);\n          lastPlayerDamageTime=performance.now();\n          sfx('hit');\n          makeScreenPulse('rgba(239,68,68,.16)',120);\n          screenShake(.7,100);\n          showMessage('Rakip seni vurdu! (-'+Math.round(dmg)+')');\n          if(playerHealth<=0)loseGame();\n        }\n        continue;\n      }\n\n      seen[id]=true;\n      if(!remotePlayers[id]){\n        remotePlayers[id]=createRemotePlayerMesh(data);\n        showMessage((data.name||'Oyuncu')+' kat\u0131ld\u0131!');\n        updateOnlineStatusText('Oda: '+onlineRoomCode+' \u2014 Rakip ba\u011fland\u0131!');\n      }\n      updateRemotePlayerMesh(remotePlayers[id],data);\n    }\n\n    for(const id in remotePlayers){\n      if(!seen[id]){\n        scene.remove(remotePlayers[id].mesh);\n        delete remotePlayers[id];\n        showMessage('Bir oyuncu ayr\u0131ld\u0131');\n      }\n    }\n  });\n}\n\nfunction createRemotePlayerMesh(data){\n  const group=new THREE.Group();\n  \/\/ Rakip her zaman k\u0131rm\u0131z\u0131\n  const bodyMat=new THREE.MeshStandardMaterial({color:0xdc2626,roughness:.5,emissive:0x7f1d1d,emissiveIntensity:.15});\n  const skinMat=new THREE.MeshStandardMaterial({color:0xf2b28c,roughness:.6});\n  const dark=new THREE.MeshStandardMaterial({color:0x111827,roughness:.4});\n\n  const body=new THREE.Mesh(new THREE.BoxGeometry(.7,.95,.4),bodyMat);body.position.y=1.35;\n  const head=new THREE.Mesh(new THREE.BoxGeometry(.44,.44,.44),skinMat);head.position.y=2.18;\n  const visor=new THREE.Mesh(new THREE.BoxGeometry(.38,.08,.04),new THREE.MeshBasicMaterial({color:0xef4444}));\n  visor.position.set(0,2.18,-.24);\n  const leg1=new THREE.Mesh(new THREE.BoxGeometry(.22,.78,.24),dark);leg1.position.set(-.18,.42,0);\n  const leg2=new THREE.Mesh(new THREE.BoxGeometry(.22,.78,.24),dark);leg2.position.set(.18,.42,0);\n  const gun=new THREE.Mesh(new THREE.BoxGeometry(.14,.11,.65),dark);gun.position.set(.48,1.4,-.4);\n\n  \/\/ Can bar\u0131\n  const hpBg=new THREE.Mesh(new THREE.BoxGeometry(.9,.08,.06),new THREE.MeshBasicMaterial({color:0x111827}));\n  hpBg.position.set(0,2.7,0);\n  const hpFill=new THREE.Mesh(new THREE.BoxGeometry(.86,.06,.07),new THREE.MeshBasicMaterial({color:0x22c55e}));\n  hpFill.position.set(0,2.7,0.01);\n  group.add(body,head,visor,leg1,leg2,gun,hpBg,hpFill);\n  group.userData.hpFill=hpFill;\n\n  \/\/ Oyuncu y=2 iken ayaklar yerde olsun (mesh y=0)\n  const feetY=Math.max(0,(data.y||2)-2);\n  group.position.set(data.x||0,feetY,data.z||0);\n  scene.add(group);\n\n  return {mesh:group,data:data,health:data.health||100};\n}\n\nfunction updateRemotePlayerMesh(rp,data){\n  if(!rp||!rp.mesh||!data)return;\n  rp.data=data;\n  rp.health=typeof data.health==='number'?data.health:rp.health;\n  if(data.alive===false||rp.health<=0){\n    rp.mesh.visible=false;\n    return;\n  }\n  rp.mesh.visible=true;\n  \/\/ Ayaklar yerde: local player y\u22482 \u2192 remote mesh y\u22480\n  const targetY=Math.max(0,(data.y||2)-2);\n  rp.mesh.position.x+=(data.x-rp.mesh.position.x)*0.35;\n  rp.mesh.position.y+=(targetY-rp.mesh.position.y)*0.35;\n  rp.mesh.position.z+=(data.z-rp.mesh.position.z)*0.35;\n  if(typeof data.yaw==='number'){\n    let dy=data.yaw-rp.mesh.rotation.y;\n    while(dy>Math.PI)dy-=Math.PI*2;\n    while(dy<-Math.PI)dy+=Math.PI*2;\n    rp.mesh.rotation.y+=dy*0.3;\n  }\n  \/\/ Can bar\u0131 g\u00fcncelle\n  if(rp.mesh.userData.hpFill){\n    const ratio=Math.max(0,Math.min(1,rp.health\/(data.maxHealth||100)));\n    rp.mesh.userData.hpFill.scale.x=Math.max(0.05,ratio);\n    rp.mesh.userData.hpFill.position.x=-(1-ratio)*0.43;\n    rp.mesh.userData.hpFill.material.color.set(ratio>0.5?0x22c55e:ratio>0.25?0xfacc15:0xef4444);\n  }\n}\n\nfunction syncMyState(){\n  if(!onlineMode||!playersRef||!onlinePlayerId||!gameRunning)return;\n  const now=performance.now();\n  if(now-lastSyncTime<SYNC_INTERVAL)return;\n  lastSyncTime=now;\n\n  \/\/ Can\u0131 Firebase'ten gelen d\u00fc\u015f\u00fck de\u011ferle ezme \u2014 sadece kendi local can\u0131n\u0131 yaz\n  playersRef.child(onlinePlayerId).update({\n    x:player.position.x,\n    y:player.position.y,\n    z:player.position.z,\n    yaw:yaw,\n    pitch:pitch,\n    health:Math.max(0,playerHealth),\n    maxHealth:playerMaxHealth,\n    weapon:temporaryWeaponId||currentWeapon,\n    character:currentCharacter,\n    alive:playerHealth>0,\n    lastSeen:Date.now()\n  }).catch(()=>{});\n}\n\nfunction getRemotePlayersAsTargets(){\n  const list=[];\n  for(const id in remotePlayers){\n    const rp=remotePlayers[id];\n    if(!rp||!rp.mesh||!rp.mesh.visible||rp.health<=0)continue;\n    \/\/ Hitbox ayaklardan g\u00f6vdeye: mesh.position.y ayak, g\u00f6vde +1.2\n    const hitPos=rp.mesh.position.clone();\n    hitPos.y+=1.2;\n    list.push({\n      position:hitPos,\n      meshPos:rp.mesh.position,\n      userData:{health:rp.health,isRemote:true,remoteId:id,maxHealth:rp.data&&rp.data.maxHealth||100}\n    });\n  }\n  return list;\n}\n\nfunction damageRemotePlayer(remoteId,amount){\n  if(!playersRef||!remoteId||!roomRef)return;\n  const rp=remotePlayers[remoteId];\n  if(!rp)return;\n  const dmg=Math.max(1,Math.round(amount));\n  \/\/ Yerel tahmin\n  rp.health=Math.max(0,(rp.health||100)-dmg);\n  \/\/ Hasar olay\u0131n\u0131 Firebase'e yaz (rakip dinler)\n  const hitRef=roomRef.child('hits').push();\n  hitRef.set({\n    target:remoteId,\n    from:onlinePlayerId,\n    damage:dmg,\n    t:Date.now()\n  }).then(()=>{\n    \/\/ 5 sn sonra sil\n    setTimeout(()=>{try{hitRef.remove();}catch(e){}},5000);\n  }).catch(()=>{});\n  \/\/ Ayr\u0131ca do\u011frudan health g\u00fcncelle (yedek)\n  playersRef.child(remoteId).transaction(cur=>{\n    if(!cur)return cur;\n    const h=Math.max(0,(typeof cur.health==='number'?cur.health:100)-dmg);\n    cur.health=h;\n    cur.alive=h>0;\n    return cur;\n  }).catch(()=>{});\n\n  if(rp.health<=0){\n    showMessage('Rakip yenildi!');\n    addChatMessage('sys','Rakip elendi!');\n    setTimeout(()=>{\n      let anyAlive=false;\n      for(const id in remotePlayers){\n        if(remotePlayers[id]&&remotePlayers[id].health>0)anyAlive=true;\n      }\n      if(!anyAlive&&gameRunning)winGame();\n    },250);\n  }\n}\n\nfunction listenHits(){\n  if(!roomRef||!onlinePlayerId)return;\n  roomRef.child('hits').on('child_added',snap=>{\n    const hit=snap.val();\n    if(!hit||hit.target!==onlinePlayerId)return;\n    if(hit.from===onlinePlayerId)return;\n    const dmg=hit.damage||10;\n    playerHealth=Math.max(0,playerHealth-dmg);\n    lastPlayerDamageTime=performance.now();\n    sfx('hit');\n    makeScreenPulse('rgba(239,68,68,.18)',140);\n    screenShake(.85,120);\n    showMessage('Vuruldun! -'+dmg);\n    addChatMessage('enemy','Hasar ald\u0131n (-'+dmg+')');\n    if(playerHealth<=0)loseGame();\n  });\n}\n\nfunction leaveOnlineRoom(){\n  if(playersRef&&onlinePlayerId){\n    try{playersRef.child(onlinePlayerId).remove();}catch(e){}\n  }\n  if(roomRef){\n    try{roomRef.child('hits').off();}catch(e){}\n    if(isOnlineHost){try{roomRef.remove();}catch(e){}}\n  }\n  for(const id in remotePlayers){\n    if(remotePlayers[id]&&remotePlayers[id].mesh)scene.remove(remotePlayers[id].mesh);\n  }\n  remotePlayers={};\n  onlineMode=false;\n  onlineRoomCode=null;\n  isOnlineHost=false;\n  onlinePlayerId=null;\n  roomRef=null;\n  playersRef=null;\n  updateOnlineStatusText('Online kapand\u0131 \u2014 bot moduna d\u00f6nd\u00fcn');\n  const roomCodeInput=document.getElementById('roomCodeInput');\n  if(roomCodeInput)roomCodeInput.value='';\n  addChatMessage('sys','Bot moduna d\u00f6n\u00fcld\u00fc');\n}\n\nfunction init(){\n  \/\/ OYNA her durumda ba\u011flans\u0131n\n  const sb=document.getElementById('startBtn');\n  const rb1=document.getElementById('restartBtn1');\n  const rb2=document.getElementById('restartBtn2');\n  if(sb)sb.onclick=()=>{try{startWithMatchmaking();}catch(e){alert('Hata: '+e.message);console.error(e);}};\n  if(rb1)rb1.onclick=()=>{try{startWithMatchmaking();}catch(e){alert('Hata: '+e.message);}};\n  if(rb2)rb2.onclick=()=>{try{startWithMatchmaking();}catch(e){alert('Hata: '+e.message);}};\n\n  if(!window.THREE){\n    alert('Three.js y\u00fcklenemedi. \u0130nternet ba\u011flant\u0131n\u0131 kontrol et.');\n    return;\n  }\n\n  try{\n  if(!raycaster)raycaster=new THREE.Raycaster();\n  if(!boomerangDir)boomerangDir=new THREE.Vector3();\n  if(!boomerangStart)boomerangStart=new THREE.Vector3();\n\n  scene=new THREE.Scene();\n  scene.background=new THREE.Color(0x0a1128);\n  scene.fog=new THREE.FogExp2(0x0a1128,.015);\n\n  camera=new THREE.PerspectiveCamera(normalFov,innerWidth\/innerHeight,.1,1000);\n\n  renderer=new THREE.WebGLRenderer({antialias:true});\n  renderer.setSize(innerWidth,innerHeight);\n  renderer.setPixelRatio(Math.min(devicePixelRatio,2));\n  renderer.shadowMap.enabled=true;\n  renderer.domElement.style.position='relative';\n  renderer.domElement.style.zIndex='1';\n  document.body.appendChild(renderer.domElement);\n  \/\/ Chat her zaman en \u00fcstte kals\u0131n\n  const chatEl=document.getElementById('chatBox');\n  if(chatEl){chatEl.style.zIndex='99999';document.body.appendChild(chatEl);}\n\n  scene.add(new THREE.AmbientLight(0xffffff,.38));\n\n  const dirLight=new THREE.DirectionalLight(0x8899ff,1.35);\n  dirLight.position.set(20,40,20);\n  dirLight.castShadow=true;\n  scene.add(dirLight);\n\n  createArena();\n  createUShip();\n\n  player=new THREE.Object3D();\n  player.position.set(0,2,20);\n  player.add(camera);\n  scene.add(player);\n\n  createPlayerGun();\n  if(parkourActive&&gunGroup)gunGroup.visible=false;\n  createThirdPersonModel();\n  applyCameraMode();\n  createBuildGhost();\n\n  addEventListener('resize',onWindowResize);\n  document.addEventListener('contextmenu',e=>e.preventDefault());\n\n  document.addEventListener('keydown',e=>{\n    \/\/ T = chat ac\/kapa (her zaman)\n    if((e.code==='KeyT'||e.key==='t'||e.key==='T')&&!chatTyping&&!chatOpen){\n      const ae=document.activeElement;\n      if(!(ae&&(ae.tagName==='INPUT'||ae.tagName==='TEXTAREA'||ae.tagName==='SELECT'))){\n        e.preventDefault();\n        e.stopPropagation();\n        openChat();\n        return;\n      }\n    }\n    if(chatOpen||chatTyping){\n      if(e.code==='Enter'||e.key==='Enter'){e.preventDefault();e.stopPropagation();sendChat();return;}\n      if(e.code==='Escape'){e.preventDefault();e.stopPropagation();closeChat();return;}\n      return; \/\/ yazarken oyun tusu alma\n    }\n    const ae=document.activeElement;\n    if(ae&&(ae.id==='chatInput'||ae.tagName==='INPUT'||ae.tagName==='TEXTAREA'))return;\n    keys[e.code]=true;\n    handleKeyDown(e);\n  },true);\n\n  document.addEventListener('keyup',e=>{\n    if(chatTyping)return;\n    const ae=document.activeElement;\n    if(ae&&(ae.id==='chatInput'||ae.tagName==='INPUT'||ae.tagName==='TEXTAREA'))return;\n    keys[e.code]=false;\n    handleKeyUp(e);\n  },true);\n\n  document.addEventListener('mousemove',onMouseMove);\n  document.addEventListener('mousedown',onMouseDown);\n  document.addEventListener('mouseup',onMouseUp);\n  document.addEventListener('pointerlockchange',onPointerLockChange);\n\n  pcBtn.onclick=()=>setDevice('pc');\n  mobileBtn.onclick=()=>setDevice('mobile');\n\n  modeSelect.onchange=()=>{\n    selectedMode=modeSelect.value;\n    menuModeText.textContent=selectedMode;\n  };\n\n  combatSelect.onchange=()=>{\n    combatMode=combatSelect.value;\n    createPlayerGun();\n    applyCameraMode();\n    showMessage(combatMode==='armed'?'Silahl\u0131 mod se\u00e7ildi':'Silahs\u0131z mod se\u00e7ildi');\n  };\n\n  startBtn.onclick=()=>startWithMatchmaking();\n  restartBtn1.onclick=()=>startWithMatchmaking();\n  restartBtn2.onclick=()=>startWithMatchmaking();\n\n  shopOpenBtn.onclick=toggleShop;\n  passOpenBtn.onclick=togglePass;\n  shopCloseBtn.onclick=()=>{shopPanel.style.display='none'};\n  passCloseBtn.onclick=()=>{passPanel.style.display='none'};\n  codeBtn.onclick=redeemCode;\n\n  buyBlueGunBtn.onclick=()=>buyUpgrade('blueSkin',20);\n  buyFastReloadBtn.onclick=()=>buyUpgrade('fastReload',35);\n  buyExtraHealthBtn.onclick=()=>buyUpgrade('extraHealth',50);\n\n  setupMobileJoystick();\n  setupMobileButtons();\n  try{setupChatUI();}catch(e){console.warn('chat setup',e);}\n\n  const createRoomBtn=document.getElementById('createRoomBtn');\n  const joinRoomBtn=document.getElementById('joinRoomBtn');\n  const roomCodeInput=document.getElementById('roomCodeInput');\n  if(createRoomBtn){\n    createRoomBtn.onclick=()=>createOnlineRoom();\n  }\n  if(joinRoomBtn){\n    joinRoomBtn.onclick=()=>joinOnlineRoom();\n  }\n\n  updateShopUI();\n  updatePassUI();\n  animate();\n  }catch(err){\n    console.error('init error',err);\n    alert('Kurulum hatas\u0131: '+(err&&err.message?err.message:err));\n  }\n}\n\n\/\/ Ba\u015flat\ntry{init();}catch(e){console.error(e);alert('Init: '+e.message);}\n\n\nfunction ensureAudio(){\n  if(!sfxEnabled)return null;\n  if(!audioCtx){\n    const Ctx=window.AudioContext||window.webkitAudioContext;\n    if(!Ctx)return null;\n    audioCtx=new Ctx();\n  }\n  if(audioCtx.state==='suspended')audioCtx.resume();\n  return audioCtx;\n}\n\nfunction tone(freq,duration=.08,type='sine',volume=.05,slide=0){\n  const ctx=ensureAudio();\n  if(!ctx)return;\n  const osc=ctx.createOscillator();\n  const gain=ctx.createGain();\n  osc.type=type;\n  osc.frequency.setValueAtTime(freq,ctx.currentTime);\n  if(slide)osc.frequency.exponentialRampToValueAtTime(Math.max(30,freq+slide),ctx.currentTime+duration);\n  gain.gain.setValueAtTime(volume,ctx.currentTime);\n  gain.gain.exponentialRampToValueAtTime(.001,ctx.currentTime+duration);\n  osc.connect(gain);\n  gain.connect(ctx.destination);\n  osc.start();\n  osc.stop(ctx.currentTime+duration);\n}\n\nfunction noise(duration=.08,volume=.05,filter=900){\n  const ctx=ensureAudio();\n  if(!ctx)return;\n  const buffer=ctx.createBuffer(1,Math.max(1,ctx.sampleRate*duration),ctx.sampleRate);\n  const data=buffer.getChannelData(0);\n  for(let i=0;i<data.length;i++)data[i]=(Math.random()*2-1)*(1-i\/data.length);\n  const source=ctx.createBufferSource();\n  const gain=ctx.createGain();\n  const biquad=ctx.createBiquadFilter();\n  biquad.type='lowpass';\n  biquad.frequency.value=filter;\n  gain.gain.setValueAtTime(volume,ctx.currentTime);\n  gain.gain.exponentialRampToValueAtTime(.001,ctx.currentTime+duration);\n  source.buffer=buffer;\n  source.connect(biquad);\n  biquad.connect(gain);\n  gain.connect(ctx.destination);\n  source.start();\n  source.stop(ctx.currentTime+duration);\n}\n\nfunction sfx(name,force=false){\n  if(!sfxEnabled)return;\n  const now=performance.now();\n  const gap={shot:75,botShot:130,laserTick:260,droneLaser:360,hit:45,foot:160}[name]||0;\n  if(!force&&gap&&lastSfxTime[name]&&now-lastSfxTime[name]<gap)return;\n  lastSfxTime[name]=now;\n\n  if(name==='shot'){tone(180,.045,'square',.035,-60);noise(.045,.025,1800);}\n  else if(name==='botShot'){tone(150,.04,'sawtooth',.025,-40);noise(.035,.018,1300);}\n  else if(name==='laserStart'){tone(520,.07,'sawtooth',.035,180);}\n  else if(name==='laserTick'){tone(760,.035,'sine',.018,40);}\n  else if(name==='laserEmpty'){tone(130,.16,'sawtooth',.04,-70);}\n  else if(name==='reload'){noise(.12,.035,1200);tone(260,.05,'triangle',.02,90);}\n  else if(name==='reloadDone'){tone(520,.055,'triangle',.035,220);}\n  else if(name==='boomerangThrow'){tone(430,.12,'triangle',.04,260);noise(.07,.02,2500);}\n  else if(name==='boomerangCatch'){tone(700,.05,'triangle',.035,-220);}\n  else if(name==='melee'){noise(.08,.04,1600);tone(260,.06,'square',.025,-80);}\n  else if(name==='hit'){tone(95,.06,'square',.04,-25);}\n  else if(name==='bossHit'){tone(70,.08,'sawtooth',.05,-20);noise(.08,.035,700);}\n  else if(name==='death'){tone(170,.12,'sawtooth',.04,-100);}\n  else if(name==='build'){tone(300,.05,'triangle',.035,140);noise(.06,.025,900);}\n  else if(name==='crate'){tone(500,.06,'triangle',.03,220);tone(760,.06,'sine',.025,120);}\n  else if(name==='droneSpawn'){tone(320,.08,'sine',.035,160);tone(620,.08,'triangle',.03,240);}\n  else if(name==='droneLaser'){tone(880,.035,'sawtooth',.015,30);}\n  else if(name==='teleport'){tone(520,.08,'sine',.04,420);tone(1040,.09,'triangle',.025,-260);}\n  else if(name==='win'){tone(520,.08,'triangle',.04,260);setTimeout(()=>tone(760,.08,'triangle',.04,260),90);setTimeout(()=>tone(1040,.12,'triangle',.045,160),180);}\n  else if(name==='lose'){tone(260,.12,'sawtooth',.04,-90);setTimeout(()=>tone(170,.18,'sawtooth',.04,-70),110);}\n  else if(name==='boss'){tone(90,.22,'sawtooth',.055,-20);setTimeout(()=>noise(.18,.045,700),80);}\n  else if(name==='start'){tone(360,.06,'triangle',.035,180);setTimeout(()=>tone(610,.08,'triangle',.035,140),70);}\n  else if(name==='jump'){tone(320,.06,'sine',.025,150);}\n}\n\nfunction screenShake(amount=.8,duration=120){\n  shakePower=Math.max(shakePower,amount);\n  shakeUntil=Math.max(shakeUntil,performance.now()+duration);\n}\n\nfunction updateScreenShake(){\n  if(!renderer||!renderer.domElement)return;\n  const now=performance.now();\n  if(now<shakeUntil){\n    const remaining = Math.max(0, shakeUntil - now);\n    const intensity = shakePower * (remaining \/ 120);\n    const x=(Math.random()-.5)*intensity*2;\n    const y=(Math.random()-.5)*intensity*2;\n    renderer.domElement.style.transform=`translate(${x}px,${y}px)`;\n  }else{\n    shakePower=0;\n    renderer.domElement.style.transform='';\n  }\n}\n\nfunction makeScreenPulse(color='rgba(255,255,255,.18)',duration=160){\n  let el=document.getElementById('soundPulseFx');\n  if(!el){\n    el=document.createElement('div');\n    el.id='soundPulseFx';\n    el.style.position='fixed';\n    el.style.inset='0';\n    el.style.zIndex='13';\n    el.style.pointerEvents='none';\n    el.style.opacity='0';\n    el.style.transition='opacity .12s linear';\n    document.body.appendChild(el);\n  }\n  el.style.background=color;\n  el.style.opacity='1';\n  clearTimeout(makeScreenPulse.t);\n  makeScreenPulse.t=setTimeout(()=>{el.style.opacity='0'},duration);\n}\n\nfunction spawnSoundParticles(pos,color=0xfacc15,count=8,power=.08){\n  if(!scene||!window.THREE||!pos)return;\n  for(let i=0;i<count;i++){\n    const mesh=new THREE.Mesh(\n      new THREE.SphereGeometry(.045+Math.random()*.035,8,8),\n      new THREE.MeshBasicMaterial({color,transparent:true,opacity:.9})\n    );\n    mesh.position.copy(pos);\n    scene.add(mesh);\n    soundParticles.push({\n      mesh,\n      velocity:new THREE.Vector3((Math.random()-.5)*power,Math.random()*power*.9,(Math.random()-.5)*power),\n      life:26+Math.random()*18\n    });\n  }\n}\n\nfunction updateSoundParticles(){\n  for(let i=soundParticles.length-1;i>=0;i--){\n    const p=soundParticles[i];\n    p.mesh.position.add(p.velocity);\n    p.velocity.y-=.0035;\n    p.life--;\n    if(p.mesh.material)p.mesh.material.opacity=Math.max(0,p.life\/40);\n    if(p.life<=0){\n      scene.remove(p.mesh);\n      soundParticles.splice(i,1);\n    }\n  }\n}\n\nfunction getPassTier(){\n  return Math.floor(passPapel\/15)+1;\n}\n\nfunction getPassProgress(){\n  return passPapel%15;\n}\n\nfunction addMatchRewards(won){\n  const matchCoins=won?8:4;\n  coins+=matchCoins;\n  passPapel+=matchCoins;\n\n  const before=coins;\n  checkPassRewards();\n  const extra=coins-before;\n\n  saveProgress();\n  updateShopUI();\n  updatePassUI();\n\n  return{coins:matchCoins,pass:matchCoins,extra};\n}\n\nfunction checkPassRewards(){\n  const tier=getPassTier();\n\n  for(const reward of passTiers){\n    if(tier>=reward.tier&&!claimedPass[reward.tier]){\n      coins+=reward.coins;\n      claimedPass[reward.tier]=true;\n      showMessage('PASS Kademe '+reward.tier+' \u00f6d\u00fcl\u00fc: '+reward.reward);\n    }\n  }\n}\n\nfunction togglePass(){\n  passPanel.style.display=passPanel.style.display==='none'?'block':'none';\n  shopPanel.style.display='none';\n  updatePassUI();\n}\n\nfunction updatePassUI(){\n  const tier=getPassTier();\n  const progress=getPassProgress();\n  const percent=(progress\/15)*100;\n\n  menuPassTier.textContent=tier;\n  passTierMini.textContent=tier;\n  passTierPanel.textContent=tier;\n\n  passFillMini.style.width=percent+'%';\n  passFillPanel.style.width=percent+'%';\n\n  passMiniText.textContent=progress+' \/ 15 U Papel';\n  passPanelText.textContent=progress+' \/ 15 U Papel';\n\n  passTierList.innerHTML='';\n\n  const header=document.createElement('div');\n  header.className='brawlPassHeader';\n  header.innerHTML=`\n    <div style=\"display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap\">\n      <div><b>\ud83c\udfc6 ARENA PASS<\/b><br><small>1. Sezon: U Gemisine Kar\u015f\u0131<\/small><br><small>Ma\u00e7 oynad\u0131k\u00e7a U Papel ilerlemesi kazan, kademeleri a\u00e7.<\/small><\/div>\n      <div style=\"font-size:24px;font-weight:900;color:#facc15\">Kademe ${tier}<\/div>\n    <\/div>\n  `;\n  passTierList.appendChild(header);\n\n  const track=document.createElement('div');\n  track.className='brawlPassTrack';\n\n  for(const p of passTiers){\n    const done=claimedPass[p.tier];\n    const open=tier>=p.tier;\n    const card=document.createElement('div');\n    card.className='brawlTierCard '+(done?'done':open?'open':'locked');\n    card.innerHTML=`\n      <div class=\"brawlTierNum\">${p.tier}<\/div>\n      <div class=\"brawlRewardIcon\">${done?'\u2705':open?'\ud83c\udf81':'\ud83d\udd12'}<\/div>\n      <div class=\"brawlRewardText\">${p.reward}<\/div>\n      <div class=\"brawlTaskText\">G\u00f6rev: ${p.task}<\/div>\n      <div class=\"brawlClaim\">${done?'ALINDI':open?'A\u00c7IK':'K\u0130L\u0130TL\u0130'}<\/div>\n    `;\n    track.appendChild(card);\n  }\n\n  passTierList.appendChild(track);\n}\n\nfunction setDevice(mode){\n  controlMode=mode;\n  pcBtn.classList.toggle('active',mode==='pc');\n  mobileBtn.classList.toggle('active',mode==='mobile');\n  showMessage(mode==='mobile'?'Telefon modu se\u00e7ildi':'Bilgisayar modu se\u00e7ildi');\n}\n\nfunction setupMobileJoystick(){\n  createJoystick('moveJoy','moveKnob',moveJoy);\n  createJoystick('lookJoy','lookKnob',lookJoy);\n}\n\nfunction createJoystick(baseId,knobId,state){\n  const base=document.getElementById(baseId);\n  const knob=document.getElementById(knobId);\n  let active=false;\n  let pointerId=null;\n\n  function center(){\n    knob.style.left='48px';\n    knob.style.top='48px';\n    state.x=0;\n    state.y=0;\n  }\n\n  base.addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    active=true;\n    pointerId=e.pointerId;\n    base.setPointerCapture(pointerId);\n    move(e);\n  });\n\n  base.addEventListener('pointermove',e=>{\n    if(!active||e.pointerId!==pointerId)return;\n    e.preventDefault();\n    move(e);\n  });\n\n  base.addEventListener('pointerup',e=>{\n    if(e.pointerId!==pointerId)return;\n    active=false;\n    center();\n  });\n\n  base.addEventListener('pointercancel',()=>{\n    active=false;\n    center();\n  });\n\n  function move(e){\n    const rect=base.getBoundingClientRect();\n    const cx=rect.left+rect.width\/2;\n    const cy=rect.top+rect.height\/2;\n    let dx=e.clientX-cx;\n    let dy=e.clientY-cy;\n    const max=48;\n    const len=Math.hypot(dx,dy);\n\n    if(len>max){\n      dx=dx\/len*max;\n      dy=dy\/len*max;\n    }\n\n    knob.style.left=(48+dx)+'px';\n    knob.style.top=(48+dy)+'px';\n\n    state.x=dx\/max;\n    state.y=dy\/max;\n  }\n}\n\nfunction setupMobileButtons(){\n  holdButton('mobFire',()=>{\n    mouseHeld=true;\n\n    if(buildMode){\n      placeBuildPiece();\n      mouseHeld=false;\n    }else{\n      playerShoot();\n    }\n  },()=>{mouseHeld=false});\n\n  document.getElementById('mobMelee').addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    if(combatMode==='unarmed')unarmedAttack();\n    else boomerangMelee();\n  });\n\n  document.getElementById('mobJump').addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    handleJumpOrTeleport();\n  });\n\n  document.getElementById('mobReload').addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    if(gameRunning&&ammoInClip<maxClip&&!isReloading)startReload();\n  });\n\n  holdButton('mobScope',()=>enterScope(),()=>exitScope());\n  document.getElementById('mobItem').addEventListener('pointerdown',e=>{e.preventDefault();useItem()});\n  holdButton('mobRun',()=>{mobileRun=true},()=>{mobileRun=false});\n  document.getElementById('mobBuild').addEventListener('pointerdown',e=>{e.preventDefault();if(gameRunning)toggleBuildMode()});\n  document.getElementById('mobWall').addEventListener('pointerdown',e=>{e.preventDefault();setBuildType('wall')});\n  document.getElementById('mobFloor').addEventListener('pointerdown',e=>{e.preventDefault();setBuildType('floor')});\n  document.getElementById('mobRamp').addEventListener('pointerdown',e=>{e.preventDefault();setBuildType('ramp')});\n  const mobEdit=document.getElementById('mobEdit');\n  if(mobEdit)mobEdit.addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    if(!gameRunning)return;\n    editMode=!editMode;\n    showMessage(editMode?'EDIT a\u00e7\u0131k \u2014 ate\u015f ile kap\u0131\/pencere':'Edit kapal\u0131');\n  });\n  const mobHud=document.getElementById('mobHudEdit');\n  if(mobHud)mobHud.addEventListener('pointerdown',e=>{\n    e.preventDefault();\n    toggleHudEditMode();\n  });\n  setupHudCustomization();\n}\n\nlet hudEditMode=false;\nfunction toggleHudEditMode(){\n  hudEditMode=!hudEditMode;\n  if(mobileControls)mobileControls.classList.toggle('hud-edit',hudEditMode);\n  showMessage(hudEditMode?'HUD d\u00fczenle: panelleri s\u00fcr\u00fckle \u2014 tekrar HUD bitir':'HUD kaydedildi');\n  if(!hudEditMode)saveHudLayout();\n}\n\nfunction setupHudCustomization(){\n  loadHudLayout();\n  makeHudDraggable(document.querySelector('.mobileActions'),'actions');\n  makeHudDraggable(document.querySelector('.mobileBuilds'),'builds');\n  makeHudDraggable(document.getElementById('moveJoy'),'moveJoy');\n  makeHudDraggable(document.getElementById('lookJoy'),'lookJoy');\n}\n\nfunction makeHudDraggable(el,key){\n  if(!el)return;\n  let dragging=false,ox=0,oy=0;\n  el.addEventListener('pointerdown',function(e){\n    if(!hudEditMode)return;\n    e.preventDefault();e.stopPropagation();\n    dragging=true;\n    const r=el.getBoundingClientRect();\n    ox=e.clientX-r.left;oy=e.clientY-r.top;\n    el.setPointerCapture(e.pointerId);\n  });\n  el.addEventListener('pointermove',function(e){\n    if(!dragging||!hudEditMode)return;\n    e.preventDefault();\n    const x=e.clientX-ox,y=e.clientY-oy;\n    el.style.left=Math.max(0,Math.min(window.innerWidth-40,x))+'px';\n    el.style.top=Math.max(0,Math.min(window.innerHeight-40,y))+'px';\n    el.style.right='auto';\n    el.style.bottom='auto';\n  });\n  el.addEventListener('pointerup',function(e){\n    if(!dragging)return;\n    dragging=false;\n    try{el.releasePointerCapture(e.pointerId);}catch(err){}\n  });\n}\n\nfunction saveHudLayout(){\n  const data={};\n  [['.mobileActions','actions'],['.mobileBuilds','builds'],['#moveJoy','moveJoy'],['#lookJoy','lookJoy']].forEach(function(pair){\n    const el=document.querySelector(pair[0]);\n    if(!el)return;\n    data[pair[1]]={left:el.style.left,top:el.style.top,right:el.style.right,bottom:el.style.bottom};\n  });\n  try{localStorage.setItem('arena_hud_layout',JSON.stringify(data));}catch(e){}\n}\n\nfunction loadHudLayout(){\n  let data=null;\n  try{data=JSON.parse(localStorage.getItem('arena_hud_layout')||'null');}catch(e){data=null;}\n  if(!data)return;\n  const map={actions:'.mobileActions',builds:'.mobileBuilds',moveJoy:'#moveJoy',lookJoy:'#lookJoy'};\n  Object.keys(map).forEach(function(k){\n    if(!data[k])return;\n    const el=document.querySelector(map[k]);\n    if(!el)return;\n    if(data[k].left){el.style.left=data[k].left;el.style.right='auto';}\n    if(data[k].top){el.style.top=data[k].top;el.style.bottom='auto';}\n    if(data[k].right&&!data[k].left)el.style.right=data[k].right;\n    if(data[k].bottom&&!data[k].top)el.style.bottom=data[k].bottom;\n  });\n}\n\nfunction holdButton(id,onDown,onUp){\n  const el=document.getElementById(id);\n  el.addEventListener('pointerdown',e=>{e.preventDefault();if(gameRunning)onDown()});\n  el.addEventListener('pointerup',e=>{e.preventDefault();onUp()});\n  el.addEventListener('pointercancel',e=>{e.preventDefault();onUp()});\n  el.addEventListener('pointerleave',()=>onUp());\n}\n\nfunction createArena(){\n  groundMesh=new THREE.Mesh(\n    new THREE.PlaneGeometry(70,70),\n    new THREE.MeshStandardMaterial({color:0x1e293b,roughness:.8})\n  );\n  groundMesh.rotation.x=-Math.PI\/2;\n  groundMesh.receiveShadow=true;\n  scene.add(groundMesh);\n\n  const grid=new THREE.GridHelper(70,35,0x4f46e5,0x334155);\n  grid.position.y=.01;\n  scene.add(grid);\n\n  const borderMat=new THREE.MeshStandardMaterial({color:0x0f172a,roughness:.5});\n  [\n    {x:0,z:-35,w:70,h:6,d:2},\n    {x:0,z:35,w:70,h:6,d:2},\n    {x:-35,z:0,w:2,h:6,d:70},\n    {x:35,z:0,w:2,h:6,d:70}\n  ].forEach(c=>addStaticWall(c.x,c.z,c.w,c.h,c.d,borderMat));\n\n  const obsMat=new THREE.MeshStandardMaterial({color:0x312e81,roughness:.4});\n  [\n    {x:-13,z:-10,w:8,h:4,d:8},\n    {x:13,z:-10,w:8,h:4,d:8},\n    {x:-13,z:10,w:8,h:4,d:8},\n    {x:13,z:10,w:8,h:4,d:8},\n    {x:0,z:0,w:7,h:4,d:7},\n    {x:0,z:-22,w:20,h:3,d:3},\n    {x:0,z:22,w:20,h:3,d:3}\n  ].forEach(c=>addStaticWall(c.x,c.z,c.w,c.h,c.d,obsMat));\n}\n\nfunction addStaticWall(x,z,w,h,d,mat){\n  const m=new THREE.Mesh(new THREE.BoxGeometry(w,h,d),mat);\n  m.position.set(x,h\/2,z);\n  m.castShadow=true;\n  m.receiveShadow=true;\n  scene.add(m);\n  walls.push(m);\n}\n\nfunction createPlayerGun(){\n  if(gunGroup)camera.remove(gunGroup);\n\n  gunGroup=new THREE.Group();\n\n  if(combatMode==='unarmed'&&!temporaryWeaponId){\n    const character=characters[currentCharacter]||characters.rookie;\n    const fistMat=new THREE.MeshStandardMaterial({color:character.hand,metalness:.15,roughness:.45});\n    const armMat=new THREE.MeshStandardMaterial({color:character.color,metalness:.28,roughness:.35});\n    const leftArm=new THREE.Mesh(new THREE.BoxGeometry(.13,.13,.38),armMat);\n    leftArm.position.set(-.13,-.14,-.22);\n    const rightArm=new THREE.Mesh(new THREE.BoxGeometry(.13,.13,.38),armMat);\n    rightArm.position.set(.13,-.14,-.22);\n    const leftFist=new THREE.Mesh(new THREE.SphereGeometry(.075,14,14),fistMat);\n    leftFist.position.set(-.13,-.14,-.46);\n    const rightFist=new THREE.Mesh(new THREE.SphereGeometry(.075,14,14),fistMat);\n    rightFist.position.set(.13,-.14,-.46);\n    gunGroup.add(leftArm,rightArm,leftFist,rightFist);\n    gunGroup.position.set(.08,-.20,-.32);\n    camera.add(gunGroup);\n    return;\n  }\n\n  const activeWeaponId=temporaryWeaponId||currentWeapon;\n  const weapon=weapons[activeWeaponId]||weapons.rifle;\n  const character=characters[currentCharacter]||characters.rookie;\n  const bodyColor=upgrades.blueSkin?0x0ea5e9:weapon.color;\n\n  const mat=new THREE.MeshStandardMaterial({color:bodyColor,metalness:.75,roughness:.25});\n  const dark=new THREE.MeshStandardMaterial({color:0x111827,metalness:.8,roughness:.25});\n  const wood=new THREE.MeshStandardMaterial({color:0x7c3f19,roughness:.55});\n\n  function box(w,h,d,x,y,z,m=mat){\n    const mesh=new THREE.Mesh(new THREE.BoxGeometry(w,h,d),m);\n    mesh.position.set(x,y,z);\n    gunGroup.add(mesh);\n    return mesh;\n  }\n\n  function barrel(r,len,x,y,z){\n    const mesh=new THREE.Mesh(new THREE.CylinderGeometry(r,r,len,12),dark);\n    mesh.rotation.x=Math.PI\/2;\n    mesh.position.set(x,y,z);\n    gunGroup.add(mesh);\n    return mesh;\n  }\n\n  if(activeWeaponId==='boomerang'){\n    const g=createBoomerangModel(0xf97316);\n    g.scale.set(.28,.28,.28);\n    g.position.set(0,0,-.32);\n    gunGroup.add(g);\n  }else if(activeWeaponId==='ak47'){\n    box(.08,.07,.55,0,0,-.28,mat);barrel(.015,.45,0,.01,-.68);box(.08,.08,.22,0,-.01,.05,wood);box(.06,.20,.10,0,-.13,-.22,dark);\n  }else if(activeWeaponId==='smg'){\n    box(.09,.09,.38,0,0,-.23,mat);barrel(.014,.28,0,.01,-.50);box(.055,.18,.08,0,-.13,-.18,dark);\n  }else if(activeWeaponId==='sniper'){\n    box(.07,.06,.72,0,0,-.34,mat);barrel(.012,.70,0,.01,-.92);\n    const scope=new THREE.Mesh(new THREE.CylinderGeometry(.025,.025,.23,12),dark);scope.rotation.z=Math.PI\/2;scope.position.set(0,.065,-.30);gunGroup.add(scope);\n  }else if(activeWeaponId==='shotgun'){\n    box(.10,.08,.54,0,0,-.25,mat);barrel(.025,.50,-.025,.02,-.62);barrel(.025,.50,.025,.02,-.62);box(.10,.10,.20,0,-.02,.05,wood);\n  }else if(activeWeaponId==='plasma'||activeWeaponId==='laser'||activeWeaponId==='ice'){\n    box(.10,.10,.38,0,0,-.24,mat);barrel(.022,.34,0,.015,-.52);\n    const orb=new THREE.Mesh(new THREE.SphereGeometry(.055,14,14),new THREE.MeshBasicMaterial({color:weapon.color}));\n    orb.position.set(0,.04,-.42);gunGroup.add(orb);\n  }else if(activeWeaponId==='minigun'){\n    box(.15,.12,.42,0,0,-.24,mat);\n    for(let i=0;i<4;i++){\n      const b=barrel(.012,.44,(i%2-.5)*.05,(Math.floor(i\/2)-.5)*.05,-.58);\n      b.userData.spin=true;\n    }\n  }else if(activeWeaponId==='rocket'){\n    box(.12,.12,.48,0,0,-.26,mat);barrel(.04,.36,0,.02,-.58);box(.14,.14,.18,0,-.02,.06,dark);\n  }else if(activeWeaponId==='railgun'){\n    box(.08,.07,.68,0,0,-.32,mat);barrel(.018,.55,0,.02,-.78);\n    const scope=new THREE.Mesh(new THREE.CylinderGeometry(.028,.028,.2,12),dark);scope.rotation.z=Math.PI\/2;scope.position.set(0,.07,-.28);gunGroup.add(scope);\n    const coil=new THREE.Mesh(new THREE.TorusGeometry(.06,.012,8,16),new THREE.MeshBasicMaterial({color:0xa78bfa}));coil.position.set(0,.01,-.5);gunGroup.add(coil);\n  }else if(activeWeaponId==='flamethrower'){\n    box(.14,.12,.42,0,0,-.24,mat);barrel(.035,.28,0,.02,-.52);box(.16,.14,.2,0,-.04,.08,dark);\n    const tank=new THREE.Mesh(new THREE.CylinderGeometry(.07,.07,.22,12),dark);tank.position.set(-.12,-.08,-.1);gunGroup.add(tank);\n  }else if(activeWeaponId==='crossbow'){\n    box(.06,.05,.42,0,0,-.22,mat);box(.55,.04,.06,0,.04,-.35,wood);box(.04,.12,.04,0,-.08,-.05,dark);\n  }else{\n    box(.06,.06,.45,0,0,-.22,mat);barrel(.015,.35,0,.01,-.5);box(.05,.14,.05,0,-.08,-.05,mat);\n  }\n\n  const charArm=new THREE.Mesh(\n    new THREE.BoxGeometry(.12,.12,.22),\n    new THREE.MeshStandardMaterial({color:character.color,metalness:.35,roughness:.35})\n  );\n  charArm.position.set(.11,-.12,-.10);\n  gunGroup.add(charArm);\n\n  const charHand=new THREE.Mesh(\n    new THREE.SphereGeometry(.065,12,12),\n    new THREE.MeshStandardMaterial({color:character.hand,metalness:.15,roughness:.45})\n  );\n  charHand.position.set(.16,-.12,-.23);\n  gunGroup.add(charHand);\n\n  const charVisor=new THREE.Mesh(\n    new THREE.BoxGeometry(.13,.035,.02),\n    new THREE.MeshBasicMaterial({color:character.visor})\n  );\n  charVisor.position.set(.02,.09,-.12);\n  gunGroup.add(charVisor);\n\n  gunGroup.position.set(.18,-.18,-.35);\n  camera.add(gunGroup);\n}\n\nfunction createBoomerangModel(color){\n  const group=new THREE.Group();\n  const mat=new THREE.MeshStandardMaterial({color,roughness:.45,metalness:.15});\n\n  const a=new THREE.Mesh(new THREE.BoxGeometry(.18,.06,1.25),mat);\n  const b=new THREE.Mesh(new THREE.BoxGeometry(.18,.06,1.25),mat);\n\n  a.position.set(-.28,0,0);\n  b.position.set(.28,0,0);\n  a.rotation.y=.75;\n  b.rotation.y=-.75;\n  group.add(a,b);\n\n  const core=new THREE.Mesh(\n    new THREE.SphereGeometry(.13,14,14),\n    new THREE.MeshStandardMaterial({color:0x111827,roughness:.3})\n  );\n  core.position.set(0,0,.25);\n  group.add(core);\n\n  return group;\n}\n\nfunction setGunPose(){\n  if(!gunGroup)return;\n\n  const activeWeaponId=temporaryWeaponId||currentWeapon;\n  const now=performance.now();\n\n  if(thirdPerson){\n    gunGroup.visible=false;\n    return;\n  }\n\n  if(activeWeaponId==='boomerang'&&boomerangActive){\n    gunGroup.visible=false;\n    return;\n  }else if(!buildMode){\n    gunGroup.visible=true;\n  }\n\n  if(activeWeaponId==='boomerang'){\n    if(now<meleeAnimUntil){\n      const p=1-(meleeAnimUntil-now)\/350;\n      gunGroup.position.set(.16+Math.sin(p*Math.PI)*.14,-.16,-.32);\n      gunGroup.rotation.set(0,Math.sin(p*Math.PI)*1.8,Math.sin(p*Math.PI)*1.2);\n      return;\n    }\n\n    if(now<throwAnimUntil){\n      const p=1-(throwAnimUntil-now)\/320;\n      gunGroup.position.set(.2,-.16,-.35-Math.sin(p*Math.PI)*.25);\n      gunGroup.rotation.set(0,p*3.5,p*6.2);\n      return;\n    }\n  }\n\n  if(isReloading){\n    const p=Math.min(1,(now-reloadAnimStart)\/Math.max(1,reloadDuration));\n    gunGroup.position.set(.15+Math.sin(p*Math.PI*4)*.025,-.22+Math.sin(p*Math.PI)*.08,-.32);\n    gunGroup.rotation.set(-.7*Math.sin(p*Math.PI),.35*Math.sin(p*Math.PI*2),-.25*Math.sin(p*Math.PI));\n    return;\n  }\n\n  gunGroup.rotation.set(0,0,0);\n\n  if(isBarrelAim){\n    gunGroup.position.set(0,-.12,-.46);\n  }else{\n    gunGroup.position.set(.18,-.18,-.35);\n  }\n}\n\nfunction randomPatrolPoint(){\n  return new THREE.Vector3(\n    -28+Math.random()*56,\n    0,\n    -28+Math.random()*56\n  );\n}\n\nfunction isSpawnClear(pos,radius,minDistance=4){\n  if(collides(pos,radius))return false;\n\n  if(player&&gameRunning){\n    const playerFlat=new THREE.Vector3(player.position.x,0,player.position.z);\n    if(pos.distanceTo(playerFlat)<minDistance)return false;\n  }\n\n  for(const bot of bots){\n    const botFlat=new THREE.Vector3(bot.position.x,0,bot.position.z);\n    if(pos.distanceTo(botFlat)<minDistance)return false;\n  }\n\n  for(const drone of drones){\n    const droneFlat=new THREE.Vector3(drone.position.x,0,drone.position.z);\n    if(pos.distanceTo(droneFlat)<minDistance)return false;\n  }\n\n  return true;\n}\n\nfunction getSafeSpawn(preferredX,preferredZ,radius,minDistance=4){\n  const preferred=new THREE.Vector3(preferredX,0,preferredZ);\n\n  if(isSpawnClear(preferred,radius,minDistance))return preferred;\n\n  const backupPoints=[\n    new THREE.Vector3(0,0,31),\n    new THREE.Vector3(-18,0,31),\n    new THREE.Vector3(18,0,31),\n    new THREE.Vector3(0,0,-31),\n    new THREE.Vector3(-18,0,-31),\n    new THREE.Vector3(18,0,-31),\n    new THREE.Vector3(-31,0,0),\n    new THREE.Vector3(31,0,0),\n    new THREE.Vector3(-24,0,20),\n    new THREE.Vector3(24,0,20),\n    new THREE.Vector3(-24,0,-20),\n    new THREE.Vector3(24,0,-20)\n  ].sort(()=>Math.random()-.5);\n\n  for(const p of backupPoints){\n    if(isSpawnClear(p,radius,minDistance))return p.clone();\n  }\n\n  for(let i=0;i<120;i++){\n    const p=randomPatrolPoint();\n    if(isSpawnClear(p,radius,minDistance))return p;\n  }\n\n  return new THREE.Vector3(preferredX,0,preferredZ);\n}\n\nfunction getNearestVisibleTarget(bot,targets){\n  let visibleTarget=null;\n  let visibleDistance=Infinity;\n\n  for(const t of targets){\n    if(!t||!t.position)continue;\n    if(t.userData&&t.userData.health<=0)continue;\n\n    const d=bot.position.distanceTo(t.position);\n    if(d>32)continue;\n\n    if(hasLineOfSight(bot.position,t.position)&&d<visibleDistance){\n      visibleTarget=t;\n      visibleDistance=d;\n    }\n  }\n\n  return {target:visibleTarget,dist:visibleDistance};\n}\n\nfunction chooseNewPatrolTarget(bot){\n  let point=randomPatrolPoint();\n\n  \/\/ Duvar\u0131n i\u00e7ini hedef se\u00e7erse birka\u00e7 kez tekrar dene.\n  for(let i=0;i<18;i++){\n    point=randomPatrolPoint();\n    if(!collides(point,.9))break;\n  }\n\n  bot.userData.patrolTarget=point;\n  bot.userData.nextPatrolTime=performance.now()+1800+Math.random()*2600;\n}\n\nfunction createBot(team,x,z){\n  const group=new THREE.Group();\n  const isAlly=team==='ally';\n  const shirt=new THREE.MeshStandardMaterial({color:isAlly?0x22c55e:0xef4444,roughness:.55});\n  const skin=new THREE.MeshStandardMaterial({color:0xf2b28c,roughness:.65});\n  const pants=new THREE.MeshStandardMaterial({color:0x111827,roughness:.5});\n  const dark=new THREE.MeshStandardMaterial({color:0x111827,roughness:.4});\n\n  const body=new THREE.Mesh(new THREE.BoxGeometry(.72,.95,.4),shirt);body.position.y=1.35;\n  const chest=new THREE.Mesh(new THREE.BoxGeometry(.78,.22,.44),shirt);chest.position.y=1.72;\n  const head=new THREE.Mesh(new THREE.BoxGeometry(.46,.48,.46),skin);head.position.y=2.2;\n  const visor=new THREE.Mesh(new THREE.BoxGeometry(.4,.08,.04),new THREE.MeshStandardMaterial({color:isAlly?0x22c55e:0xef4444,metalness:.7,roughness:.25,emissive:isAlly?0x166534:0x7f1d1d,emissiveIntensity:.3}));\n  visor.position.set(0,2.2,-.24);\n  const leg1=new THREE.Mesh(new THREE.BoxGeometry(.22,.78,.24),pants);leg1.position.set(-.18,.42,0);\n  const leg2=new THREE.Mesh(new THREE.BoxGeometry(.22,.78,.24),pants);leg2.position.set(.18,.42,0);\n  const arm1=new THREE.Mesh(new THREE.BoxGeometry(.16,.7,.16),shirt);arm1.position.set(-.48,1.25,0);\n  const arm2=new THREE.Mesh(new THREE.BoxGeometry(.16,.7,.16),shirt);arm2.position.set(.48,1.25,0);\n  const gun=new THREE.Mesh(new THREE.BoxGeometry(.14,.11,.7),dark);gun.position.set(.5,1.45,-.42);\n\n  group.add(body,chest,head,visor,leg1,leg2,arm1,arm2,gun);\n  group.userData.animParts={leg1,leg2,arm1,arm2,body};\n  const safeBotSpawn=getSafeSpawn(x,z,.95,7);\n  group.position.set(safeBotSpawn.x,0,safeBotSpawn.z);\n  const namePool=(typeof botNames!=='undefined'&&botNames.length)?botNames:['Nova','Rex','Blitz','Echo','Kai'];\n  const aiName=namePool[Math.floor(Math.random()*namePool.length)]||'Bot';\n  const skill=Math.min(1,0.35+difficultyLevel*0.08+Math.random()*0.25);\n  const hp=Math.round(100+difficultyLevel*8+(isAlly?10:15));\n  group.userData={\n    team,\n    health:hp,\n    maxHealth:hp,\n    lastShot:0,\n    lastMelee:0,\n    decision:0,\n    strafe:Math.random()<.5?-1:1,\n    velocityY:0,\n    grounded:true,\n    slowUntil:0,\n    aiName,\n    skill,\n    \/\/ Geli\u015fmi\u015f AI: patrol \/ search \/ attack \/ flee \/ flank\n    state:'patrol',\n    patrolTarget:randomPatrolPoint(),\n    nextPatrolTime:0,\n    lastSeenTarget:null,\n    stuckTimer:0,\n    preferHighGround:skill>0.5,\n    accuracy:0.55+skill*0.4\n  };\n\n  scene.add(group);\n  bots.push(group);\n  if(isAlly)allies.push(group);\n  else enemies.push(group);\n  if(!isAlly)addChatMessage('ai',aiName+' arena\\'ya girdi (AI lv '+(Math.round(skill*10)\/10)+')');\n\n  return group;\n}\n\nfunction createBossBot(type){\n  const cfg=bossTypes[type]||bossTypes.giantRobot;\n  const boss=createBot('enemy',0,-28);\n\n  boss.userData.isBoss=true;\n  boss.userData.bossType=type;\n  boss.userData.bossName=cfg.name;\n  boss.userData.health=cfg.hp;\n  boss.userData.maxHealth=cfg.hp;\n  boss.userData.speedMul=cfg.speedMul||1;\n  boss.userData.shotDamage=cfg.damage||12;\n  boss.userData.shotCooldown=cfg.cooldown||650;\n  boss.userData.bossReward=cfg.reward||50;\n  boss.userData.stationary=!!cfg.stationary;\n  boss.userData.flying=!!cfg.flying;\n  boss.userData.shield=cfg.shield||0;\n\n  boss.scale.setScalar(cfg.scale||1.5);\n  const safeBossSpawn=getSafeSpawn(0,-28,1.5,12);\n  boss.position.set(safeBossSpawn.x,cfg.flying?3.2:0,safeBossSpawn.z);\n\n  boss.traverse(obj=>{\n    if(obj.isMesh&&obj.material&&obj.material.color){\n      obj.material=obj.material.clone();\n      obj.material.color.set(cfg.color);\n    }\n  });\n\n  const crown=new THREE.Mesh(\n    new THREE.TorusGeometry(.55,.06,10,20),\n    new THREE.MeshBasicMaterial({color:0xfacc15})\n  );\n  crown.position.y=2.95;\n  crown.rotation.x=Math.PI\/2;\n  boss.add(crown);\n\n  showMessage('BOSS MODU: '+cfg.icon+' '+cfg.name+' geldi!');\n  return boss;\n}\n\nfunction setupTeams(){\n  clearBots();\n  if(selectedMode==='parkour')return;\n\n  if(bossRoundActive){\n    createBossBot(currentBossType||'giantRobot');\n    return;\n  }\n\n  if(selectedMode==='1v1'){\n    createBot('enemy',0,-24);\n  }\n\n  if(selectedMode==='2v2'){\n    createBot('ally',-3,18);\n    createBot('enemy',-5,-24);\n    createBot('enemy',5,-24);\n  }\n\n  if(selectedMode==='3v3'){\n    createBot('ally',-4,18);\n    createBot('ally',4,18);\n    createBot('enemy',-8,-24);\n    createBot('enemy',0,-26);\n    createBot('enemy',8,-24);\n  }\n}\n\nfunction clearBots(){\n  for(const b of bots)scene.remove(b);\n  bots=[];\n  allies=[];\n  enemies=[];\n}\n\n\nlet matchmakingLocked=false;\nlet pendingStartRound=false;\n\nfunction showMatchmaking(){\n  if(!matchOverlay)return;\n  matchOverlay.style.display='flex';\n  matchOverlay.style.pointerEvents='none';\n  const title=matchOverlay.querySelector('.matchTitle');\n  const dots=matchOverlay.querySelector('.matchDots');\n  const small=matchOverlay.querySelector('small');\n  if(title)title.textContent='MA\u00c7 BULUNUYOR';\n  if(dots)dots.textContent='ARENA HAZIRLANIYOR...';\n  if(small)small.textContent=onlineMode?'Online rakip bekleniyor \/ ba\u011flan\u0131yor':'Botlar, sand\u0131klar ve U gemisi yerle\u015ftiriliyor';\n  clearTimeout(showMatchmaking.timer);\n  showMatchmaking.timer=setTimeout(()=>{\n    if(matchOverlay)matchOverlay.style.display='none';\n    matchmakingLocked=false;\n  },1800);\n}\n\nfunction hideMatchmaking(){\n  matchmakingLocked=false;\n  if(matchOverlay)matchOverlay.style.display='none';\n}\n\nfunction startWithMatchmaking(){\n  \/\/ Basit ve g\u00fcvenilir ba\u015flatma \u2014 men\u00fcde kalma sorununu \u00e7\u00f6zer\n  if(gameRunning)return;\n  try{\n    ensureAudio();\n    showMatchmaking();\n    if(controlMode==='mobile'){\n      startMobileRound();\n    }else{\n      isPointerLocked=true;\n      try{if(renderer&&renderer.domElement)renderer.domElement.requestPointerLock();}catch(e){}\n      startRound();\n    }\n  }catch(err){\n    console.error('start error',err);\n    alert('Oyun ba\u015flat\u0131lamad\u0131: '+(err&&err.message?err.message:err));\n    \/\/ Yine de men\u00fcy\u00fc kapat\u0131p dene\n    try{isPointerLocked=true;startRound();}catch(e2){console.error(e2);}\n  }\n}\n\nfunction createUShip(){\n  if(uShip)scene.remove(uShip);\n  uShip=new THREE.Group();\n  uShipParts=[];\n  const hullMat=new THREE.MeshStandardMaterial({color:0x0f172a,metalness:.65,roughness:.28,emissive:0x07111f,emissiveIntensity:.35});\n  const glowMat=new THREE.MeshBasicMaterial({color:0x38bdf8});\n  function part(w,h,d,x,y,z){\n    const m=new THREE.Mesh(new THREE.BoxGeometry(w,h,d),hullMat);\n    m.position.set(x,y,z);m.castShadow=true;m.receiveShadow=true;\n    m.userData.isUShip=true;\n    uShip.add(m);uShipParts.push(m);return m;\n  }\n  part(2.2,.55,7.6,-3.2,0,0);\n  part(2.2,.55,7.6,3.2,0,0);\n  part(8.6,.55,2.0,0,0,3.0);\n  const core=new THREE.Mesh(new THREE.SphereGeometry(.7,18,18),glowMat);\n  core.position.set(0,-.16,3.1);core.userData.isUShip=true;uShip.add(core);uShipParts.push(core);\n  const leftGlow=new THREE.Mesh(new THREE.BoxGeometry(.18,.18,6.2),glowMat);leftGlow.position.set(-3.2,-.35,0);leftGlow.userData.isUShip=true;uShip.add(leftGlow);uShipParts.push(leftGlow);\n  const rightGlow=leftGlow.clone();rightGlow.position.x=3.2;rightGlow.userData.isUShip=true;uShip.add(rightGlow);uShipParts.push(rightGlow);\n  uShip.position.set(0,20,0);\n  uShip.rotation.y=Math.PI;\n  scene.add(uShip);\n}\n\nfunction positionUShip(){\n  if(!uShip)return;\n  if(parkourActive)uShip.position.set(0,20,70);\n  else uShip.position.set(0,20,0);\n}\n\nfunction updateUShip(){\n  if(!uShip)return;\n  uShip.rotation.y+=.0025;\n  for(let i=uShipBeams.length-1;i>=0;i--){\n    const beam=uShipBeams[i];\n    beam.life--;\n    if(beam.life<=0){scene.remove(beam.mesh);uShipBeams.splice(i,1);}\n  }\n}\n\nfunction bulletHitUShip(from,to,team){\n  if(!uShip||!uShipParts.length)return false;\n  const delta=to.clone().sub(from);\n  const distance=delta.length();\n  if(distance<=.001)return false;\n  delta.normalize();\n  raycaster.set(from,delta);\n  const hits=raycaster.intersectObjects(uShipParts,true);\n  if(hits.length>0&&hits[0].distance<=distance+.25){\n    flashUShip();\n    if(team==='player')uShipRetaliate();\n    return true;\n  }\n  return false;\n}\n\nfunction flashUShip(){\n  if(!uShip)return;\n  for(const p of uShipParts){\n    if(!p.material||!p.material.color)continue;\n    const old=p.material.color.clone();\n    p.material.color.set(0xfff7ad);\n    setTimeout(()=>{if(p.material&&p.material.color)p.material.color.copy(old)},90);\n  }\n}\n\nfunction uShipRetaliate(){\n  const now=performance.now();\n  if(now<uShipCooldown)return;\n  uShipCooldown=now+850;\n  const start=uShip.position.clone();\n  start.y-=.8;\n  const target=player.position.clone();\n  target.y=1.25;\n  createBeamBetween(start,target,0xff8a00,.16,18);\n  if(hasLineOfSight(start,target)){\n    playerHealth-=10;\n    lastPlayerDamageTime=performance.now();\n    sfx('hit');\n    makeScreenPulse('rgba(255,138,0,.18)',160);\n    screenShake(.9,140);\n    showMessage('U gemisi kar\u015f\u0131 lazer att\u0131!');\n    if(playerHealth<=0)loseGame();\n  }else{\n    showMessage('U gemisi lazer att\u0131 ama engel tuttu!');\n  }\n}\n\nfunction createBeamBetween(start,end,color=0xff8a00,width=.08,life=12){\n  const mid=start.clone().lerp(end,.5);\n  const dir=end.clone().sub(start);\n  const len=dir.length();\n  if(len<=.01)return;\n  const mesh=new THREE.Mesh(new THREE.CylinderGeometry(width,width,len,10),new THREE.MeshBasicMaterial({color,transparent:true,opacity:.78}));\n  mesh.position.copy(mid);\n  mesh.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0),dir.normalize());\n  scene.add(mesh);\n  uShipBeams.push({mesh,life});\n}\n\nfunction requestPointerLock(){\n  renderer.domElement.requestPointerLock();\n}\n\nfunction startMobileRound(){\n  isPointerLocked=true;\n  startRound();\n  mobileControls.style.display='block';\n}\n\nfunction onPointerLockChange(){\n  if(controlMode!=='pc')return;\n  if(document.pointerLockElement===renderer.domElement){\n    isPointerLocked=true;\n  }else{\n    isPointerLocked=false;\n    \/\/ ESC ile \u00e7\u0131k\u0131\u015f \u2014 oyunu men\u00fcye al\n    if(gameRunning){\n      gameRunning=false;\n      mouseHeld=false;\n      hideMatchmaking();\n      menu.style.display='flex';\n      hud.style.display='none';\n      hint.style.display='none';\n      crosshair.style.display='none';\n      mobileControls.style.display='none';\n      exitScope();\n      setBuildMode(false);\n    }\n  }\n}\n\n\nfunction getEffectiveCharacter(){\n  const base=characters[currentCharacter]||characters.rookie;\n  if(!parkourActive)return base;\n  \/\/ Parkurda g\u00f6r\u00fcn\u00fc\u015f se\u00e7ili karakterde kal\u0131r, ama Karanl\u0131k Gezgin g\u00fcc\u00fc verilir.\n  return Object.assign({},base,{speedMul:Math.max(base.speedMul||1,1.18),regenMul:Math.max(base.regenMul||1,1.25),reloadMul:base.reloadMul||1,damageMul:base.damageMul||1,doubleJump:true});\n}\n\nfunction createParkourCourse(){\n  clearParkourCourse();\n  const matStart=new THREE.MeshStandardMaterial({color:0x22c55e,roughness:.55});\n  const matMid=new THREE.MeshStandardMaterial({color:0x38bdf8,roughness:.55});\n  const matHard=new THREE.MeshStandardMaterial({color:0xf97316,roughness:.55});\n  const matGoal=new THREE.MeshStandardMaterial({color:0xfacc15,roughness:.35,metalness:.25});\n\n  \/\/ Ba\u015flang\u0131\u00e7 platformu: her zaman sabit, geni\u015f ve oyuncunun alt\u0131nda\n  const startSize=7.5;\n  const startY=0.4;\n  const startPlatform=new THREE.Mesh(new THREE.BoxGeometry(startSize,.4,startSize),matStart);\n  startPlatform.position.set(0,startY,0);\n  startPlatform.receiveShadow=true;\n  startPlatform.userData={isParkour:true,index:0,reward:1,isStart:true};\n  scene.add(startPlatform);\n  parkourPlatforms.push(startPlatform);\n\n  let x=0;\n  let prevZ=0;\n  for(let i=1;i<24;i++){\n    const difficulty=Math.floor(i\/5);\n    const size=Math.max(2.4,5.0-difficulty*.4);\n    const gap=5.5+difficulty*.5;\n    if(i>1)x+=rand(-4.0-difficulty*.3,4.0+difficulty*.3);\n    x=Math.max(-20,Math.min(20,x));\n    const z=prevZ+gap;\n    prevZ=z;\n    const y=0.25+difficulty*.18+Math.sin(i*.8)*.2;\n    const mat=i>18?matHard:matMid;\n    const platform=new THREE.Mesh(new THREE.BoxGeometry(size,.32,size),mat);\n    platform.position.set(x,y,z);\n    platform.receiveShadow=true;\n    platform.userData={isParkour:true,index:i,reward:Math.max(1,Math.floor(i\/3)+1)};\n    scene.add(platform);\n    parkourPlatforms.push(platform);\n\n    if(i>2&&i%4===0){\n      const rail=new THREE.Mesh(new THREE.BoxGeometry(size*.7,1.1,.22),new THREE.MeshStandardMaterial({color:0x0f172a,roughness:.5}));\n      rail.position.set(x,y+.7,z+size*.36);\n      rail.userData={isParkour:true,index:i,obstacle:true};\n      scene.add(rail);\n      parkourPlatforms.push(rail);\n      walls.push(rail);\n    }\n  }\n\n  const last=parkourPlatforms.filter(p=>p.userData&&!p.userData.obstacle).at(-1);\n  parkourGoal=new THREE.Mesh(new THREE.TorusGeometry(1.2,.12,12,32),new THREE.MeshBasicMaterial({color:0xfacc15}));\n  parkourGoal.position.set(last.position.x,last.position.y+1.7,last.position.z);\n  parkourGoal.rotation.x=Math.PI\/2;\n  scene.add(parkourGoal);\n}\n\nfunction clearParkourCourse(){\n  for(const p of parkourPlatforms){\n    scene.remove(p);\n    walls=walls.filter(w=>w!==p);\n  }\n  parkourPlatforms=[];\n  if(parkourGoal){scene.remove(parkourGoal);parkourGoal=null;}\n  \/\/ parkourActive burada kapat\u0131lmaz \u2014 startRound \/ finishParkour y\u00f6netir\n  parkourBestIndex=0;\n  parkourEarnedPreview=0;\n}\n\nfunction handleParkourPlatformStand(){\n  let stood=false;\n  for(const piece of parkourPlatforms){\n    if(!piece.userData||piece.userData.obstacle)continue;\n    const box=new THREE.Box3().setFromObject(piece);\n    const margin=.55;\n    const insideXZ=player.position.x>box.min.x-margin&&player.position.x<box.max.x+margin&&player.position.z>box.min.z-margin&&player.position.z<box.max.z+margin;\n    if(!insideXZ)continue;\n    const surfaceY=box.max.y;\n    const standY=surfaceY+1.75;\n    \/\/ Geni\u015f yakalama band\u0131 \u2014 z\u0131plarken platforma otursun\n    if(player.position.y<=standY+0.35&&player.position.y>=surfaceY-0.25&&playerVelocityY<=0.2){\n      player.position.y=standY;\n      playerVelocityY=0;\n      isGrounded=true;\n      jumpsLeft=(getEffectiveCharacter().doubleJump)?2:1;\n      stood=true;\n      const idx=piece.userData.index||0;\n      if(idx>parkourBestIndex){\n        parkourBestIndex=idx;\n        parkourEarnedPreview=calculateParkourReward(idx);\n        if(idx%3===0){showMessage('Parkur ilerleme: '+idx+' | '+parkourEarnedPreview+' Unapark');}\n      }\n    }\n  }\n  if(!stood&&player.position.y<0.5){\n    \/\/ d\u00fc\u015ft\u00fc\n  }\n  return stood;\n}\n\nfunction calculateParkourReward(index){\n  \/\/ \u0130lerledik\u00e7e \u00f6d\u00fcl katlan\u0131r: ilk ad\u0131mlar az, ileri k\u0131s\u0131m daha \u00e7ok Unapark verir.\n  return Math.max(0,Math.floor(index*1.4 + Math.pow(index,1.35)*.45));\n}\n\nfunction finishParkour(completed){\n  if(!gameRunning||!parkourActive)return;\n  gameRunning=false;\n  const reward=calculateParkourReward(parkourBestIndex)+(completed?25:0);\n  unapark+=reward;\n  passPapel+=Math.floor(reward\/3);\n  totalMatches++;\n  saveProgress();\n  updateShopUI();\n  updatePassUI();\n  clearBullets();\n  clearBuildPieces();\n  clearSmokes();\n  clearBoomerang();\n  clearLootCrates();\n  clearBots();\n  clearDrones();\n  clearParkourCourse();\n  parkourActive=false;\n  winRewardText.textContent=(completed?'Parkuru bitirdin! ':'Parkur bitti. ')+'+'+reward+' Unapark kazand\u0131n.';\n  winScreen.style.display='flex';\n  hud.style.display='none';\n  hint.style.display='none';\n  crosshair.style.display='none';\n  mobileControls.style.display='none';\n  exitScope();\n  setBuildMode(false);\n  if(document.pointerLockElement)document.exitPointerLock();\n}\n\nfunction updateParkourMode(){\n  if(!parkourActive)return;\n  if(parkourGoal)parkourGoal.rotation.z+=.035;\n  if(player.position.y<-8){finishParkour(false);return;}\n  if(parkourBestIndex>=23){finishParkour(true);}\n}\n\nfunction startRound(){\n  selectedMode=modeSelect.value;\n  combatMode=combatSelect.value;\n  menuModeText.textContent=selectedMode;\n  parkourActive=(selectedMode==='parkour');\n\n  const bossKeys=Object.keys(bossTypes);\n  bossRoundActive=!parkourActive&&((totalMatches+1)%5===0);\n  currentBossType=bossRoundActive?bossKeys[Math.floor(Math.random()*bossKeys.length)]:null;\n\n  gameRunning=true;\n  pendingStartRound=false;\n  matchmakingLocked=false;\n  hideMatchmaking();\n  ensureAudio();\n  sfx('start',true);\n\n  menu.style.display='none';\n  gameOverScreen.style.display='none';\n  winScreen.style.display='none';\n  hud.style.display='flex';\n  hint.style.display='block';\n  crosshair.style.display='block';\n  shopPanel.style.display='none';\n  passPanel.style.display='none';\n  const chatBox=document.getElementById('chatBox');\n  if(chatBox)chatBox.style.display='flex';\n  \/\/ Chat input odaktan \u00e7\u0131ks\u0131n ki WASD \u00e7al\u0131\u015fs\u0131n\n  const chatInput=document.getElementById('chatInput');\n  if(chatInput)chatInput.blur();\n  if(document.activeElement&&document.activeElement.blur)document.activeElement.blur();\n\n  mobileControls.style.display=controlMode==='mobile'?'block':'none';\n\n  const character=parkourActive?getEffectiveCharacter():(characters[currentCharacter]||characters.rookie);\n  playerMaxHealth=(upgrades.extraHealth?110:100)+(character.hpBonus||0);\n  playerHealth=playerMaxHealth;\n  lastPlayerDamageTime=performance.now();\n\n  temporaryWeaponId=null;\n  const selectedWeapon=weapons[currentWeapon]||weapons.rifle;\n  maxClip=selectedWeapon.clip||0;\n  ammoInClip=selectedWeapon.clip||0;\n  ammoReserve=selectedWeapon.reserve||0;\n\n  stamina=100;\n  isReloading=false;\n  reloadDuration=(upgrades.fastReload?800:1500)*((characters[currentCharacter]||characters.rookie).reloadMul||1);\n\n  smokeBombCount=2;\n\n  yaw=0;\n  pitch=0;\n  playerVelocityY=0;\n  isGrounded=true;\n  jumpsLeft=character.doubleJump?2:1;\n  forwardHoldTime=0;\n  mouseHeld=false;\n  mobileRun=false;\n  moveJoy.x=0;moveJoy.y=0;lookJoy.x=0;lookJoy.y=0;\n\n  clearBullets();\n  clearBuildPieces();\n  clearSmokes();\n  clearBoomerang();\n  clearLootCrates();\n  clearDrones();\n  clearBots();\n  darkDronesSpawned=false;\n\n  positionUShip();\n\n  if(parkourActive){\n    createParkourCourse();\n    const startPlatform=parkourPlatforms.find(p=>p.userData&&(p.userData.isStart||p.userData.index===0)&&!p.userData.obstacle)\n      || parkourPlatforms.find(p=>p.userData&&!p.userData.obstacle);\n    if(startPlatform){\n      const box=new THREE.Box3().setFromObject(startPlatform);\n      \/\/ Kafa y\u00fcksekli\u011fi = y\u00fczey + 1.75 \u2014 platformun tam \u00fcst\u00fcnde ba\u015fla\n      player.position.set(startPlatform.position.x, box.max.y+1.75, startPlatform.position.z);\n    }else{\n      player.position.set(0,2.2,0);\n    }\n    playerVelocityY=0;\n    isGrounded=true;\n    jumpsLeft=2;\n    yaw=0;\n    pitch=0;\n    player.rotation.y=0;\n    camera.rotation.x=0;\n    parkourStartTime=performance.now();\n    parkourBestIndex=0;\n    parkourEarnedPreview=0;\n    showMessage('Parkur ba\u015flad\u0131! \u0130lerledik\u00e7e Unapark kazan.');\n  }else{\n    const safePlayerSpawn=getSafeSpawn(0,30,.9,8);\n    player.position.set(safePlayerSpawn.x,2,safePlayerSpawn.z);\n  }\n  player.rotation.y=yaw;\n  camera.rotation.x=pitch;\n  applyCameraMode();\n\n  if(!parkourActive&&!onlineMode)setupTeams();\n  if(!parkourActive&&combatMode==='unarmed'&&!bossRoundActive&&!onlineMode)spawnLootCrates();\n  window._stormStart=performance.now();\n  initStorm();\n  createPlayerGun();\n  if(parkourActive&&gunGroup)gunGroup.visible=false;\n  createThirdPersonModel();\n  applyCameraMode();\n  setBuildMode(false);\n\n  if(onlineMode){\n    \/\/ Online'da host \u00fcst tarafta, misafir alt tarafta ba\u015flar\n    if(isOnlineHost){\n      player.position.set(0,2,28);\n      yaw=0;\n    }else{\n      player.position.set(0,2,-28);\n      yaw=Math.PI;\n    }\n    player.rotation.y=yaw;\n    camera.rotation.x=0;\n    pitch=0;\n    bossRoundActive=false;\n    showMessage('ONLINE VS ba\u015flad\u0131! Oda: '+onlineRoomCode);\n  }else if(bossRoundActive){\n    const cfg=bossTypes[currentBossType]||bossTypes.giantRobot;\n    sfx('boss',true);\n    makeScreenPulse('rgba(255,138,0,.18)',220);\n    showMessage('5. ma\u00e7 BOSS MODU! '+cfg.icon+' '+cfg.name+' geldi.');\n  }else{\n    showMessage(selectedMode+' '+(combatMode==='armed'?'silahl\u0131':'silahs\u0131z')+' ba\u015flad\u0131! 0 ile kamera de\u011fi\u015fir.');\n  }\n  updateHUD();\n}\n\nfunction onMouseMove(e){\n  if(!isPointerLocked||controlMode==='mobile')return;\n\n  const sensitivity=isScoped?.0008:.002;\n\n  yaw-=e.movementX*sensitivity;\n  pitch-=e.movementY*sensitivity;\n  pitch=Math.max(-Math.PI\/2.2,Math.min(Math.PI\/2.2,pitch));\n\n  player.rotation.y=yaw;\n  camera.rotation.x=pitch;\n}\n\nfunction onMouseDown(e){\n  if(!gameRunning||!isPointerLocked)return;\n\n  if(e.button===0){\n    mouseHeld=true;\n\n    if(buildMode){\n      placeBuildPiece();\n      mouseHeld=false;\n      return;\n    }\n\n    playerShoot();\n  }\n\n  if(e.button===2){\n    if(combatMode==='unarmed')unarmedAttack();\n    else boomerangMelee();\n  }\n}\n\nfunction onMouseUp(e){\n  if(e.button===0)mouseHeld=false;\n}\n\nfunction handleKeyDown(e){\n  if(!gameRunning)return;\n\n  if(e.code==='Digit0'){\n    toggleThirdPerson();\n    return;\n  }\n\n  if(e.code==='KeyB')toggleBuildMode();\n  if(e.code==='Digit1')setBuildType('wall');\n\n  if(e.code==='Digit2'){\n    if(buildMode)setBuildType('floor');\n    else useItem();\n  }\n\n  if(e.code==='Digit3')setBuildType('ramp');\n  \/\/ G = Edit modu (kap\u0131\/pencere)\n  if(e.code==='KeyG'){\n    editMode=!editMode;\n    if(editMode){buildMode=false;if(buildGhost)buildGhost.visible=false;showMessage('EDIT: sol t\u0131k kap\u0131\/pencere \u2014 F stil de\u011fi\u015ftir');}\n    else showMessage('Edit kapal\u0131');\n  }\n  if(e.code==='KeyF'&&editMode){\n    editStyle=editStyle==='door'?'window':'door';\n    showMessage('Edit stil: '+(editStyle==='door'?'KAPI':'PENCERE'));\n  }\n  \/\/ M = malzeme de\u011fi\u015ftir (Ah\u015fap\/Ta\u015f\/Metal)\n  if(e.code==='KeyM'){\n    const order=['wood','stone','metal'];\n    const i=order.indexOf(buildMaterial);\n    buildMaterial=order[(i+1)%order.length];\n    const bm=buildMaterials[buildMaterial];\n    showMessage('Malzeme: '+(bm?bm.name:buildMaterial));\n  }\n  \/\/ V = balyoz\n  if(e.code==='KeyV'){\n    currentWeapon='pickaxe';\n    temporaryWeaponId=null;\n    createPlayerGun();\n    showMessage('Balyoz se\u00e7ildi \u2014 build k\u0131r!');\n  }\n\n  if(buildMode)return;\n\n  if(e.code==='KeyR'&&ammoInClip<maxClip&&!isReloading)startReload();\n  if(e.code==='KeyE')enterScope();\n\n  if(e.code==='Space'){\n    handleJumpOrTeleport();\n  }\n}\n\nfunction handleKeyUp(e){\n  if(e.code==='KeyE')exitScope();\n}\n\nfunction handleJumpOrTeleport(){\n  if(currentWeapon==='boomerang'&&boomerangActive&&boomerang){\n    teleportToBoomerang();\n    return;\n  }\n\n  const character=characters[currentCharacter]||characters.rookie;\n  if(isGrounded||((character.doubleJump)&&jumpsLeft>0)){\n    playerVelocityY=jumpStrength;\n    sfx('jump');\n    isGrounded=false;\n    jumpsLeft=Math.max(0,jumpsLeft-1);\n    if(character.doubleJump&&!isGrounded)showMessage('Jet \u00e7ift z\u0131plama!');\n  }\n}\n\nfunction enterScope(){\n  if(!gameRunning||buildMode)return;\n\n  const weapon=weapons[currentWeapon]||weapons.rifle;\n\n  if(currentWeapon==='boomerang'){\n    showMessage('Bumerangda ni\u015fan yok!');\n    return;\n  }\n\n  if(weapon.scope){\n    isScoped=true;\n    isBarrelAim=false;\n    camera.fov=scopedFov;\n    camera.updateProjectionMatrix();\n    scopeOverlay.style.display='block';\n    crosshair.style.display='none';\n\n    if(gunGroup)gunGroup.visible=false;\n    return;\n  }\n\n  if(weapon.barrelAim){\n    isBarrelAim=true;\n    showMessage('Namlu pozisyonu a\u00e7\u0131k');\n    return;\n  }\n\n  showMessage('Bu silahta E modu yok');\n}\n\nfunction exitScope(){\n  isScoped=false;\n  isBarrelAim=false;\n\n  if(camera){\n    camera.fov=normalFov;\n    camera.updateProjectionMatrix();\n  }\n\n  scopeOverlay.style.display='none';\n\n  if(isPointerLocked&&gameRunning&&!buildMode)crosshair.style.display='block';\n  if(gunGroup)gunGroup.visible=true;\n}\n\nfunction startReload(){\n  const activeWeaponId=temporaryWeaponId||currentWeapon;\n  const weapon=weapons[activeWeaponId]||weapons.rifle;\n\n  if(weapon.type==='boomerang'){\n    showMessage('Bumerangda \u015farj\u00f6r yok!');\n    return;\n  }\n\n  if(ammoReserve<=0){\n    showMessage('Yedek cephane yok!');\n    return;\n  }\n\n  isReloading=true;\n  reloadAnimStart=performance.now();\n\n  showMessage('Mermi dolduruluyor...');\n\n  setTimeout(()=>{\n    if(!gameRunning)return;\n\n    const needed=maxClip-ammoInClip;\n    const loaded=Math.min(needed,ammoReserve);\n\n    ammoInClip+=loaded;\n    ammoReserve-=loaded;\n    isReloading=false;\n\n    setGunPose();\n    sfx('reloadDone');\n    showMessage('\u015earj\u00f6r doldu!');\n    updateHUD();\n  },reloadDuration);\n}\n\nfunction swingPickaxe(weapon){\n  const now=performance.now();\n  if(now-lastPlayerShotTime<(weapon.cooldown||400))return;\n  lastPlayerShotTime=now;\n  sfx('melee');\n  meleeAnimUntil=now+300;\n  \/\/ \u00d6nce build\n  const dir=new THREE.Vector3(0,0,-1).applyQuaternion(camera.getWorldQuaternion(new THREE.Quaternion()));\n  const origin=camera.getWorldPosition(new THREE.Vector3());\n  if(raycaster){\n    raycaster.set(origin,dir);\n    const hits=raycaster.intersectObjects(buildPieces,false);\n    if(hits.length&&hits[0].distance<4.5){\n      const piece=hits[0].object;\n      damageBuildPiece(piece,weapon.buildDamage||55);\n      showMessage('Build hasar!');\n      return;\n    }\n  }\n  \/\/ Bot\n  const target=findClosestEnemyToPlayer(3.5);\n  if(target){\n    damageBot(target,weapon.damage||22,'Balyoz isabet!');\n  }else showMessage('Balyoz bo\u015fa!');\n}\n\nfunction tryEditBuild(){\n  if(!raycaster||!camera)return false;\n  const dir=new THREE.Vector3(0,0,-1).applyQuaternion(camera.getWorldQuaternion(new THREE.Quaternion()));\n  const origin=camera.getWorldPosition(new THREE.Vector3());\n  raycaster.set(origin,dir);\n  const hits=raycaster.intersectObjects(buildPieces,true);\n  if(!hits.length||hits[0].distance>8){showMessage('Edit i\u00e7in duvara bak');return true;}\n  let piece=hits[0].object;\n  while(piece&&piece.parent&&!piece.userData.isBuildPiece)piece=piece.parent;\n  if(!piece||!piece.userData||!piece.userData.isBuildPiece){showMessage('Sadece kendi build');return true;}\n  if(piece.userData.type!=='wall'){showMessage('Sadece duvar editlenir');return true;}\n  applyWallEdit(piece,editStyle);\n  return true;\n}\n\nfunction applyWallEdit(piece,style){\n  if(piece.userData.edited){showMessage('Bu duvar zaten editli');return;}\n  piece.userData.edited=style;\n  \/\/ G\u00f6rsel delik: yar\u0131 saydam kap\u0131\/pencere paneli\n  const holeH=style==='door'?2.2:0.9;\n  const holeY=style==='door'?1.1:1.6;\n  const holeW=style==='door'?1.1:1.4;\n  const hole=new THREE.Mesh(\n    new THREE.BoxGeometry(holeW,holeH,0.15),\n    new THREE.MeshBasicMaterial({color:0x0f172a,transparent:true,opacity:0.15,depthWrite:false})\n  );\n  hole.position.set(0,holeY,0);\n  hole.userData.isEditHole=true;\n  piece.add(hole);\n  \/\/ \u00c7er\u00e7eve kenarlar\u0131 (g\u00f6rsel)\n  const frameMat=new THREE.MeshStandardMaterial({color:0x1e293b,roughness:0.5});\n  const top=new THREE.Mesh(new THREE.BoxGeometry(holeW+0.15,0.12,0.2),frameMat);\n  top.position.set(0,holeY+holeH\/2,0.05);\n  const left=new THREE.Mesh(new THREE.BoxGeometry(0.12,holeH,0.2),frameMat);\n  left.position.set(-holeW\/2,holeY,0.05);\n  const right=new THREE.Mesh(new THREE.BoxGeometry(0.12,holeH,0.2),frameMat);\n  right.position.set(holeW\/2,holeY,0.05);\n  piece.add(top,left,right);\n  piece.userData.editHole={w:holeW,h:holeH,y:holeY,style:style};\n  \/\/ Can biraz d\u00fc\u015fs\u00fcn\n  piece.userData.hp=Math.max(10,(piece.userData.hp||40)*0.7);\n  sfx('build');\n  showMessage(style==='door'?'Kap\u0131 a\u00e7\u0131ld\u0131!':'Pencere a\u00e7\u0131ld\u0131!');\n}\n\nfunction playerShoot(){\n  if(editMode){\n    tryEditBuild();\n    return;\n  }\n  if(combatMode==='unarmed'&&!temporaryWeaponId){\n    unarmedAttack();\n    return;\n  }\n\n  const weaponId=temporaryWeaponId||currentWeapon;\n  const weapon=weapons[weaponId]||weapons.rifle;\n\n  if(weapon.type==='boomerang'){\n    throwBoomerang();\n    return;\n  }\n  if(weapon.type==='melee'||weaponId==='pickaxe'){\n    swingPickaxe(weapon);\n    return;\n  }\n\n  const now=performance.now();\n\n  if(now-lastPlayerShotTime<weapon.cooldown)return;\n\n  if(isReloading){\n    sfx('reload');\n  showMessage('\u015earj\u00f6r doluyor...');\n    return;\n  }\n\n  if(ammoInClip<=0){\n    showMessage('Cephane bitti! R ile doldur.');\n    return;\n  }\n\n  lastPlayerShotTime=now;\n  sfx('shot');\n  screenShake(weapon.type==='beam'?.25:.45,80);\n\n  if(weapon.burst){\n    shootBurst(weapon);\n  }else{\n    fireWeaponBullet(weapon);\n  }\n\n  ammoInClip--;\n\n  makeMuzzleFlash();\n  updateHUD();\n}\n\nfunction shootBurst(weapon){\n  let count=0;\n  const interval=setInterval(()=>{\n    if(!gameRunning||count>=weapon.burst){\n      clearInterval(interval);\n      return;\n    }\n    fireWeaponBullet(weapon);\n    count++;\n  },70);\n}\n\nfunction fireWeaponBullet(weapon){\n  const weaponId=temporaryWeaponId||currentWeapon;\n  const pellets=weapon.pellets||1;\n\n  for(let i=0;i<pellets;i++){\n    const dir=new THREE.Vector3(0,0,-1);\n    camera.getWorldDirection(dir);\n\n    if(pellets>1){\n      dir.x+=(Math.random()-.5)*.13;\n      dir.y+=(Math.random()-.5)*.08;\n      dir.z+=(Math.random()-.5)*.13;\n      dir.normalize();\n    }\n\n    const start=new THREE.Vector3();\n    camera.getWorldPosition(start);\n    start.add(dir.clone().multiplyScalar(.85));\n\n    const bulletColor=weapon.color||0xfacc15;\n\n    \/\/ \u0130nce k\u0131sa \u00e7ubuk mermi (Fortnite tarz\u0131)\n    const stickLen=weaponId==='sniper'||weaponId==='railgun'?0.55:weaponId==='rocket'?0.4:0.32;\n    const stickR=weaponId==='rocket'?0.06:0.028;\n    const bullet=new THREE.Mesh(\n      new THREE.CylinderGeometry(stickR,stickR,stickLen,6),\n      new THREE.MeshBasicMaterial({color:bulletColor})\n    );\n    bullet.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0),dir.clone().normalize());\n    bullet.position.copy(start);\n    bullet.userData.isProjectile=true;\n    bullet.userData.bulletUid=++bulletUid;\n    bullet.userData.owner='player';\n    scene.add(bullet);\n\n    \/\/ Fortnite h\u0131z\u0131\n    const bulletSpeed=weaponId==='laser'?5.8:weaponId==='plasma'?5.2:weaponId==='minigun'?4.8:weaponId==='flamethrower'?2.4:weaponId==='sniper'||weaponId==='railgun'?7.5:weaponId==='rocket'?3.6:5.5;\n    const rangeMul=weapon.shortRange?0.5:1;\n    bullets.push({\n      mesh:bullet,\n      bulletUid:bullet.userData.bulletUid,\n      velocity:dir.clone().multiplyScalar(bulletSpeed*(weaponId==='rocket'?0.85:1)),\n      speed:bulletSpeed,\n      life:weaponId==='minigun'?40:weaponId==='flamethrower'?28:weaponId==='rocket'?55:50,\n      age:0,\n      start:start.clone(),\n      maxRange:(weaponId==='sniper'||weaponId==='railgun'?120:weaponId==='laser'?90:weaponId==='minigun'?55:weaponId==='flamethrower'?12:weaponId==='rocket'?55:80)*rangeMul,\n      damage:weapon.damage,\n      team:'player',\n      slow:weapon.slow||false,\n      explosive:!!weapon.explosive,\n      splash:weapon.splash||0,\n      owner:'player',\n      dir:dir.clone()\n    });\n  }\n}\n\nfunction throwBoomerang(){\n  const now=performance.now();\n\n  if(now-lastPlayerShotTime<450)return;\n\n  if(boomerangActive){\n    showMessage('Bumerang h\u00e2l\u00e2 havada!');\n    return;\n  }\n\n  lastPlayerShotTime=now;\n  throwAnimUntil=performance.now()+320;\n\n  const start=new THREE.Vector3();\n  camera.getWorldPosition(start);\n\n  const dir=new THREE.Vector3(0,0,-1);\n  camera.getWorldDirection(dir);\n  dir.y=0;\n\n  if(dir.lengthSq()<.01){\n    dir.set(Math.sin(yaw),0,Math.cos(yaw));\n  }\n\n  dir.normalize();\n\n  boomerang=createBoomerangModel(0xf97316);\n  boomerang.scale.set(.55,.55,.55);\n\n  boomerang.position.copy(start);\n  boomerang.position.add(dir.clone().multiplyScalar(1.4));\n  boomerang.position.y-=.25;\n\n  scene.add(boomerang);\n\n  boomerangActive=true;\n  boomerangReturning=false;\n  boomerangDir.copy(dir);\n  boomerangStart.copy(boomerang.position);\n  boomerangHitOut=false;\n  boomerangHitBack=false;\n\n  if(gunGroup)gunGroup.visible=false;\n\n  sfx('boomerangThrow');\n  screenShake(.45,90);\n  showMessage('Bumerang f\u0131rlad\u0131! Space ile \u0131\u015f\u0131nlanabilirsin.');\n}\n\nfunction updateBoomerang(){\n  if(!boomerangActive||!boomerang)return;\n\n  boomerang.rotation.y+=.38;\n  boomerang.rotation.z+=.28;\n\n  if(!boomerangReturning){\n    const old=boomerang.position.clone();\n    boomerang.position.add(boomerangDir.clone().multiplyScalar(.48));\n\n    const traveled=boomerang.position.distanceTo(boomerangStart);\n\n    if(traveled>14||bulletHitsWallSegment(old,boomerang.position)){\n      boomerangReturning=true;\n    }\n\n    if(!boomerangHitOut){\n      const hit=findClosestEnemyToPoint(boomerang.position,1.55);\n      if(hit){\n        damageBot(hit,18,'Bumerang giderken vurdu!');\n        boomerangHitOut=true;\n      }\n    }\n  }else{\n    const target=player.position.clone();\n    target.y=1.4;\n\n    const toPlayer=target.sub(boomerang.position);\n    const dist=toPlayer.length();\n\n    if(dist<1.1){\n      catchBoomerang();\n      return;\n    }\n\n    toPlayer.normalize();\n    boomerang.position.add(toPlayer.multiplyScalar(.55));\n\n    if(!boomerangHitBack){\n      const hit=findClosestEnemyToPoint(boomerang.position,1.55);\n      if(hit){\n        damageBot(hit,18,'Bumerang d\u00f6nerken vurdu!');\n        boomerangHitBack=true;\n      }\n    }\n  }\n}\n\nfunction catchBoomerang(){\n  if(boomerang){\n    scene.remove(boomerang);\n    boomerang=null;\n  }\n\n  boomerangActive=false;\n  boomerangReturning=false;\n  boomerangHitOut=false;\n  boomerangHitBack=false;\n\n  if(gunGroup&&!buildMode)gunGroup.visible=true;\n  sfx('boomerangCatch');\n}\n\nfunction clearBoomerang(){\n  if(boomerang){\n    scene.remove(boomerang);\n    boomerang=null;\n  }\n\n  boomerangActive=false;\n  boomerangReturning=false;\n  boomerangHitOut=false;\n  boomerangHitBack=false;\n\n  if(gunGroup&&!buildMode)gunGroup.visible=true;\n}\n\nfunction teleportToBoomerang(){\n  if(!boomerang)return;\n\n  player.position.x=boomerang.position.x;\n  player.position.z=boomerang.position.z;\n  player.position.y=Math.max(2,boomerang.position.y+1.5);\n\n  playerVelocityY=0;\n  isGrounded=false;\n\n  catchBoomerang();\n  sfx('teleport',true);\n  makeScreenPulse('rgba(56,189,248,.18)',170);\n  screenShake(1.2,150);\n  showMessage('Bumeranga \u0131\u015f\u0131nland\u0131n!');\n}\n\nfunction boomerangMelee(){\n  if(currentWeapon!=='boomerang'){\n    showMessage('Yak\u0131n sald\u0131r\u0131 sadece bumerangda!');\n    return;\n  }\n\n  const now=performance.now();\n\n  if(now-lastMeleeTime<meleeCooldown)return;\n\n  lastMeleeTime=now;\n  meleeAnimUntil=performance.now()+350;\n\n  const target=findClosestEnemyToPlayer(3.2);\n\n  if(!target){\n    showMessage('Yak\u0131n sald\u0131r\u0131 bo\u015fa gitti!');\n    return;\n  }\n\n  const forward=new THREE.Vector3(Math.sin(yaw),0,Math.cos(yaw)).normalize();\n  const toEnemy=target.position.clone().sub(player.position);\n  toEnemy.y=0;\n  toEnemy.normalize();\n\n  if(forward.dot(toEnemy)<.2){\n    showMessage('Adama d\u00f6n\u00fck de\u011filsin!');\n    return;\n  }\n\n  damageBot(target,meleeDamage,'Yak\u0131n sald\u0131r\u0131 isabet!');\n}\n\nfunction findClosestEnemyToPlayer(range){\n  let best=null;\n  let bestDist=Infinity;\n\n  for(const e of enemies){\n    if(e.userData.health<=0)continue;\n\n    const d=e.position.distanceTo(player.position);\n\n    if(d<range&&d<bestDist){\n      best=e;\n      bestDist=d;\n    }\n  }\n\n  \/\/ Online rakipler\n  if(onlineMode){\n    for(const id in remotePlayers){\n      const rp=remotePlayers[id];\n      if(!rp||!rp.mesh||!rp.mesh.visible||rp.health<=0)continue;\n      const d=rp.mesh.position.distanceTo(player.position);\n      if(d<range&&d<bestDist){\n        best={position:rp.mesh.position,userData:{health:rp.health,isRemote:true,remoteId:id}};\n        bestDist=d;\n      }\n    }\n  }\n\n  return best;\n}\n\nfunction findClosestEnemyToPoint(point,range){\n  let best=null;\n  let bestDist=Infinity;\n\n  for(const e of enemies){\n    if(e.userData.health<=0)continue;\n\n    const box=new THREE.Box3().setFromObject(e);\n    box.expandByScalar(e.userData.isBoss?.55:.18);\n    const closest=box.clampPoint(point.clone(),new THREE.Vector3());\n    const d=closest.distanceTo(point);\n\n    if(d<range&&d<bestDist){\n      best=e;\n      bestDist=d;\n    }\n  }\n\n  return best;\n}\n\nfunction damageBot(bot,amount,msg){\n  \/\/ Online rakip\n  if(bot.userData&&bot.userData.isRemote&&bot.userData.remoteId){\n    damageRemotePlayer(bot.userData.remoteId,amount);\n    if(msg)showMessage(msg);\n    sfx('hit');\n    return;\n  }\n\n  if(bot.userData&&bot.userData.shield){\n    amount*=Math.max(0,1-bot.userData.shield);\n  }\n  bot.userData.health-=amount;\n\n  const hitPos=bot.position.clone();\n  hitPos.y+=bot.userData&&bot.userData.isBoss?1.6:1.2;\n  spawnSoundParticles(hitPos,bot.userData&&bot.userData.isBoss?0xff8a00:0xffffff,bot.userData&&bot.userData.isBoss?18:8,bot.userData&&bot.userData.isBoss?.13:.08);\n  sfx(bot.userData&&bot.userData.isBoss?'bossHit':'hit');\n  screenShake(bot.userData&&bot.userData.isBoss?.75:.35,90);\n\n  if(bot.userData&&bot.userData.isBoss){\n    bot.traverse(obj=>{\n      if(obj.isMesh&&obj.material&&obj.material.color){\n        const oldColor=obj.material.color.clone();\n        obj.material.color.set(0xffffff);\n        setTimeout(()=>{\n          if(obj.material&&obj.material.color)obj.material.color.copy(oldColor);\n        },70);\n      }\n    });\n  }\n\n  if(msg)showMessage(msg);\n\n  if(bot.userData.health<=0){\n    sfx('death');\n    bot.userData.health=0;\n    scene.remove(bot);\n    enemies=enemies.filter(e=>e!==bot);\n    allies=allies.filter(a=>a!==bot);\n    bots=bots.filter(b=>b!==bot);\n  }\n\n  checkWinLose();\n}\n\nfunction makeMuzzleFlash(){\n  if(!gunGroup||currentWeapon==='sniper'||currentWeapon==='boomerang')return;\n\n  const color=weapons[currentWeapon].color||0xfff3a3;\n\n  const flash=new THREE.Mesh(\n    new THREE.SphereGeometry(.055,10,10),\n    new THREE.MeshBasicMaterial({color,transparent:true,opacity:.95})\n  );\n\n  flash.position.set(0,.01,-.72);\n  gunGroup.add(flash);\n\n  setTimeout(()=>{\n    if(gunGroup)gunGroup.remove(flash);\n  },55);\n}\n\nfunction useItem(){\n  if(!gameRunning)return;\n\n  if(smokeBombCount<=0){\n    showMessage('Sis bombas\u0131 kalmad\u0131!');\n    return;\n  }\n\n  smokeBombCount--;\n\n  const dir=new THREE.Vector3(0,0,-1);\n  camera.getWorldDirection(dir);\n  dir.y=0;\n\n  if(dir.lengthSq()<.01)dir.set(0,0,-1);\n  dir.normalize();\n\n  const pos=player.position.clone().add(dir.multiplyScalar(5));\n  pos.y=1.2;\n\n  createSmoke(pos);\n  sfx('build');\n  makeScreenPulse('rgba(156,163,175,.12)',160);\n  showMessage('Sis bombas\u0131 at\u0131ld\u0131! Kalan: '+smokeBombCount);\n}\n\nfunction createSmoke(pos){\n  const smokeGroup=new THREE.Group();\n\n  for(let i=0;i<12;i++){\n    const puff=new THREE.Mesh(\n      new THREE.SphereGeometry(1.2+Math.random()*1.4,16,16),\n      new THREE.MeshBasicMaterial({\n        color:0x9ca3af,\n        transparent:true,\n        opacity:.30,\n        depthWrite:false\n      })\n    );\n\n    puff.position.set((Math.random()-.5)*4,(Math.random()-.5)*2,(Math.random()-.5)*4);\n    smokeGroup.add(puff);\n  }\n\n  smokeGroup.position.copy(pos);\n  scene.add(smokeGroup);\n\n  const smoke={\n    mesh:smokeGroup,\n    position:pos.clone(),\n    radius:5.4,\n    duration:8000\n  };\n\n  smokes.push(smoke);\n\n  setTimeout(()=>{\n    scene.remove(smokeGroup);\n    smokes=smokes.filter(s=>s!==smoke);\n  },smoke.duration);\n}\n\n\nfunction createLootCrate(x,z,weaponId){\n  const group=new THREE.Group();\n  const crateMat=new THREE.MeshStandardMaterial({color:0x8b5a2b,roughness:.65,metalness:.05});\n  const edgeMat=new THREE.MeshStandardMaterial({color:0xfacc15,roughness:.35,metalness:.2});\n  const box=new THREE.Mesh(new THREE.BoxGeometry(1.25,1.0,1.25),crateMat);\n  box.position.y=.5;\n  const band1=new THREE.Mesh(new THREE.BoxGeometry(1.38,.18,1.38),edgeMat);\n  band1.position.y=.94;\n  const band2=new THREE.Mesh(new THREE.BoxGeometry(1.38,.16,1.38),edgeMat);\n  band2.position.y=.18;\n  const glow=new THREE.Mesh(new THREE.SphereGeometry(.16,12,12),new THREE.MeshBasicMaterial({color:(weapons[weaponId]||weapons.rifle).color||0xfacc15}));\n  glow.position.y=1.18;\n  group.add(box,band1,band2,glow);\n  group.position.set(x,0,z);\n  group.userData={weaponId,opened:false,spin:Math.random()*6.28};\n  scene.add(group);\n  lootCrates.push(group);\n}\n\nfunction spawnLootCrates(){\n  clearLootCrates();\n  const crateCount=selectedMode==='3v3'?8:selectedMode==='2v2'?6:5;\n  const preferred=[[-22,-4],[22,-4],[-22,8],[22,8],[0,-14],[-8,24],[8,24],[0,8]];\n  for(let i=0;i<crateCount;i++){\n    let pos=null;\n    if(preferred[i]){\n      const p=preferred[i];\n      pos=getSafeSpawn(p[0],p[1],1.1);\n    }else{\n      pos=getSafeSpawn(rand(-28,28),rand(-22,24),1.1);\n    }\n    const weaponId=lootWeaponPool[Math.floor(Math.random()*lootWeaponPool.length)];\n    createLootCrate(pos.x,pos.z,weaponId);\n  }\n}\n\nfunction updateLootCrates(){\n  if(combatMode!=='unarmed')return;\n  for(let i=lootCrates.length-1;i>=0;i--){\n    const crate=lootCrates[i];\n    crate.userData.spin+=.025;\n    crate.rotation.y=crate.userData.spin;\n    crate.position.y=Math.sin(performance.now()*.003+crate.userData.spin)*.08;\n\n    \/\/ Sand\u0131k yerde, oyuncu kameras\u0131\/karakteri ise y\u00fcksekte oldu\u011fu i\u00e7in\n    \/\/ normal 3D distance bazen 2 metreye yak\u0131n \u00e7\u0131k\u0131yordu ve sand\u0131k a\u00e7\u0131lm\u0131yordu.\n    \/\/ Bu y\u00fczden sadece yatay X-Z mesafesine bak\u0131yoruz.\n    const dx=crate.position.x-player.position.x;\n    const dz=crate.position.z-player.position.z;\n    const horizontalDistance=Math.hypot(dx,dz);\n\n    if(horizontalDistance<2.15){\n      openLootCrate(crate);\n      lootCrates.splice(i,1);\n      continue;\n    }\n\n    const botOpener=findBotNearCrate(crate,2.05);\n    if(botOpener){\n      openLootCrateForBot(crate,botOpener);\n      lootCrates.splice(i,1);\n    }\n  }\n}\n\nfunction openLootCrate(crate){\n  if(!crate||crate.userData.opened)return;\n  crate.userData.opened=true;\n  const weaponId=crate.userData.weaponId||'rifle';\n  const weapon=weapons[weaponId]||weapons.rifle;\n  temporaryWeaponId=weaponId;\n  maxClip=weapon.clip||0;\n  ammoInClip=weapon.clip||0;\n  ammoReserve=weapon.reserve||0;\n  isReloading=false;\n  scene.remove(crate);\n  createPlayerGun();\n  sfx('crate');\n  makeScreenPulse('rgba(250,204,21,.12)',130);\n  spawnSoundParticles(crate.position.clone().add(new THREE.Vector3(0,1,0)),weapon.color||0xfacc15,14,.11);\n  showMessage('Sand\u0131ktan '+weapon.name+' \u00e7\u0131kt\u0131!');\n}\n\nfunction findBotNearCrate(crate,range){\n  for(const bot of bots){\n    if(!bot||!bot.position||bot.userData.health<=0)continue;\n    const dx=crate.position.x-bot.position.x;\n    const dz=crate.position.z-bot.position.z;\n    if(Math.hypot(dx,dz)<range)return bot;\n  }\n  return null;\n}\n\nfunction openLootCrateForBot(crate,bot){\n  if(!crate||crate.userData.opened)return;\n  crate.userData.opened=true;\n  const weaponId=crate.userData.weaponId||'rifle';\n  const weapon=weapons[weaponId]||weapons.rifle;\n  bot.userData.lootWeaponId=weaponId;\n  bot.userData.lootWeaponName=weapon.name;\n  bot.userData.lootDamage=Math.max(5,(weapon.damage||10)*.75);\n  bot.userData.lootCooldown=Math.max(260,weapon.cooldown||650);\n  scene.remove(crate);\n  sfx('crate');\n  spawnSoundParticles(crate.position.clone().add(new THREE.Vector3(0,1,0)),weapon.color||0xfacc15,10,.09);\n  showMessage((bot.userData.team==='ally'?'Dost bot':'Bot')+' sand\u0131ktan '+weapon.name+' ald\u0131!');\n}\n\nfunction clearLootCrates(){\n  for(const crate of lootCrates)scene.remove(crate);\n  lootCrates=[];\n  temporaryWeaponId=null;\n}\n\nfunction clearSmokes(){\n  for(const smoke of smokes)scene.remove(smoke.mesh);\n  smokes=[];\n}\n\nfunction animate(){\n  requestAnimationFrame(animate);\n\n  if(gameRunning){\n    if(controlMode==='mobile')updateMobileLook();\n\n    const weapon=weapons[currentWeapon]||weapons.rifle;\n\n    if(mouseHeld&&weapon.auto&&!buildMode&&isPointerLocked){\n      playerShoot();\n    }\n\n    setGunPose();\n    updatePlayer();\n    updateParkourMode();\n    updateDarkVoyagerDrones();\n    updateDrones();\n    if(!onlineMode)updateBots();\n    updateBullets();\n    cleanupLingeringBullets();\n    const _dt=Math.min(0.05,((performance.now()-(animate._last||performance.now()))\/1000)||0.016);\n    animate._last=performance.now();\n    updateStorm(_dt);\n    cleanupOldBuilds();\n    updateUShip();\n    updateThirdPersonAnimation();\n    updateBoomerang();\n    updateLootCrates();\n    updateBuildGhost();\n    applyCameraMode();\n    updateHUD();\n    if(onlineMode)syncMyState();\n    updateBotChat();\n  }\n\n  if(!gameRunning){\n    updateUShip();\n    updateSoundParticles();\n    updateScreenShake();\n  }\n  renderer.render(scene,camera);\n}\n\nfunction updateMobileLook(){\n  if(Math.abs(lookJoy.x)>.03||Math.abs(lookJoy.y)>.03){\n    yaw-=lookJoy.x*.035;\n    pitch-=lookJoy.y*.025;\n    pitch=Math.max(-Math.PI\/2.2,Math.min(Math.PI\/2.2,pitch));\n    player.rotation.y=yaw;\n    camera.rotation.x=pitch;\n  }\n}\n\nfunction updatePlayer(){\n  const now=performance.now();\n\n  const character=getEffectiveCharacter();\n\n  if(playerHealth<playerMaxHealth&&now-lastPlayerDamageTime>healDelay){\n    playerHealth=Math.min(playerMaxHealth,playerHealth+healSpeed*(character.regenMul||1));\n  }\n\n  let inputX=0,inputZ=0;\n\n  if(controlMode==='mobile'){\n    inputX=moveJoy.x;\n    inputZ=moveJoy.y;\n  }else{\n    if(keys.KeyW)inputZ-=1;\n    if(keys.KeyS)inputZ+=1;\n    if(keys.KeyA)inputX-=1;\n    if(keys.KeyD)inputX+=1;\n  }\n\n  if(keys.KeyW||(controlMode==='mobile'&&inputZ<-0.35))forwardHoldTime+=16;\n  else forwardHoldTime=0;\n\n  const autoRun=(keys.KeyW&&forwardHoldTime>=autoRunDelay&&stamina>2)||(mobileRun&&stamina>2);\n  const speed=(autoRun?moveSpeed*runSpeedMultiplier:moveSpeed)*(character.speedMul||1);\n\n  if(autoRun)stamina=Math.max(0,stamina-.45);\n  else stamina=Math.min(100,stamina+.25);\n\n  if(stamina<=1){\n    forwardHoldTime=0;\n    mobileRun=false;\n  }\n\n  const len=Math.hypot(inputX,inputZ);\n\n  if(len>1){\n    inputX\/=len;\n    inputZ\/=len;\n  }\n\n  const forward=new THREE.Vector3(Math.sin(yaw),0,Math.cos(yaw));\n  const right=new THREE.Vector3(Math.cos(yaw),0,-Math.sin(yaw));\n\n  const move=new THREE.Vector3();\n  move.addScaledVector(right,inputX*speed);\n  move.addScaledVector(forward,inputZ*speed);\n\n  moveWithCollision(player,move,.65);\n  lastMoveAmount=Math.hypot(inputX,inputZ);\n  lastRunState=autoRun;\n\n  playerVelocityY+=gravity;\n  player.position.y+=playerVelocityY;\n\n  if(parkourActive){\n    handleParkourPlatformStand();\n  }else if(player.position.y<=2){\n    player.position.y=2;\n    playerVelocityY=0;\n    isGrounded=true;\n    jumpsLeft=(getEffectiveCharacter().doubleJump)?2:1;\n  }\n\n  handleRampClimb(player);\n  handleFloorStand(player,true);\n  handleWallTopStand(player,true);\n\n  player.position.x=Math.max(-32,Math.min(32,player.position.x));\n  player.position.z=parkourActive?Math.max(-8,Math.min(160,player.position.z)):Math.max(-32,Math.min(32,player.position.z));\n}\n\n\nfunction updateDarkVoyagerDrones(){\n  \/\/ Karanl\u0131k Gezgin se\u00e7iliyken can yar\u0131dan a\u015fa\u011f\u0131 d\u00fc\u015ferse 1 kez 3 dron \u00e7a\u011f\u0131r\u0131r.\n  if(currentCharacter!=='shadow')return;\n  if(darkDronesSpawned)return;\n  if(playerHealth>playerMaxHealth\/2)return;\n\n  darkDronesSpawned=true;\n  spawnDarkVoyagerDrones();\n}\n\nfunction spawnDarkVoyagerDrones(){\n  clearDrones();\n\n  for(let i=0;i<3;i++){\n    const angle=(Math.PI*2\/3)*i;\n    const drone=createDarkDrone(i,angle);\n    drone.position.set(\n      player.position.x+Math.cos(angle)*2.4,\n      player.position.y+1.1,\n      player.position.z+Math.sin(angle)*2.4\n    );\n    scene.add(drone);\n    drones.push(drone);\n  }\n\n  sfx('droneSpawn',true);\n  makeScreenPulse('rgba(249,115,22,.14)',180);\n  showMessage('Karanl\u0131k Gezgin 3 dron \u00e7a\u011f\u0131rd\u0131!');\n}\n\nfunction createDarkDrone(index,angle){\n  const drone=new THREE.Group();\n\n  const body=new THREE.Mesh(\n    new THREE.SphereGeometry(.28,18,18),\n    new THREE.MeshStandardMaterial({color:0x111827,metalness:.75,roughness:.25})\n  );\n\n  const eye=new THREE.Mesh(\n    new THREE.BoxGeometry(.32,.08,.035),\n    new THREE.MeshBasicMaterial({color:0xf97316})\n  );\n  eye.position.set(0,.05,-.27);\n\n  const wingMat=new THREE.MeshStandardMaterial({color:0x0f172a,metalness:.55,roughness:.35});\n  const wingL=new THREE.Mesh(new THREE.BoxGeometry(.42,.06,.16),wingMat);\n  wingL.position.set(-.34,0,0);\n  const wingR=new THREE.Mesh(new THREE.BoxGeometry(.42,.06,.16),wingMat);\n  wingR.position.set(.34,0,0);\n\n  const beam=new THREE.Mesh(\n    new THREE.CylinderGeometry(.025,.025,1,8),\n    new THREE.MeshBasicMaterial({color:0xf97316,transparent:true,opacity:.78})\n  );\n  beam.visible=false;\n  drone.add(body,eye,wingL,wingR,beam);\n\n  drone.userData={\n    team:'ally',\n    health:Math.max(1,playerMaxHealth\/2),\n    maxHealth:Math.max(1,playerMaxHealth\/2),\n    index,\n    angle,\n    beam,\n    damagePerFrame:.035,\n    range:16,\n    fovCos:Math.cos(Math.PI\/12),\n    spin:Math.random()*Math.PI*2\n  };\n\n  return drone;\n}\n\nfunction updateDrones(){\n  for(let i=drones.length-1;i>=0;i--){\n    const drone=drones[i];\n\n    if(drone.userData.health<=0){\n      scene.remove(drone);\n      drones.splice(i,1);\n      continue;\n    }\n\n    const offsets=[new THREE.Vector3(-1.4,1.25,1.9),new THREE.Vector3(0,1.55,2.25),new THREE.Vector3(1.4,1.25,1.9)];\n    const baseOffset=offsets[drone.userData.index%offsets.length].clone();\n    const playerYaw=player.rotation.y;\n    baseOffset.applyAxisAngle(new THREE.Vector3(0,1,0),playerYaw);\n\n    const desired=new THREE.Vector3(\n      player.position.x+baseOffset.x,\n      player.position.y+baseOffset.y,\n      player.position.z+baseOffset.z\n    );\n\n    drone.position.lerp(desired,.12);\n    drone.userData.spin+=.12;\n\n    const forward=new THREE.Vector3(Math.sin(playerYaw),0,Math.cos(playerYaw)).normalize();\n    const lookPoint=drone.position.clone().add(forward);\n    drone.lookAt(lookPoint.x,drone.position.y,lookPoint.z);\n\n    const target=findDroneTarget(drone,forward);\n    updateDroneBeam(drone,target);\n\n    if(target){\n      damageBot(target,drone.userData.damagePerFrame,'');\n    }\n  }\n}\n\nfunction findDroneTarget(drone,forward){\n  let best=null;\n  let bestDistance=Infinity;\n\n  for(const enemy of enemies){\n    if(enemy.userData.health<=0)continue;\n\n    const targetPoint=enemy.position.clone();\n    targetPoint.y=1.25;\n    const distance=drone.position.distanceTo(targetPoint);\n    const toTarget=targetPoint.clone().sub(drone.position);\n    const flatToTarget=toTarget.clone();\n    flatToTarget.y=0;\n    if(flatToTarget.lengthSq()<.001)continue;\n    flatToTarget.normalize();\n\n    if(distance>drone.userData.range)continue;\n    if(forward&&forward.dot(flatToTarget)<drone.userData.fovCos)continue;\n    if(!hasLineOfSight(drone.position,targetPoint))continue;\n\n    if(distance<bestDistance){\n      best=enemy;\n      bestDistance=distance;\n    }\n  }\n\n  return best;\n}\n\nfunction updateDroneBeam(drone,target){\n  const beam=drone.userData.beam;\n\n  if(!beam)return;\n\n  if(!target){\n    beam.visible=false;\n    return;\n  }\n\n  const targetPoint=target.position.clone();\n  targetPoint.y=1.25;\n  const direction=targetPoint.clone().sub(drone.position);\n  const length=direction.length();\n\n  if(length<=.05){\n    beam.visible=false;\n    return;\n  }\n\n  beam.visible=true;\n  if(!drone.userData.lastBeamSound||performance.now()-drone.userData.lastBeamSound>330){\n    drone.userData.lastBeamSound=performance.now();\n    sfx('droneLaser');\n  }\n  beam.geometry.dispose();\n  beam.geometry=new THREE.CylinderGeometry(.025,.025,length,8);\n  beam.position.copy(direction.clone().multiplyScalar(.5));\n  beam.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0),direction.clone().normalize());\n}\n\nfunction clearDrones(){\n  for(const drone of drones){\n    const beam=drone.userData&&drone.userData.beam;\n    if(beam&&beam.geometry)beam.geometry.dispose();\n    scene.remove(drone);\n  }\n  drones=[];\n}\n\nfunction updateBots(){\n  for(const bot of [...bots]){\n    if(bot.userData.health<=0)continue;\n\n    const now=performance.now();\n    const isAlly=bot.userData.team==='ally';\n    const possibleTargets=isAlly?enemies:[{position:player.position,userData:{health:playerHealth,isPlayer:true}},...allies,...drones];\n    const visible=getNearestVisibleTarget(bot,possibleTargets);\n    const seenTarget=visible.target;\n    const seenDistance=visible.dist;\n    const hpRatio=bot.userData.health\/Math.max(1,bot.userData.maxHealth);\n    const lowHealth=hpRatio<0.38;\n    const playerLow=!isAlly&&playerHealth<playerMaxHealth*0.35;\n    const aggression=0.55+Math.min(0.45,difficultyLevel*0.06);\n\n    let targetPoint=null;\n    let combatTarget=null;\n\n    if(seenTarget){\n      bot.userData.lastSeenTarget=seenTarget.position.clone();\n      bot.userData.lastSeenTime=now;\n      if(lowHealth&&!bot.userData.isBoss)bot.userData.state='flee';\n      else if(playerLow&&!isAlly&&Math.random()<0.35)bot.userData.state='rush';\n      else bot.userData.state='attack';\n      combatTarget=seenTarget;\n    }else if(bot.userData.lastSeenTarget&&now-(bot.userData.lastSeenTime||0)<4500){\n      bot.userData.state='search';\n    }else{\n      bot.userData.state='patrol';\n      bot.userData.lastSeenTarget=null;\n    }\n\n    if(bot.userData.state==='attack'||bot.userData.state==='rush'){\n      targetPoint=combatTarget.position.clone();\n      \/\/ Ate\u015f alt\u0131nda veya yak\u0131n d\u00f6v\u00fc\u015fte bot duvar \u00f6rer\n      if(!bot.userData.isBoss&&seenDistance<18&&Math.random()<0.012+difficultyLevel*0.002){\n        botTryBuild(bot,combatTarget);\n      }\n    }\n\n    if(bot.userData.state==='flee'){\n      if(!bot.userData.isBoss&&Math.random()<0.02)botTryBuild(bot,combatTarget||{position:player.position});\n\n      const away=bot.position.clone().sub(combatTarget?combatTarget.position:player.position);\n      away.y=0;\n      if(away.lengthSq()<.01)away.set(Math.random()-.5,0,Math.random()-.5);\n      away.normalize();\n      let coverBoost=away.clone().multiplyScalar(14);\n      for(const w of walls){\n        const c=w.position.clone();c.y=0;\n        const d=bot.position.distanceTo(c);\n        if(d>3&&d<16){\n          const toWall=c.clone().sub(bot.position);toWall.y=0;\n          if(toWall.lengthSq()>0.01){\n            toWall.normalize();\n            if(away.dot(toWall)>0.15){coverBoost=toWall.multiplyScalar(10);break;}\n          }\n        }\n      }\n      targetPoint=bot.position.clone().add(coverBoost);\n    }\n\n    if(bot.userData.state==='search'){\n      targetPoint=bot.userData.lastSeenTarget.clone();\n      if(bot.position.distanceTo(targetPoint)<2.4){\n        bot.userData.lastSeenTarget=null;\n        bot.userData.state='patrol';\n        chooseNewPatrolTarget(bot);\n        targetPoint=bot.userData.patrolTarget.clone();\n      }\n    }\n\n    if(bot.userData.state==='patrol'){\n      if(!bot.userData.patrolTarget||now>bot.userData.nextPatrolTime||bot.position.distanceTo(bot.userData.patrolTarget)<2.2){\n        chooseNewPatrolTarget(bot);\n      }\n      targetPoint=bot.userData.patrolTarget.clone();\n    }\n\n    const baseSpeed=(now<bot.userData.slowUntil)?.04:.078;\n    const speed=baseSpeed*(bot.userData.speedMul||1)*(bot.userData.state==='rush'?1.35:bot.userData.state==='flee'?1.22:1);\n    const dir=targetPoint.clone().sub(bot.position);\n    dir.y=0;\n    if(dir.length()>0.01)dir.normalize();\n\n    const side=new THREE.Vector3(dir.z,0,-dir.x);\n    bot.userData.decision--;\n\n    if(bot.userData.decision<=0){\n      bot.userData.decision=28+Math.random()*50;\n      bot.userData.strafe=Math.random()<.5?-1:1;\n      if(Math.random()<aggression*0.4)bot.userData.strafe*=-1;\n    }\n\n    const move=new THREE.Vector3();\n\n    if(bot.userData.stationary){\n      \/\/ sabit boss\n    }else if(bot.userData.state==='attack'||bot.userData.state==='rush'){\n      if(combatMode==='unarmed'&&!bot.userData.isBoss){\n        if(seenDistance>2.3)move.addScaledVector(dir,speed*1.22);\n        else move.addScaledVector(side,speed*.8*bot.userData.strafe);\n      }else{\n        const idealMin=bot.userData.isBoss?7:4.5;\n        const idealMax=bot.userData.isBoss?14:11;\n        if(bot.userData.state==='rush'||seenDistance>idealMax){\n          move.addScaledVector(dir,speed*(1+aggression*0.25));\n          move.addScaledVector(side,speed*.35*bot.userData.strafe);\n        }else if(seenDistance<idealMin){\n          move.addScaledVector(dir,-speed*.65);\n          move.addScaledVector(side,speed*.9*bot.userData.strafe);\n        }else{\n          move.addScaledVector(side,speed*(0.95+aggression*0.2)*bot.userData.strafe);\n          if(Math.random()<0.08)move.addScaledVector(dir,(Math.random()-.5)*speed);\n        }\n      }\n    }else if(bot.userData.state==='flee'){\n      move.addScaledVector(dir,speed*1.28);\n      move.addScaledVector(side,speed*.4*bot.userData.strafe);\n    }else{\n      move.addScaledVector(dir,speed*1.12);\n    }\n\n    if(!bot.userData.flying&&bot.userData.grounded&&(bot.userData.state==='attack'||bot.userData.state==='flee'||bot.userData.state==='rush')){\n      if(Math.random()<0.012+difficultyLevel*0.002){\n        bot.userData.velocityY=.18;\n        bot.userData.grounded=false;\n      }\n    }\n\n    const before=bot.position.clone();\n    moveWithCollision(bot,move,.72);\n\n    if(before.distanceTo(bot.position)<.006){\n      bot.userData.stuckTimer=(bot.userData.stuckTimer||0)+1;\n      if(bot.userData.stuckTimer>12){\n        const escape=new THREE.Vector3((Math.random()-.5)*2,0,(Math.random()-.5)*2).normalize().multiplyScalar(speed*2.2);\n        moveWithCollision(bot,escape,.72);\n        chooseNewPatrolTarget(bot);\n        bot.userData.lastSeenTarget=null;\n        bot.userData.stuckTimer=0;\n        bot.userData.strafe*=-1;\n      }\n    }else{\n      bot.userData.stuckTimer=0;\n    }\n\n    if(move.lengthSq()>0.0001||(seenTarget&&bot.userData.state!=='patrol')){\n      const lookDir=seenTarget&&bot.userData.state!=='flee'&&bot.userData.state!=='patrol'\n        ? seenTarget.position.clone().sub(bot.position)\n        : dir;\n      lookDir.y=0;\n      if(lookDir.lengthSq()>0.001){\n        bot.lookAt(bot.position.x+lookDir.x,bot.position.y,bot.position.z+lookDir.z);\n      }\n    }\n\n    \/\/ Bot y\u00fcr\u00fcme animasyonu\n    const ap=bot.userData.animParts;\n    if(ap){\n      const movingBot=move.lengthSq()>0.00005;\n      const bt=now*0.012;\n      const sw=movingBot?Math.sin(bt)*0.55:0;\n      if(ap.leg1)ap.leg1.rotation.x=sw;\n      if(ap.leg2)ap.leg2.rotation.x=-sw;\n      if(ap.arm1)ap.arm1.rotation.x=-sw*0.7;\n      if(ap.arm2)ap.arm2.rotation.x=sw*0.7;\n      if(ap.body)ap.body.position.y=1.35+(movingBot?Math.abs(Math.sin(bt))*0.04:0);\n    }\n\n    if(bot.userData.flying){\n      bot.position.y=3.2+Math.sin(now*.004)*.35;\n      bot.userData.velocityY=0;\n      bot.userData.grounded=false;\n    }else{\n      bot.userData.velocityY+=gravity;\n      bot.position.y+=bot.userData.velocityY;\n\n      if(bot.position.y<=0){\n        bot.position.y=0;\n        bot.userData.velocityY=0;\n        bot.userData.grounded=true;\n      }\n\n      handleWallTopStand(bot,false);\n      handleFloorStand(bot,false);\n      handleRampClimb(bot);\n    }\n\n    if(seenTarget&&seenDistance<32){\n      if(bot.userData.state==='flee'){\n        if(Math.random()<0.25)botShoot(bot,seenTarget);\n      }else{\n        botShoot(bot,seenTarget);\n      }\n    }\n  }\n}\n\nfunction canShootDirectly(start,aim){\n  const delta=aim.clone().sub(start);\n  const distance=delta.length();\n\n  if(distance<=.05)return true;\n\n  delta.normalize();\n  const ray=new THREE.Raycaster(start,delta,0,distance);\n  const hits=ray.intersectObjects(getBulletBlockers(),false);\n\n  return hits.length===0;\n}\n\nfunction botShoot(bot,target){\n  if(combatMode==='unarmed'&&!bot.userData.lootWeaponId&&!bot.userData.isBoss){\n    botMelee(bot,target);\n    return;\n  }\n\n  const now=performance.now();\n  let cd=bot.userData.lootWeaponId ? (bot.userData.lootCooldown||650) : (bot.userData.team==='ally'?720:Math.max(430,850-difficultyLevel*35));\n  if(bot.userData.isBoss)cd=bot.userData.shotCooldown||cd;\n\n  if(now-bot.userData.lastShot<cd)return;\n\n  const start=bot.position.clone();\n  start.y=bot.userData.flying ? bot.position.y : 1.5;\n\n  const aim=target.position.clone();\n  aim.y=target.userData&&target.userData.isPlayer ? 1.35 : Math.max(1.25,target.position.y+1.25);\n\n  \/\/ Zorluk artt\u0131k\u00e7a hafif \u00f6ng\u00f6r\u00fc\n  if(target.userData&&target.userData.isPlayer&&difficultyLevel>=2){\n    const lead=0.08+Math.min(0.22,difficultyLevel*0.03);\n    const fwd=new THREE.Vector3(Math.sin(yaw),0,Math.cos(yaw));\n    if(typeof lastMoveAmount==='number'&&lastMoveAmount>0.2){\n      aim.add(fwd.multiplyScalar(lead*lastMoveAmount*8));\n    }\n  }\n\n  if(!canShootDirectly(start,aim))return;\n\n  bot.userData.lastShot=now;\n  sfx(bot.userData.isBoss?'boss':'botShot');\n\n  const dir=aim.clone().sub(start).normalize();\n\n  const bSpeed=bot.userData.isBoss?4.2:5.2;\n  const bullet=new THREE.Mesh(\n    new THREE.CylinderGeometry(0.025,0.025,0.3,6),\n    new THREE.MeshBasicMaterial({color:bot.userData.isBoss?0xff8a00:(bot.userData.team==='ally'?0x22c55e:0xef4444)})\n  );\n  bullet.quaternion.setFromUnitVectors(new THREE.Vector3(0,1,0),dir);\n\n  const bulletStart=start.clone().add(dir.clone().multiplyScalar(1.0));\n  bullet.position.copy(bulletStart);\n  bullet.userData.isProjectile=true;\n  bullet.userData.bulletUid=++bulletUid;\n  bullet.userData.owner=bot.userData.team;\n  scene.add(bullet);\n\n  bullets.push({\n    mesh:bullet,\n    bulletUid:bullet.userData.bulletUid,\n    velocity:dir.clone().multiplyScalar(bSpeed),\n    life:bot.userData.isBoss?55:45,\n    age:0,\n    start:bulletStart.clone(),\n    maxRange:bot.userData.isBoss?70:60,\n    damage:bot.userData.isBoss ? (bot.userData.shotDamage||12) : (bot.userData.lootWeaponId ? (bot.userData.lootDamage||10) : (bot.userData.team==='ally'?12:8+difficultyLevel*.4)),\n    team:bot.userData.team,\n    owner:bot.userData.team\n  });\n}\n\n\n\nfunction disposeProjectileMesh(mesh){\n  if(!mesh)return;\n  try{\n    mesh.visible=false;\n    mesh.userData.isProjectile=false;\n    mesh.userData.dead=true;\n    if(mesh.parent)mesh.parent.remove(mesh);\n    else if(scene)scene.remove(mesh);\n    if(mesh.geometry&&mesh.geometry.dispose)mesh.geometry.dispose();\n    if(mesh.material){\n      if(Array.isArray(mesh.material))mesh.material.forEach(m=>m&&m.dispose&&m.dispose());\n      else if(mesh.material.dispose)mesh.material.dispose();\n    }\n  }catch(e){}\n}\n\nfunction removeBulletAt(index){\n  const b=bullets[index];\n  if(!b){if(index>=0&&index<bullets.length)bullets.splice(index,1);return;}\n  if(b.velocity){b.velocity.set(0,0,0);}\n  b.life=0;\n  b.dead=true;\n  if(b.mesh)disposeProjectileMesh(b.mesh);\n  b.mesh=null;\n  bullets.splice(index,1);\n}\n\nfunction removeOrphanProjectiles(){\n  if(!scene)return;\n  const live=new Set();\n  for(const b of bullets){\n    if(b&&b.mesh)live.add(b.mesh);\n  }\n  const orphans=[];\n  scene.traverse(obj=>{\n    if(obj&&obj.userData&&obj.userData.isProjectile&&!live.has(obj))orphans.push(obj);\n  });\n  for(const obj of orphans)disposeProjectileMesh(obj);\n}\n\nfunction cleanupLingeringBullets(){\n  \/\/ Daha agresif temizlik: ate\u015f edilen yerde tak\u0131l\u0131 kalan g\u00f6rsel mermileri s\u00fcp\u00fcr\u00fcr.\n  const camPos=new THREE.Vector3();\n  if(camera)camera.getWorldPosition(camPos);\n\n  for(let i=bullets.length-1;i>=0;i--){\n    const b=bullets[i];\n    if(!b){bullets.splice(i,1);continue;}\n    if(!b.mesh||!b.mesh.position){\n      b.mesh=null;b.dead=true;\n      bullets.splice(i,1);\n      continue;\n    }\n\n    \/\/ Hemen g\u00f6r\u00fcnmez yap, sonra sil\n    if(b.dead){\n      disposeProjectileMesh(b.mesh);\n      b.mesh=null;\n      bullets.splice(i,1);\n      continue;\n    }\n\n    const p=b.mesh.position;\n    const speed=b.velocity?b.velocity.length():0;\n    const travelled=b.start?p.distanceTo(b.start):0;\n    const nearCamera=camPos&&p.distanceTo(camPos)<1.6;\n    const stuckNearStart=b.start&&travelled<0.4&&(b.age||0)>4;\n    const stuckNearGun=nearCamera&&(speed<0.2||travelled<1.2)&&(b.age||0)>2;\n\n    const bad=\n      b.dead||\n      !Number.isFinite(p.x)||!Number.isFinite(p.y)||!Number.isFinite(p.z)||\n      speed<0.04||\n      Math.abs(p.x)>95||Math.abs(p.y)>80||Math.abs(p.z)>95||\n      (b.maxRange&&travelled>b.maxRange)||\n      (b.life!==undefined&&b.life<=0)||\n      (b.age||0)>90||\n      stuckNearStart||\n      stuckNearGun||\n      (b.owner==='player'&&(b.age||0)>2&&nearCamera&&speed<0.35);\n\n    if(bad){\n      if(b.mesh){b.mesh.visible=false;disposeProjectileMesh(b.mesh);b.mesh=null;}\n      b.dead=true;\n      if(b.velocity)b.velocity.set(0,0,0);\n      bullets.splice(i,1);\n    }\n  }\n\n  removeOrphanProjectiles();\n\n  if(soundParticles){\n    for(let i=soundParticles.length-1;i>=0;i--){\n      const p=soundParticles[i];\n      if(!p||!p.mesh||p.life<=0||!Number.isFinite(p.mesh.position.x)){\n        if(p&&p.mesh){try{scene.remove(p.mesh);}catch(e){}}\n        soundParticles.splice(i,1);\n      }\n    }\n  }\n}\nfunction updateBullets(){\n  for(let i=bullets.length-1;i>=0;i--){\n    const b=bullets[i];\n    if(!b||b.dead||!b.mesh||!b.mesh.parent){\n      if(b&&b.mesh)disposeProjectileMesh(b.mesh);\n      bullets.splice(i,1);\n      continue;\n    }\n    const oldPos=b.mesh.position.clone();\n\n    if(!b.velocity||!Number.isFinite(b.velocity.x)||!Number.isFinite(b.velocity.y)||!Number.isFinite(b.velocity.z)){\n      removeBulletAt(i);\n      continue;\n    }\n    if(!b.start)b.start=oldPos.clone();\n    if(!b.maxRange)b.maxRange=55;\n    b.age=(b.age||0)+1;\n    const speed=b.velocity.length();\n    if(speed<0.03||b.life<=0){\n      removeBulletAt(i);\n      continue;\n    }\n    b.mesh.position.add(b.velocity);\n    b.life--;\n\n    if(bulletHitUShip(oldPos,b.mesh.position,b.team)){\n      removeBulletAt(i);\n      continue;\n    }\n\n    const hitBuild=b.team==='player'?bulletHitBuildPiece(oldPos,b.mesh.position,b.damage||18):false;\n    const hitWall=bulletHitsWallSegment(oldPos,b.mesh.position);\n    const outOfRange=b.start&&b.maxRange&&b.mesh.position.distanceTo(b.start)>b.maxRange;\n    const invalidBullet=!Number.isFinite(b.mesh.position.x)||!Number.isFinite(b.mesh.position.y)||!Number.isFinite(b.mesh.position.z);\n    const outsideArena=Math.abs(b.mesh.position.x)>140||Math.abs(b.mesh.position.y)>90||Math.abs(b.mesh.position.z)>190;\n    const tooOld=(b.age||0)>260;\n    let remove=invalidBullet||outsideArena||tooOld||b.life<=0||outOfRange||hitBuild||hitWall;\n    if(remove&&b.mesh){b.mesh.visible=false;}\n    if(remove&&b.explosive&&b.splash&&(hitBuild||hitWall||outOfRange)){\n      const epicenter=b.mesh.position.clone();\n      for(const e of [...enemies]){\n        if(e.userData.health<=0)continue;\n        const d=e.position.distanceTo(epicenter);\n        if(d<b.splash){\n          const falloff=Math.max(0,1-d\/b.splash);\n          damageBot(e,Math.round((b.damage||40)*0.55*falloff),'Roket patlamas\u0131!');\n        }\n      }\n      spawnSoundParticles(epicenter.clone(),0xf97316,18,.14);\n      screenShake(.9,140);\n    }\n\n    if(!remove){\n      if(b.team==='player'||b.team==='ally'){\n        const target=findEnemyHitOnSegment(oldPos,b.mesh.position,1.2);\n\n        if(target){\n          \/\/ \u00d6nce g\u00f6rseli kald\u0131r \u2014 hasar s\u0131ras\u0131nda donma\/kalma olmas\u0131n\n          if(b.mesh){b.mesh.visible=false;disposeProjectileMesh(b.mesh);b.mesh=null;}\n          b.dead=true;\n          if(b.velocity)b.velocity.set(0,0,0);\n          if(b.slow)target.userData.slowUntil=performance.now()+1800;\n          try{\n            damageBot(target,b.damage||10,b.explosive?'Roket isabet!':'\u0130sabet!');\n            if(b.explosive&&b.splash){\n              const epicenter=target.position.clone();\n              for(const e of [...enemies]){\n                if(e===target||e.userData.health<=0)continue;\n                const d=e.position.distanceTo(epicenter);\n                if(d<b.splash){\n                  const falloff=1-d\/b.splash;\n                  damageBot(e,Math.round((b.damage||40)*0.45*falloff),'Patlama!');\n                }\n              }\n              spawnSoundParticles(epicenter.clone().add(new THREE.Vector3(0,1.2,0)),0xf97316,22,.16);\n              screenShake(1.1,160);\n              sfx('bossHit');\n            }\n          }catch(err){}\n          remove=true;\n        }\n\n        \/\/ Online rakip hasar\u0131\n        if(!remove&&onlineMode&&b.team==='player'){\n          const remotes=getRemotePlayersAsTargets();\n          for(const rt of remotes){\n            const boxCenter=rt.position.clone();\n            boxCenter.y+=1.2;\n            const dist=oldPos.distanceTo(boxCenter);\n            const dist2=b.mesh?b.mesh.position.distanceTo(boxCenter):999;\n            if(Math.min(dist,dist2)<1.35){\n              if(b.mesh){b.mesh.visible=false;disposeProjectileMesh(b.mesh);b.mesh=null;}\n              b.dead=true;\n              if(b.velocity)b.velocity.set(0,0,0);\n              damageRemotePlayer(rt.userData.remoteId,b.damage||12);\n              sfx('hit');\n              showMessage('Rakibe isabet!');\n              remove=true;\n              break;\n            }\n          }\n        }\n      }else{\n        if(b.mesh.position.distanceTo(player.position.clone().setY(1.4))<1.1){\n          if(b.mesh){b.mesh.visible=false;disposeProjectileMesh(b.mesh);b.mesh=null;}\n          b.dead=true;\n          if(b.velocity)b.velocity.set(0,0,0);\n          playerHealth-=(b.damage||8)*((characters[currentCharacter]||characters.rookie).damageMul||1);\n          lastPlayerDamageTime=performance.now();\n          remove=true;\n          sfx('hit');\n          makeScreenPulse('rgba(239,68,68,.16)',120);\n          screenShake(.8,120);\n          showMessage('Vuruldun!');\n\n          if(playerHealth<=0)loseGame();\n        }\n\n        for(const ally of allies){\n          if(!remove&&b.mesh.position.distanceTo(ally.position.clone().setY(1.2))<1.2){\n            ally.userData.health-=b.damage||8;\n            remove=true;\n\n            if(ally.userData.health<=0){\n              scene.remove(ally);\n              allies=allies.filter(a=>a!==ally);\n              bots=bots.filter(x=>x!==ally);\n            }\n          }\n        }\n\n        for(const drone of drones){\n          if(!remove&&b.mesh.position.distanceTo(drone.position)<.75){\n            drone.userData.health-=b.damage||8;\n            remove=true;\n            if(drone.userData.health<=0){\n              scene.remove(drone);\n              drones=drones.filter(d=>d!==drone);\n            }\n          }\n        }\n      }\n    }\n\n    if(remove){\n      removeBulletAt(i);\n    }\n  }\n\n  checkWinLose();\n}\n\nfunction findEnemyHitOnSegment(from,to,range){\n  let best=null;\n  let bestDist=Infinity;\n  const seg=to.clone().sub(from);\n  const segLenSq=seg.lengthSq();\n\n  for(const e of enemies){\n    if(e.userData.health<=0)continue;\n\n    const box=new THREE.Box3().setFromObject(e);\n    box.expandByScalar(e.userData.isBoss?.55:.22);\n    const center=box.getCenter(new THREE.Vector3());\n\n    let closestOnSeg;\n    if(segLenSq<=0.000001){\n      closestOnSeg=from.clone();\n    }else{\n      const t=Math.max(0,Math.min(1,center.clone().sub(from).dot(seg)\/segLenSq));\n      closestOnSeg=from.clone().add(seg.clone().multiplyScalar(t));\n    }\n\n    const closestOnBox=box.clampPoint(closestOnSeg,new THREE.Vector3());\n    const dist=closestOnBox.distanceTo(closestOnSeg);\n\n    if(dist<range&&dist<bestDist){\n      best=e;\n      bestDist=dist;\n    }\n  }\n\n  return best;\n}\n\nfunction findEnemyHit(point,range){\n  let best=null;\n  let bestDist=Infinity;\n\n  for(const e of enemies){\n    if(e.userData.health<=0)continue;\n\n    \/\/ Bosslar b\u00fcy\u00fck veya havada olabildi\u011fi i\u00e7in sadece merkez noktas\u0131na bakmak yetmiyordu.\n    \/\/ Art\u0131k ger\u00e7ek 3D kutusuna bak\u0131yoruz; b\u00f6ylece boss d\u00fczg\u00fcn hasar yer.\n    const box=new THREE.Box3().setFromObject(e);\n    box.expandByScalar(e.userData.isBoss?.55:.22);\n\n    const closest=box.clampPoint(point.clone(),new THREE.Vector3());\n    const dist=closest.distanceTo(point);\n\n    if(dist<range&&dist<bestDist){\n      best=e;\n      bestDist=dist;\n    }\n  }\n\n  return best;\n}\n\nfunction bulletHitBuildPiece(from,to,damage){\n  const delta=to.clone().sub(from);\n  const distance=delta.length();\n\n  if(distance<=.001)return false;\n\n  delta.normalize();\n  raycaster.set(from,delta);\n\n  const targets=buildPieces.filter(p=>p.userData&&p.userData.isBuildPiece);\n  const hits=raycaster.intersectObjects(targets,false);\n\n  if(hits.length>0&&hits[0].distance<=distance+.15){\n    const piece=hits[0].object;\n    piece.userData.hp-=damage;\n\n    if(piece.material){\n      const old=piece.material.color.clone();\n      piece.material.color.set(0xffffff);\n\n      setTimeout(()=>{\n        if(piece.material)piece.material.color.copy(old);\n      },60);\n    }\n\n    if(piece.userData.hp<=0)destroyBuildPiece(piece);\n\n    return true;\n  }\n\n  return false;\n}\n\nfunction toggleBuildMode(){\n  setBuildMode(!buildMode);\n}\n\nfunction setBuildMode(active){\n  buildMode=active;\n  if(buildMode)editMode=false;\n\n  if(buildMode){\n    exitScope();\n    crosshair.style.display='block';\n\n    if(gunGroup)gunGroup.visible=false;\n    if(buildGhost)buildGhost.visible=true;\n\n    showMessage('Build a\u00e7\u0131k');\n  }else{\n    if(buildGhost)buildGhost.visible=false;\n    if(gunGroup&&!boomerangActive)gunGroup.visible=true;\n\n    showMessage('Build kapal\u0131');\n  }\n}\n\nfunction setBuildType(type){\n  if(!buildColors[type])return;\n\n  buildType=type;\n  createBuildGhost();\n\n  if(buildMode)buildGhost.visible=true;\n\n  showMessage('Se\u00e7ildi: '+buildNames[type]);\n}\n\nfunction createBuildGhost(){\n  if(buildGhost){\n    scene.remove(buildGhost);\n    buildGhost=null;\n  }\n\n  buildGhost=new THREE.Mesh(\n    getBuildGeometry(buildType),\n    new THREE.MeshBasicMaterial({\n      color:buildColors[buildType],\n      transparent:true,\n      opacity:.35,\n      depthWrite:false\n    })\n  );\n\n  buildGhost.visible=false;\n  scene.add(buildGhost);\n}\n\nfunction getBuildGeometry(type){\n  if(type==='wall')return new THREE.BoxGeometry(5,3,.28);\n  if(type==='floor')return new THREE.BoxGeometry(5,.2,5);\n  return new THREE.BoxGeometry(5,.25,5);\n}\n\nfunction getBuildTarget(){\n  const origin=new THREE.Vector3();\n  camera.getWorldPosition(origin);\n\n  const dir=new THREE.Vector3();\n  camera.getWorldDirection(dir);\n  dir.normalize();\n\n  const targets=[groundMesh,...walls,...buildPieces].filter(Boolean);\n\n  raycaster.set(origin,dir);\n  raycaster.near=.2;\n  raycaster.far=buildDistance;\n\n  const hits=raycaster.intersectObjects(targets,false);\n\n  let point,hitObject=null;\n\n  if(hits.length>0){\n    point=hits[0].point.clone();\n    hitObject=hits[0].object;\n  }else{\n    const flatDir=dir.clone();\n    flatDir.y=0;\n\n    if(flatDir.lengthSq()<.001)return null;\n\n    flatDir.normalize();\n    point=player.position.clone().add(flatDir.multiplyScalar(buildDistance));\n    point.y=0;\n    hitObject=groundMesh;\n  }\n\n  point.x=Math.round(point.x\/2)*2;\n  point.z=Math.round(point.z\/2)*2;\n\n  return{point,hitObject,hasSupport:Boolean(hitObject)};\n}\n\nfunction updateBuildGhost(){\n  if(!buildGhost||!buildMode)return;\n\n  const target=getBuildTarget();\n\n  if(!target)return;\n\n  applyBuildTransform(buildGhost,target,buildType);\n}\n\nfunction applyBuildTransform(obj,target,type){\n  const point=target.point?target.point:target;\n  const gridAngle=Math.round(yaw\/(Math.PI\/2))*(Math.PI\/2);\n\n  obj.position.x=point.x;\n  obj.position.z=point.z;\n\n  if(type==='wall'){\n    obj.position.y=point.y+1.5;\n    obj.rotation.set(0,gridAngle,0);\n  }else if(type==='floor'){\n    obj.position.y=point.y+.12;\n    obj.rotation.set(0,gridAngle,0);\n  }else{\n    \/\/ Rampa oyuncuya d\u00f6n\u00fck: al\u00e7ak taraf oyuncuya yak\u0131n, ileri y\u00f6nde y\u00fckselir\n    obj.position.y=point.y+1.25;\n    \/\/ Bak\u0131\u015f y\u00f6n\u00fcne g\u00f6re 90\u00b0 snap \u2014 rampan\u0131n e\u011fimi oyuncudan uza\u011fa\n    let lookAngle=Math.round(yaw\/(Math.PI\/2))*(Math.PI\/2);\n    \/\/ BoxGeometry + rotX(-PI\/6): y\u00fcksek taraf -localZ; oyuncudan uzak olsun diye yaw kullan\n    obj.rotation.set(-Math.PI\/6,lookAngle,0);\n    \/\/ Rampay\u0131 hafif\u00e7e oyuncudan uza\u011fa kayd\u0131r ki al\u00e7ak kenar yak\u0131nda kals\u0131n\n    const push=new THREE.Vector3(Math.sin(lookAngle),0,Math.cos(lookAngle));\n    obj.position.x+=push.x*0.35;\n    obj.position.z+=push.z*0.35;\n  }\n}\n\nfunction botTryBuild(bot,threat){\n  if(!bot||!bot.userData)return;\n  const now=performance.now();\n  if(now-(bot.userData.lastBuild||0)<2200)return;\n  if(buildPieces.length>70)return;\n  bot.userData.lastBuild=now;\n  const threatPos=threat&&threat.position?threat.position:player.position;\n  const away=bot.position.clone().sub(threatPos);\n  away.y=0;\n  if(away.lengthSq()<0.01)away.set(Math.sin(bot.rotation.y),0,Math.cos(bot.rotation.y));\n  away.normalize();\n  \/\/ Bot ile tehdit aras\u0131na duvar\n  const placeAt=bot.position.clone().add(away.clone().multiplyScalar(-1.8));\n  placeAt.y=0;\n  const piece=new THREE.Mesh(\n    getBuildGeometry('wall'),\n    new THREE.MeshStandardMaterial({color:0xb45309,roughness:0.65,metalness:0.05})\n  );\n  piece.position.set(placeAt.x,1.35,placeAt.z);\n  piece.rotation.y=Math.atan2(away.x,away.z);\n  piece.castShadow=true;\n  piece.receiveShadow=true;\n  piece.userData.isBuildPiece=true;\n  piece.userData.type='wall';\n  piece.userData.material='wood';\n  piece.userData.hp=45;\n  piece.userData.maxHp=45;\n  piece.userData.createdAt=now;\n  piece.userData.lastHit=now;\n  piece.userData.botBuilt=true;\n  scene.add(piece);\n  buildPieces.push(piece);\n  walls.push(piece);\n  sfx('build');\n}\n\nfunction placeBuildPiece(){\n  const target=getBuildTarget();\n\n  if(!target)return;\n\n  if(!target.hasSupport){\n    showMessage('Havaya build koyamazs\u0131n!');\n    return;\n  }\n\n  const piece=new THREE.Mesh(\n    getBuildGeometry(buildType),\n    new THREE.MeshStandardMaterial({\n      color:buildColors[buildType],\n      roughness:.65,\n      metalness:.05\n    })\n  );\n\n  applyBuildTransform(piece,target,buildType);\n\n  piece.castShadow=true;\n  piece.receiveShadow=true;\n  piece.userData.isBuildPiece=true;\n  piece.userData.type=buildType;\n  piece.userData.material=buildMaterial;\n  piece.userData.createdAt=performance.now();\n  piece.userData.lastHit=performance.now();\n\n  const mat=buildMaterials[buildMaterial]||buildMaterials.wood;\n  const baseHp=buildType==='wall'?60:buildType==='ramp'?45:35;\n  piece.userData.hp=Math.round(baseHp*mat.hpMul);\n  piece.userData.maxHp=piece.userData.hp;\n  if(mat.color&&mat.color[buildType])piece.material.color.set(mat.color[buildType]);\n\n  scene.add(piece);\n  buildPieces.push(piece);\n\n  if(buildType==='wall')walls.push(piece);\n\n  \/\/ Performans: max 80 build\n  while(buildPieces.length>80){\n    const old=buildPieces.shift();\n    if(old){\n      scene.remove(old);\n      walls=walls.filter(w=>w!==old);\n      try{if(old.geometry)old.geometry.dispose();if(old.material)old.material.dispose();}catch(e){}\n    }\n  }\n\n  sfx('build');\n  spawnSoundParticles(piece.position.clone().add(new THREE.Vector3(0,1,0)),buildColors[buildType]||0x60a5fa,10,.08);\n  showMessage(mat.name+' '+buildNames[buildType]+' kuruldu!');\n}\n\nfunction handleRampClimb(obj){\n  for(const piece of buildPieces){\n    if(!piece.userData||piece.userData.type!=='ramp')continue;\n\n    const local=piece.worldToLocal(obj.position.clone());\n    const halfW=2.7;\n    const halfD=2.7;\n\n    if(local.x>-halfW&&local.x<halfW&&local.z>-halfD&&local.z<halfD){\n      \/\/ local.z: + taraf al\u00e7ak, - taraf y\u00fcksek (rotX -PI\/6 sonras\u0131)\n      const t=Math.max(0,Math.min(1,(halfD-local.z)\/(halfD*2)));\n      const rampHeight=t*2.4;\n      const worldRampPoint=piece.localToWorld(new THREE.Vector3(local.x,rampHeight,local.z));\n      const offset=obj===player?0.4:0;\n      const desiredY=Math.max(obj===player?2:0,worldRampPoint.y+offset);\n\n      if(obj.position.y<=desiredY+0.35){\n        obj.position.y=desiredY;\n        if(obj===player){playerVelocityY=0;isGrounded=true;}\n        else{obj.userData.velocityY=0;obj.userData.grounded=true;}\n      }\n    }\n  }\n}\n\nfunction handleFloorStand(obj,isPlayer){\n  for(const piece of buildPieces){\n    if(!piece.userData||piece.userData.type!=='floor')continue;\n\n    const box=new THREE.Box3().setFromObject(piece);\n    const margin=isPlayer?.5:.55;\n\n    if(\n      obj.position.x>box.min.x-margin&&obj.position.x<box.max.x+margin&&\n      obj.position.z>box.min.z-margin&&obj.position.z<box.max.z+margin\n    ){\n      const surfaceY=box.max.y;\n      const headOff=isPlayer?1.75:0;\n      const standY=surfaceY+headOff;\n      const velY=isPlayer?playerVelocityY:(obj.userData.velocityY||0);\n      \/\/ \u00dcst bant: alt\u0131ndan ge\u00e7erken \u0131\u015f\u0131nlama yok\n      if(obj.position.y<=standY+0.35&&obj.position.y>=surfaceY+headOff-0.7&&velY<=0.2){\n        obj.position.y=standY;\n        if(isPlayer){playerVelocityY=0;isGrounded=true;}\n        else{obj.userData.velocityY=0;obj.userData.grounded=true;}\n      }\n    }\n  }\n}\n\nfunction handleWallTopStand(obj,isPlayer){\n  for(const wall of walls){\n    \/\/ \u0130nce build duvarlar\u0131 ve parkour raylar\u0131 da dahil; yeterince kal\u0131n\/y\u00fcksek olanlar\n    const box=new THREE.Box3().setFromObject(wall);\n    const wallH=box.max.y-box.min.y;\n    if(wallH<1.2)continue; \/\/ \u00e7ok al\u00e7ak engelleri atla\n\n    const margin=isPlayer?.5:.6;\n    const insideXZ=\n      obj.position.x>box.min.x-margin&&obj.position.x<box.max.x+margin&&\n      obj.position.z>box.min.z-margin&&obj.position.z<box.max.z+margin;\n    if(!insideXZ)continue;\n\n    const surfaceY=box.max.y;\n    const headOff=isPlayer?1.75:0;\n    const standY=surfaceY+headOff;\n    const velY=isPlayer?playerVelocityY:(obj.userData.velocityY||0);\n    \/\/ \u00dcst y\u00fczey band\u0131: alt\u0131na d\u00fc\u015fmeden \u00fcstte tut, alt\u0131ndan ge\u00e7erken \u0131\u015f\u0131nlama\n    \/\/ Geni\u015f bant (\u00f6zellikle y\u00fcksek harita duvarlar\u0131 i\u00e7in)\n    if(obj.position.y<=standY+0.5&&obj.position.y>=surfaceY+headOff-0.85&&velY<=0.25){\n      obj.position.y=standY;\n      if(isPlayer){playerVelocityY=0;isGrounded=true;jumpsLeft=(getEffectiveCharacter().doubleJump)?2:1;}\n      else{obj.userData.velocityY=0;obj.userData.grounded=true;}\n    }\n  }\n}\n\nfunction moveWithCollision(obj,move,radius){\n  const old=obj.position.clone();\n\n  obj.position.x+=move.x;\n  if(collides(obj.position,radius,obj))obj.position.x=old.x;\n\n  obj.position.z+=move.z;\n  if(collides(obj.position,radius,obj))obj.position.z=old.z;\n\n  \/\/ H\u00e2l\u00e2 g\u00f6m\u00fcl\u00fcyse d\u0131\u015far\u0131 it\n  resolveEmbedding(obj,radius);\n}\n\nfunction isOnTopOfWall(pos,box,obj){\n  \/\/ Oyuncu ba\u015f y\u00fcksekli\u011fi ~ surface+1.75; bot y\u22480 taban\n  const isPlayerObj=obj===player;\n  const feetY=isPlayerObj?pos.y-1.75:pos.y;\n  return feetY>=box.max.y-0.2;\n}\n\nfunction collides(pos,radius,obj){\n  for(const wall of walls){\n    if(!wall||!wall.parent)continue;\n    const box=new THREE.Box3().setFromObject(wall);\n    \/\/ \u00dcstte y\u00fcr\u00fcyorsa \u00e7arp\u0131\u015fma yok\n    if(isOnTopOfWall(pos,box,obj||player))continue;\n    \/\/ Ba\u015f\u0131n \u00e7ok \u00fcst\u00fcnde kalan ince tavanlar\n    if(pos.y>box.max.y+1.0)continue;\n\n    \/\/ Yatay AABB + radius\n    const minX=box.min.x-radius,maxX=box.max.x+radius;\n    const minZ=box.min.z-radius,maxZ=box.max.z+radius;\n    if(pos.x<=minX||pos.x>=maxX||pos.z<=minZ||pos.z>=maxZ)continue;\n\n    \/\/ Dikey: ayak\/g\u00f6vde duvar g\u00f6vdesiyle kesi\u015fiyorsa\n    const bodyLow=obj===player?pos.y-1.7:pos.y;\n    const bodyHigh=obj===player?pos.y+0.2:pos.y+2.2;\n    if(bodyHigh<box.min.y||bodyLow>box.max.y)continue;\n\n    return true;\n  }\n  return false;\n}\n\nfunction resolveEmbedding(obj,radius){\n  \/\/ En fazla 4 kez d\u0131\u015far\u0131 it \u2014 duvar i\u00e7ine g\u00f6m\u00fclmeyi \u00e7\u00f6zer\n  for(let n=0;n<4;n++){\n    let pushed=false;\n    for(const wall of walls){\n      if(!wall||!wall.parent)continue;\n      const box=new THREE.Box3().setFromObject(wall);\n      if(isOnTopOfWall(obj.position,box,obj))continue;\n\n      const minX=box.min.x-radius,maxX=box.max.x+radius;\n      const minZ=box.min.z-radius,maxZ=box.max.z+radius;\n      const p=obj.position;\n      if(p.x<=minX||p.x>=maxX||p.z<=minZ||p.z>=maxZ)continue;\n\n      const bodyLow=obj===player?p.y-1.7:p.y;\n      const bodyHigh=obj===player?p.y+0.2:p.y+2.2;\n      if(bodyHigh<box.min.y||bodyLow>box.max.y)continue;\n\n      \/\/ En yak\u0131n kenara it\n      const dxL=p.x-minX,dxR=maxX-p.x,dzL=p.z-minZ,dzR=maxZ-p.z;\n      const m=Math.min(dxL,dxR,dzL,dzR);\n      if(m===dxL)p.x=minX-0.01;\n      else if(m===dxR)p.x=maxX+0.01;\n      else if(m===dzL)p.z=minZ-0.01;\n      else p.z=maxZ+0.01;\n      pushed=true;\n    }\n    if(!pushed)break;\n  }\n}\n\nfunction getBulletBlockers(){\n  return[...walls,...buildPieces];\n}\n\nfunction bulletHitsWallSegment(from,to){\n  const delta=to.clone().sub(from);\n  const distance=delta.length();\n\n  if(distance<=.001)return false;\n\n  delta.normalize();\n  raycaster.set(from,delta);\n\n  const hits=raycaster.intersectObjects(getBulletBlockers(),false);\n\n  return hits.length>0&&hits[0].distance<=distance+.15;\n}\n\nfunction hasLineOfSight(from,to){\n  const start=from.clone();\n  start.y=from.y+1.45;\n\n  const end=to.clone();\n  end.y=to.y?to.y:1.35;\n\n  const dir=end.clone().sub(start);\n  const distance=dir.length();\n\n  if(distance<=.01)return true;\n\n  dir.normalize();\n\n  const ray=new THREE.Raycaster(start,dir,0,distance);\n  const hits=ray.intersectObjects(getBulletBlockers(),false);\n\n  if(hits.length>0)return false;\n\n  for(const smoke of smokes){\n    if(lineIntersectsSmoke(start,end,smoke))return false;\n  }\n\n  return true;\n}\n\nfunction lineIntersectsSmoke(start,end,smoke){\n  const line=end.clone().sub(start);\n  const len=line.length();\n\n  if(len<=.001)return false;\n\n  const dir=line.clone().normalize();\n  const toSmoke=smoke.position.clone().sub(start);\n  const projection=toSmoke.dot(dir);\n\n  if(projection<0||projection>len)return false;\n\n  const closest=start.clone().add(dir.multiplyScalar(projection));\n\n  return closest.distanceTo(smoke.position)<smoke.radius;\n}\n\nfunction checkWinLose(){\n  if(!gameRunning)return;\n  if(parkourActive)return; \/\/ Parkurda d\u00fc\u015fman yok; biti\u015f finishParkour ile\n\n  if(playerHealth<=0){\n    loseGame();\n    return;\n  }\n\n  if(onlineMode){\n    \/\/ Online'da rakip can\u0131 0 olunca damageRemotePlayer zaten winGame \u00e7a\u011f\u0131r\u0131r\n    return;\n  }\n\n  if(enemies.length<=0){\n    winGame();\n  }\n}\n\nfunction winGame(){\n  if(!gameRunning)return;\n\n  gameRunning=false;\n  sfx('win',true);\n  makeScreenPulse('rgba(34,197,94,.16)',220);\n\n  totalMatches++;\n  recordLeaderboardWin();\n  const reward=addMatchRewards(true);\n  if(bossRoundActive){\n    const bossCfg=bossTypes[currentBossType]||bossTypes.giantRobot;\n    coins+=bossCfg.reward||50;\n    passPapel+=bossCfg.reward||50;\n    reward.coins+=bossCfg.reward||50;\n    reward.pass+=bossCfg.reward||50;\n  }\n  difficultyLevel++;\n\n  clearBullets();\n  clearBuildPieces();\n  clearSmokes();\n  clearBoomerang();\n  clearLootCrates();\n  clearBots();\n  clearStorm();\n\n  saveProgress();\n  updateShopUI();\n  updatePassUI();\n\n  winRewardText.textContent=bossRoundActive\n    ? 'BOSS yenildi! +'+reward.coins+' U Papel kazand\u0131n. PASS i\u00e7in +'+reward.pass+' U Papel ilerleme ald\u0131n.'\n    : '+'+reward.coins+' U Papel kazand\u0131n. PASS i\u00e7in +'+reward.pass+' U Papel ilerleme ald\u0131n.';\n\n  winScreen.style.display='flex';\n  hud.style.display='none';\n  hint.style.display='none';\n  crosshair.style.display='none';\n  mobileControls.style.display='none';\n  \/\/ Chat men\u00fcde de kals\u0131n\n  const cb1=document.getElementById('chatBox');if(cb1)cb1.style.display='flex';\n\n  exitScope();\n  setBuildMode(false);\n\n  if(document.pointerLockElement)document.exitPointerLock();\n}\n\nfunction loseGame(){\n  if(!gameRunning)return;\n\n  gameRunning=false;\n  sfx('lose',true);\n  makeScreenPulse('rgba(239,68,68,.14)',220);\n\n  totalMatches++;\n  const reward=addMatchRewards(false);\n\n  clearBullets();\n  clearBuildPieces();\n  clearSmokes();\n  clearBoomerang();\n  clearLootCrates();\n  clearBots();\n  clearStorm();\n\n  loseRewardText.textContent='Kaybetsen de +'+reward.coins+' U Papel kazand\u0131n. PASS i\u00e7in +'+reward.pass+' U Papel ilerleme ald\u0131n.';\n\n  gameOverScreen.style.display='flex';\n  hud.style.display='none';\n  hint.style.display='none';\n  crosshair.style.display='none';\n  mobileControls.style.display='none';\n  const cb2=document.getElementById('chatBox');if(cb2)cb2.style.display='flex';\n\n  exitScope();\n  setBuildMode(false);\n\n  if(document.pointerLockElement)document.exitPointerLock();\n}\n\nfunction clearBullets(){\n  for(let i=bullets.length-1;i>=0;i--){\n    removeBulletAt(i);\n  }\n  removeOrphanProjectiles();\n  bullets=[];\n}\n\nfunction destroyBuildPiece(piece,withFx){\n  if(!piece)return;\n  if(withFx){\n    try{spawnSoundParticles(piece.position.clone().add(new THREE.Vector3(0,1,0)),0xf8fafc,14,.1);}catch(e){}\n  }\n  scene.remove(piece);\n  buildPieces=buildPieces.filter(p=>p!==piece);\n  walls=walls.filter(w=>w!==piece);\n  try{if(piece.geometry)piece.geometry.dispose();if(piece.material)piece.material.dispose();}catch(e){}\n}\n\nfunction damageBuildPiece(piece,amount){\n  if(!piece||!piece.userData)return;\n  piece.userData.hp=(piece.userData.hp||30)-amount;\n  piece.userData.lastHit=performance.now();\n  if(piece.material&&piece.material.emissive){\n    piece.material.emissive.setHex(0xff2200);\n    setTimeout(()=>{try{piece.material.emissive.setHex(0x000000);}catch(e){}},80);\n  }\n  if(piece.userData.hp<=0)destroyBuildPiece(piece,true);\n}\n\nfunction cleanupOldBuilds(){\n  if(!gameRunning)return;\n  const now=performance.now();\n  \/\/ 90 sn dokunulmayan build yok olur\n  for(let i=buildPieces.length-1;i>=0;i--){\n    const p=buildPieces[i];\n    if(!p||!p.userData)continue;\n    const last=p.userData.lastHit||p.userData.createdAt||now;\n    if(now-last>90000)destroyBuildPiece(p,false);\n  }\n  \/\/ Desteksiz par\u00e7alar (\u00e7ok y\u00fcksekte ve alt\u0131 bo\u015f)\n  for(let i=buildPieces.length-1;i>=0;i--){\n    const p=buildPieces[i];\n    if(!p)continue;\n    if(p.position.y>1.2){\n      let supported=false;\n      const box=new THREE.Box3().setFromObject(p);\n      for(const o of buildPieces){\n        if(o===p)continue;\n        const ob=new THREE.Box3().setFromObject(o);\n        if(Math.abs(ob.max.y-box.min.y)<0.4&&\n           box.min.x<ob.max.x&&box.max.x>ob.min.x&&\n           box.min.z<ob.max.z&&box.max.z>ob.min.z){supported=true;break;}\n      }\n      if(!supported&&p.position.y>3.5&&(now-(p.userData.createdAt||0))>4000){\n        destroyBuildPiece(p,true);\n      }\n    }\n  }\n}\n\nfunction initStorm(){\n  clearStorm();\n  if(parkourActive)return;\n  stormRadius=58;\n  stormTarget=58;\n  stormDamageTimer=0;\n  const geo=new THREE.RingGeometry(stormRadius-0.4,stormRadius+0.4,64);\n  geo.rotateX(-Math.PI\/2);\n  stormRing=new THREE.Mesh(geo,new THREE.MeshBasicMaterial({color:0xa855f7,transparent:true,opacity:0.55,side:THREE.DoubleSide}));\n  stormRing.position.y=0.08;\n  scene.add(stormRing);\n}\n\nfunction clearStorm(){\n  if(stormRing){scene.remove(stormRing);try{stormRing.geometry.dispose();}catch(e){}stormRing=null;}\n  stormMesh=null;\n}\n\nfunction updateStorm(dt){\n  if(!gameRunning||parkourActive||!stormRing)return;\n  \/\/ Her 25 sn daral\n  const elapsed=(performance.now()-(window._stormStart||performance.now()))\/1000;\n  if(!window._stormStart)window._stormStart=performance.now();\n  stormTarget=Math.max(12,58-elapsed*0.35);\n  stormRadius+=(stormTarget-stormRadius)*Math.min(1,dt*0.4);\n  \/\/ Ring g\u00fcncelle\n  try{\n    stormRing.geometry.dispose();\n    const geo=new THREE.RingGeometry(Math.max(0.5,stormRadius-0.35),stormRadius+0.35,64);\n    geo.rotateX(-Math.PI\/2);\n    stormRing.geometry=geo;\n  }catch(e){}\n  \/\/ D\u0131\u015far\u0131da hasar\n  const dist=Math.hypot(player.position.x,player.position.z);\n  if(dist>stormRadius){\n    stormDamageTimer+=dt;\n    if(stormDamageTimer>0.7){\n      stormDamageTimer=0;\n      playerHealth=Math.max(0,playerHealth-4);\n      makeScreenPulse('rgba(168,85,247,.2)',100);\n      showMessage('F\u0131rt\u0131na hasar\u0131!');\n      if(playerHealth<=0)loseGame();\n    }\n  }else stormDamageTimer=0;\n}\n\nfunction recordLeaderboardWin(){\n  totalWins=(totalWins||0)+1;\n  try{localStorage.setItem('arena_wins',totalWins);}catch(e){}\n  const name=typeof getChatDisplayName==='function'?getChatDisplayName():('Oyuncu'+Math.floor(Math.random()*9999));\n  let board=[];\n  try{board=JSON.parse(localStorage.getItem('arena_leaderboard')||'[]');}catch(e){board=[];}\n  const existing=board.find(x=>x.name===name);\n  if(existing)existing.wins=(existing.wins||0)+1;\n  else board.push({name:name,wins:1});\n  board.sort((a,b)=>(b.wins||0)-(a.wins||0));\n  board=board.slice(0,15);\n  try{localStorage.setItem('arena_leaderboard',JSON.stringify(board));}catch(e){}\n  if(typeof firebaseReady!=='undefined'&&firebaseReady&&db){\n    try{\n      db.ref('leaderboard\/'+name.replace(\/[.#$\\[\\]]\/g,'_')).set({name:name,wins:totalWins,t:Date.now()});\n    }catch(e){}\n  }\n  return board;\n}\n\nfunction clearBuildPieces(){\n  for(const piece of buildPieces)scene.remove(piece);\n\n  buildPieces=[];\n  walls=walls.filter(wall=>!wall.userData||!wall.userData.isBuildPiece);\n\n  if(buildGhost){\n    scene.remove(buildGhost);\n    buildGhost=null;\n  }\n\n  createBuildGhost();\n}\n\nfunction toggleShop(){\n  shopPanel.style.display=shopPanel.style.display==='none'?'block':'none';\n  passPanel.style.display='none';\n  updateShopUI();\n}\n\nfunction redeemCode(){\n  const value=(codeInput.value||'').replace(\/\\s+\/g,'');\n\n  if(value==='2846'){\n    coins+=100;\n    passPapel+=100;\n    codeInput.value='';\n    checkPassRewards();\n    saveProgress();\n    updateShopUI();\n    updatePassUI();\n    showMessage('+100 U Papel!');\n  }else{\n    showMessage('Kod yanl\u0131\u015f!');\n  }\n}\n\nfunction buyUpgrade(name,price){\n  if(upgrades[name]){\n    showMessage('Zaten al\u0131nd\u0131!');\n    return;\n  }\n\n  if(coins<price){\n    showMessage('Yeterli U Papel yok!');\n    return;\n  }\n\n  coins-=price;\n  upgrades[name]=true;\n\n  saveProgress();\n  updateShopUI();\n\n  if(name==='blueSkin')createPlayerGun();\n\n  showMessage('Sat\u0131n al\u0131nd\u0131!');\n}\n\nfunction buyOrSelectWeapon(id){\n  const w=weapons[id];\n\n  if(!w)return;\n\n  if(!ownedWeapons[id]){\n    if(coins<w.price){\n      showMessage('Yeterli U Papel yok!');\n      return;\n    }\n\n    coins-=w.price;\n    ownedWeapons[id]=true;\n    showMessage(w.name+' al\u0131nd\u0131!');\n  }\n\n  currentWeapon=id;\n\n  const selectedWeapon=weapons[currentWeapon]||weapons.rifle;\n  maxClip=selectedWeapon.clip||0;\n  ammoInClip=selectedWeapon.clip||0;\n  ammoReserve=selectedWeapon.reserve||0;\n\n  exitScope();\n  clearBoomerang();\n  saveProgress();\n  updateShopUI();\n  createPlayerGun();\n  if(parkourActive&&gunGroup)gunGroup.visible=false;\n  createThirdPersonModel();\n  applyCameraMode();\n}\n\n\nfunction buyOrSelectCharacter(id){\n  const character=characters[id];\n  if(!character)return;\n\n  if(!ownedCharacters[id]){\n    if(coins<character.price){\n      showMessage('Yeterli U Papel yok!');\n      return;\n    }\n    coins-=character.price;\n    ownedCharacters[id]=true;\n    showMessage(character.name+' al\u0131nd\u0131!');\n  }\n\n  currentCharacter=id;\n  saveProgress();\n  updateShopUI();\n  createPlayerGun();\n  if(parkourActive&&gunGroup)gunGroup.visible=false;\n  createThirdPersonModel();\n  applyCameraMode();\n}\n\nfunction renderCharacterShop(){\n  characterShop.innerHTML='';\n\n  for(const id of Object.keys(characters)){\n    const c=characters[id];\n    const card=document.createElement('div');\n    card.className='weaponCard';\n\n    const label=currentCharacter===id?'Se\u00e7ili':ownedCharacters[id]?'Se\u00e7':'Al';\n\n    card.innerHTML=`\n      <div class=\"weaponIcon\">${c.icon}<\/div>\n      <div class=\"weaponInfo\">\n        <b>${c.name}<\/b>\n        <small>\n          ${c.tag}<br>\n          Can bonusu: +${c.hpBonus||0} | H\u0131z: x${c.speedMul||1}<br>\n          Fiyat: ${c.price||'\u00dccretsiz'} U Papel\n        <\/small>\n      <\/div>\n      <button class=\"miniButton\">${label}<\/button>\n    `;\n\n    card.querySelector('button').onclick=()=>buyOrSelectCharacter(id);\n    characterShop.appendChild(card);\n  }\n}\n\nfunction updateShopUI(){\n  coinCountSpan.textContent=coins;\n  menuCoins.textContent=coins;\n  if(menuUnapark)menuUnapark.textContent=unapark;\n  menuDifficulty.textContent=difficultyLevel;\n  menuWeapon.textContent=(combatMode==='armed'?(weapons[currentWeapon]||weapons.rifle).name:'Silahs\u0131z')+' | '+(characters[currentCharacter]||characters.rookie).name;\n\n  buyBlueGunBtn.disabled=upgrades.blueSkin;\n  buyFastReloadBtn.disabled=upgrades.fastReload;\n  buyExtraHealthBtn.disabled=upgrades.extraHealth;\n\n  buyBlueGunBtn.textContent=upgrades.blueSkin?'Al\u0131nd\u0131':'Al';\n  buyFastReloadBtn.textContent=upgrades.fastReload?'Al\u0131nd\u0131':'Al';\n  buyExtraHealthBtn.textContent=upgrades.extraHealth?'Al\u0131nd\u0131':'Al';\n\n  renderWeaponShop();\n  renderCharacterShop();\n}\n\nfunction renderWeaponShop(){\n  weaponShop.innerHTML='';\n\n  for(const id of Object.keys(weapons)){\n    const w=weapons[id];\n    const card=document.createElement('div');\n    card.className='weaponCard';\n\n    const label=currentWeapon===id?'Se\u00e7ili':ownedWeapons[id]?'Se\u00e7':'Al';\n\n    const ammoInfo=w.type==='boomerang'\n      ? '\u015earj\u00f6r yok | Space \u0131\u015f\u0131nlanma'\n      : 'Hasar: '+w.damage+' | \u015earj\u00f6r: '+w.clip+' | Yedek: '+w.reserve;\n\n    card.innerHTML=`\n      <div class=\"weaponIcon\">${w.icon}<\/div>\n      <div class=\"weaponInfo\">\n        <b>${w.name}<\/b>\n        <small>\n          ${ammoInfo}<br>\n          ${w.tag}<br>\n          Fiyat: ${w.price||'\u00dccretsiz'} U Papel\n        <\/small>\n      <\/div>\n      <button class=\"miniButton\">${label}<\/button>\n    `;\n\n    card.querySelector('button').onclick=()=>buyOrSelectWeapon(id);\n    weaponShop.appendChild(card);\n  }\n}\n\nfunction saveProgress(){\n  localStorage.setItem('arena_coins',coins);\n  localStorage.setItem('arena_difficulty',difficultyLevel);\n  localStorage.setItem('arena_totalMatches',totalMatches);\n  localStorage.setItem('arena_pass_papel',passPapel);\n  localStorage.setItem('arena_unapark',unapark);\n  localStorage.setItem('arena_pass_claimed',JSON.stringify(claimedPass));\n  localStorage.setItem('arena_upgrades',JSON.stringify(upgrades));\n  localStorage.setItem('arena_currentWeapon',currentWeapon);\n  localStorage.setItem('arena_ownedWeapons',JSON.stringify(ownedWeapons));\n  localStorage.setItem('arena_currentCharacter',currentCharacter);\n  localStorage.setItem('arena_ownedCharacters',JSON.stringify(ownedCharacters));\n}\n\nfunction showMessage(text){\n  messageBox.textContent=text;\n  messageBox.style.display='block';\n\n  clearTimeout(showMessage.timer);\n\n  showMessage.timer=setTimeout(()=>{\n    messageBox.style.display='none';\n  },1300);\n}\n\n\/\/ ==================== GLOBAL CHAT (t\u00fcm oyuncular ayn\u0131 sohbet) ====================\nconst botNames=['Nova','Rex','ShadowAI','Blitz','Echo','Kai','Viper','Orbit','Storm','Pixel'];\nconst realisticChatLines=[\n  'birisi 1v1 ister mi','oda kuruyorum gelin','bu pump \u00e7ok k\u0131r\u0131yor',\n  'scar aldim az once','lag var m\u0131 sizde','gg wp','tek at\u0131\u015f heavy efsane',\n  'parkur rekoru k\u0131rd\u0131m','pass kademe ka\u00e7 sizde','botlar bug\u00fcn sert',\n  'mobilde oynuyorum','3v3 laz\u0131m 1 ki\u015fi','build \u00f6\u011frenmeye \u00e7al\u0131\u015f\u0131yorum',\n  'bumerang ile \u0131\u015f\u0131nlanmak efsane','u gemisi yine vurdu','sand\u0131ktan deagle \u00e7\u0131kt\u0131',\n  'kimse online m\u0131','selam herkese','yeni silahlar g\u00fczel olmu\u015f'\n];\nconst botChatLines=[\n  'flank al\u0131yorum','reload','high ground','push','cover!',\n  'hedef kilitlendi','mermi az','geri \u00e7ekil','ramp rush'\n];\nlet lastBotChat=0;\nlet lastMenuAiChat=0;\nlet menuAiStarted=false;\nlet globalChatId=null;\nlet globalChatName=null;\nlet globalChatRef=null;\nlet globalChatBound=false;\nlet seenChatKeys={};\nlet chatTyping=false;\nlet chatOpen=false;\n\nfunction getChatDisplayName(){\n  if(globalChatName)return globalChatName;\n  const saved=localStorage.getItem('arena_chat_name');\n  if(saved&&saved.length>=2){globalChatName=saved;return saved;}\n  const n='Oyuncu'+Math.floor(1000+Math.random()*9000);\n  globalChatName=n;\n  localStorage.setItem('arena_chat_name',n);\n  return n;\n}\n\nfunction formatChatTime(ts){\n  const d=new Date(ts||Date.now());\n  const h=String(d.getHours()).padStart(2,'0');\n  const m=String(d.getMinutes()).padStart(2,'0');\n  return h+':'+m;\n}\n\nfunction addChatMessage(type,text,meta){\n  const log=document.getElementById('chatLog');\n  if(!log)return;\n  const div=document.createElement('div');\n  div.className=type||'sys';\n  const time=formatChatTime(meta&&meta.t);\n  let line='';\n  if(type==='me')line='['+time+'] '+getChatDisplayName()+': '+text;\n  else if(type==='global'||type==='enemy')line='['+time+'] '+(meta&&meta.name?meta.name:'Oyuncu')+': '+text;\n  else if(type==='bot')line='['+time+'] '+(meta&&meta.name?meta.name:'Bot')+': '+text;\n  else if(type==='ai')line='['+time+'] ArenaAI: '+text;\n  else if(type==='sys')line='['+time+'] \u2022 '+text;\n  else line='['+time+'] '+text;\n  div.textContent=line;\n  log.appendChild(div);\n  log.scrollTop=log.scrollHeight;\n  while(log.children.length>60)log.removeChild(log.firstChild);\n}\n\nfunction openChat(){\n  chatOpen=true;\n  chatTyping=true;\n  const box=document.getElementById('chatBox');\n  const row=document.getElementById('chatInputRow');\n  const input=document.getElementById('chatInput');\n  const btn=document.getElementById('chatOpenBtn');\n  if(box){box.style.display='flex';box.style.zIndex='99999';}\n  if(row){row.style.display='flex';}\n  if(btn)btn.textContent='KAPAT';\n  if(document.pointerLockElement){\n    try{document.exitPointerLock();}catch(e){}\n  }\n  try{Object.keys(keys).forEach(k=>{keys[k]=false;});}catch(e){}\n  if(input){\n    input.value='';\n    input.disabled=false;\n    input.readOnly=false;\n    input.style.display='block';\n    setTimeout(function(){try{input.focus();input.select();}catch(e){}},50);\n  }\n  showMessage('Chat acik \u2014 yaz, Enter gonder');\n}\n\nfunction closeChat(){\n  chatOpen=false;\n  chatTyping=false;\n  const row=document.getElementById('chatInputRow');\n  const input=document.getElementById('chatInput');\n  const btn=document.getElementById('chatOpenBtn');\n  if(row)row.style.display='none';\n  if(btn)btn.textContent='YAZ (T)';\n  if(input){try{input.blur();}catch(e){} input.value='';}\n  if(gameRunning&&controlMode==='pc'&&renderer&&renderer.domElement){\n    setTimeout(function(){\n      try{renderer.domElement.requestPointerLock();}catch(e){}\n    },50);\n  }\n}\n\nfunction toggleChat(){\n  if(chatOpen)closeChat();\n  else openChat();\n}\n\nfunction sendChat(){\n  try{\n    const input=document.getElementById('chatInput');\n    if(!input)return;\n    let text=(input.value||'').trim();\n    if(!text){\n      closeChat();\n      return;\n    }\n    if(text.toLowerCase().startsWith('\/nick ')){\n      const nn=text.slice(6).trim().slice(0,16);\n      if(nn.length>=2){\n        globalChatName=nn;\n        try{localStorage.setItem('arena_chat_name',nn);}catch(e){}\n        addChatMessage('sys','Takma ad: '+nn);\n      }\n      input.value='';\n      closeChat();\n      return;\n    }\n    if(text.length>120)text=text.slice(0,120);\n    input.value='';\n\n    const name=getChatDisplayName();\n    const payload={id:globalChatId||('local_'+Date.now()),name:name,text:text,t:Date.now()};\n    addChatMessage('me',text,{t:payload.t,name:name});\n\n    if(typeof firebaseReady!=='undefined'&&firebaseReady&&db){\n      try{\n        const ref=db.ref('globalChat').push();\n        ref.set(payload).then(function(){\n          setTimeout(function(){try{ref.remove();}catch(e){}},120000);\n        }).catch(function(){});\n      }catch(e){}\n    }\n    closeChat();\n  }catch(err){\n    console.error('sendChat',err);\n    closeChat();\n  }\n}\n\nfunction listenGlobalChat(){\n  if(!firebaseReady||!db||globalChatBound)return;\n  globalChatBound=true;\n  globalChatId='g_'+Math.random().toString(36).slice(2,10)+'_'+Date.now().toString(36);\n  globalChatRef=db.ref('globalChat');\n  \/\/ Son mesajlar\u0131 dinle\n  globalChatRef.limitToLast(40).on('child_added',snap=>{\n    const m=snap.val();\n    const key=snap.key;\n    if(!m||!m.text)return;\n    if(seenChatKeys[key])return;\n    seenChatKeys[key]=true;\n    \/\/ Kendi mesaj\u0131n\u0131 tekrar g\u00f6sterme\n    if(m.id&&globalChatId&&m.id===globalChatId)return;\n    addChatMessage('global',m.text,{name:m.name||'Oyuncu',t:m.t});\n  });\n  \/\/ Presence: online say\u0131s\u0131\n  try{\n    const pres=db.ref('presence\/'+globalChatId);\n    pres.set({name:getChatDisplayName(),t:Date.now()});\n    pres.onDisconnect().remove();\n    db.ref('presence').on('value',snap=>{\n      const n=snap.numChildren?snap.numChildren():Object.keys(snap.val()||{}).length;\n      const title=document.getElementById('chatTitle');\n      if(title)title.textContent='\ud83d\udcac Global Chat \u00b7 '+n+' \u00e7evrimi\u00e7i';\n    });\n  }catch(e){}\n}\n\nfunction listenChat(){\n  \/\/ Oda chat'i de global'e ek olarak (opsiyonel)\n  if(!roomRef)return;\n  roomRef.child('chat').on('child_added',snap=>{\n    const m=snap.val();\n    if(!m||m.from===onlinePlayerId)return;\n    addChatMessage('enemy',m.text,{name:m.name||'Rakip',t:m.t});\n  });\n}\n\nfunction updateBotChat(){\n  const now=performance.now();\n  if(!gameRunning){\n    if(!menuAiStarted){\n      menuAiStarted=true;\n      addChatMessage('sys','Global chat a\u00e7\u0131k \u2014 sitedeki herkes ayn\u0131 sohbette');\n      addChatMessage('sys','Takma ad de\u011fi\u015ftir: \/nick \u0130smin');\n      if(firebaseReady)addChatMessage('ai','Ba\u011fland\u0131n. Yaz, herkes g\u00f6rs\u00fcn.');\n      else addChatMessage('sys','Firebase yoksa sadece lokal g\u00f6r\u00fcn\u00fcr');\n    }\n    \/\/ Ara s\u0131ra ger\u00e7ek\u00e7i bot\/oyuncu hissi (sadece Firebase yoksa veya seyrek)\n    if(now-lastMenuAiChat>28000+Math.random()*20000){\n      lastMenuAiChat=now;\n      if(!firebaseReady||Math.random()<0.25){\n        const name=botNames[Math.floor(Math.random()*botNames.length)];\n        const line=realisticChatLines[Math.floor(Math.random()*realisticChatLines.length)];\n        addChatMessage('bot',line,{name:name,t:Date.now()});\n      }\n    }\n    return;\n  }\n  if(onlineMode)return;\n  if(now-lastBotChat<7000+Math.random()*8000)return;\n  if(bots.length===0)return;\n  lastBotChat=now;\n  const name=botNames[Math.floor(Math.random()*botNames.length)];\n  const line=botChatLines[Math.floor(Math.random()*botChatLines.length)];\n  addChatMessage('bot',line,{name:name,t:Date.now()});\n}\n\nfunction setupChatUI(){\n  const input=document.getElementById('chatInput');\n  const chatBox=document.getElementById('chatBox');\n  const openBtn=document.getElementById('chatOpenBtn');\n  const row=document.getElementById('chatInputRow');\n  if(chatBox){\n    chatBox.style.display='flex';\n    chatBox.style.zIndex='99999';\n    try{document.body.appendChild(chatBox);}catch(e){}\n  }\n  if(row)row.style.display='none';\n  getChatDisplayName();\n\n  \/\/ Yesil buton \u2014 T calismasa bile acar\n  if(openBtn){\n    openBtn.onclick=function(e){\n      e.preventDefault();\n      e.stopPropagation();\n      if(chatOpen)closeChat();\n      else openChat();\n    };\n  }\n\n  if(input){\n    input.addEventListener('keydown',function(e){\n      if(e.code==='Enter'||e.key==='Enter'){\n        e.preventDefault();\n        e.stopPropagation();\n        sendChat();\n      }\n      if(e.code==='Escape'){\n        e.preventDefault();\n        closeChat();\n      }\n      e.stopPropagation();\n    });\n  }\n\n  const roomCodeInput=document.getElementById('roomCodeInput');\n  if(roomCodeInput){\n    roomCodeInput.addEventListener('input',function(){\n      const v=(roomCodeInput.value||'').trim();\n      if(!v&&onlineMode){\n        leaveOnlineRoom();\n        showMessage('Oda kodu silindi \u2014 bot moduna d\u00f6n\u00fcld\u00fc');\n      }\n    });\n  }\n  try{listenGlobalChat();}catch(e){console.warn(e);}\n  setInterval(function(){if(!gameRunning)updateBotChat();},5000);\n  addChatMessage('sys','YAZ butonuna bas veya T tusuna bas');\n}\n\n\nfunction toggleThirdPerson(){\n  thirdPerson=!thirdPerson;\n  applyCameraMode();\n  showMessage(thirdPerson?'3. \u015fah\u0131s kamera':'1. \u015fah\u0131s kamera');\n}\n\nfunction applyCameraMode(){\n  if(!camera)return;\n\n  if(thirdPerson){\n    camera.position.set(0,1.35,5.8);\n  }else{\n    camera.position.set(0,0,0);\n  }\n\n  camera.rotation.x=pitch;\n  if(thirdPersonModel)thirdPersonModel.visible=thirdPerson;\n  if(gunGroup)gunGroup.visible=!thirdPerson&&!buildMode&&!(currentWeapon==='boomerang'&&boomerangActive);\n}\n\nfunction createThirdPersonModel(){\n  if(thirdPersonModel){\n    player.remove(thirdPersonModel);\n    thirdPersonModel=null;\n  }\n\n  const character=characters[currentCharacter]||characters.rookie;\n  thirdPersonModel=new THREE.Group();\n  thirdPersonParts=null;\n\n  const bodyMat=new THREE.MeshStandardMaterial({color:character.color,metalness:.4,roughness:.4});\n  const handMat=new THREE.MeshStandardMaterial({color:character.hand,metalness:.1,roughness:.55});\n  const darkMat=new THREE.MeshStandardMaterial({color:0x0f172a,metalness:.35,roughness:.45});\n  const visorMat=new THREE.MeshStandardMaterial({color:character.visor,metalness:.8,roughness:.2,emissive:character.visor,emissiveIntensity:.25});\n  const bootMat=new THREE.MeshStandardMaterial({color:0x1e293b,metalness:.5,roughness:.4});\n\n  \/\/ G\u00f6vde\n  const torso=new THREE.Mesh(new THREE.BoxGeometry(.62,.78,.36),bodyMat);\n  torso.position.set(0,-.72,0);\n  const chest=new THREE.Mesh(new THREE.BoxGeometry(.68,.22,.4),bodyMat);\n  chest.position.set(0,-.42,0.02);\n\n  \/\/ Kafa\n  const head=new THREE.Mesh(new THREE.BoxGeometry(.44,.44,.44),handMat);\n  head.position.set(0,-.08,0);\n  const visor=new THREE.Mesh(new THREE.BoxGeometry(.38,.1,.04),visorMat);\n  visor.position.set(0,-.05,-.23);\n  const neck=new THREE.Mesh(new THREE.CylinderGeometry(.1,.12,.12,8),handMat);\n  neck.position.set(0,-.28,0);\n\n  \/\/ Kollar (pivot omuzda)\n  const armL=new THREE.Group();\n  const armLMesh=new THREE.Mesh(new THREE.BoxGeometry(.16,.62,.16),bodyMat);\n  armLMesh.position.set(0,-.28,0);\n  const handL=new THREE.Mesh(new THREE.SphereGeometry(.09,10,10),handMat);\n  handL.position.set(0,-.62,0);\n  armL.add(armLMesh,handL);\n  armL.position.set(-.42,-.42,0);\n\n  const armR=new THREE.Group();\n  const armRMesh=new THREE.Mesh(new THREE.BoxGeometry(.16,.62,.16),bodyMat);\n  armRMesh.position.set(0,-.28,0);\n  const handR=new THREE.Mesh(new THREE.SphereGeometry(.09,10,10),handMat);\n  handR.position.set(0,-.62,0);\n  armR.add(armRMesh,handR);\n  armR.position.set(.42,-.42,0);\n\n  \/\/ Bacaklar (pivot kal\u00e7ada)\n  const legL=new THREE.Group();\n  const legLMesh=new THREE.Mesh(new THREE.BoxGeometry(.2,.7,.22),darkMat);\n  legLMesh.position.set(0,-.32,0);\n  const bootL=new THREE.Mesh(new THREE.BoxGeometry(.22,.14,.28),bootMat);\n  bootL.position.set(0,-.72,.02);\n  legL.add(legLMesh,bootL);\n  legL.position.set(-.18,-1.15,0);\n\n  const legR=new THREE.Group();\n  const legRMesh=new THREE.Mesh(new THREE.BoxGeometry(.2,.7,.22),darkMat);\n  legRMesh.position.set(0,-.32,0);\n  const bootR=new THREE.Mesh(new THREE.BoxGeometry(.22,.14,.28),bootMat);\n  bootR.position.set(0,-.72,.02);\n  legR.add(legRMesh,bootR);\n  legR.position.set(.18,-1.15,0);\n\n  thirdPersonModel.add(torso,chest,head,visor,neck,armL,armR,legL,legR);\n  thirdPersonParts={body:torso,chest,head,visor,armL,armR,legL,legR};\n\n  if(combatMode==='armed'){\n    const weapon=weapons[temporaryWeaponId||currentWeapon]||weapons.rifle;\n    const gun=new THREE.Mesh(\n      new THREE.BoxGeometry(.14,.12,.7),\n      new THREE.MeshStandardMaterial({color:weapon.color||0x475569,metalness:.65,roughness:.28})\n    );\n    gun.position.set(.52,-.55,-.32);\n    gun.rotation.x=.15;\n    thirdPersonModel.add(gun);\n    thirdPersonParts.gun=gun;\n  }else{\n    \/\/ yumruk pozisyonu\n  }\n\n  thirdPersonModel.visible=thirdPerson;\n  player.add(thirdPersonModel);\n}\n\nfunction updateThirdPersonAnimation(){\n  if(!thirdPerson||!thirdPersonModel||!thirdPersonParts)return;\n  const moving=lastMoveAmount>.05;\n  const running=!!lastRunState;\n  const t=performance.now()*(running?0.014:0.01);\n  const amp=running?0.85:0.42;\n  const walkSwing=moving?Math.sin(t)*amp:0;\n  const armSwing=moving?Math.sin(t+Math.PI)*(running?0.65:0.38):0;\n  const bob=moving?Math.abs(Math.sin(t))* (running?0.06:0.03):0;\n\n  if(thirdPersonParts.legL)thirdPersonParts.legL.rotation.x=walkSwing;\n  if(thirdPersonParts.legR)thirdPersonParts.legR.rotation.x=-walkSwing;\n  if(thirdPersonParts.armL){\n    thirdPersonParts.armL.rotation.x=armSwing-0.1;\n    thirdPersonParts.armL.rotation.z=moving?0.08:-0.05;\n  }\n  if(thirdPersonParts.armR){\n    thirdPersonParts.armR.rotation.x=-armSwing-0.1;\n    thirdPersonParts.armR.rotation.z=moving?-0.08:0.05;\n  }\n  if(thirdPersonParts.body){\n    thirdPersonParts.body.position.y=-.72+bob;\n    thirdPersonParts.body.rotation.y=moving?Math.sin(t*0.5)*0.04:0;\n  }\n  if(thirdPersonParts.chest)thirdPersonParts.chest.position.y=-.42+bob;\n  if(thirdPersonParts.head){\n    thirdPersonParts.head.position.y=-.08+bob;\n    thirdPersonParts.head.rotation.z=moving?Math.sin(t*0.5)*0.04:0;\n  }\n  if(thirdPersonParts.visor)thirdPersonParts.visor.position.y=-.05+bob;\n  if(!moving){\n    if(thirdPersonParts.armL){thirdPersonParts.armL.rotation.x=-0.12;thirdPersonParts.armL.rotation.z=-0.05;}\n    if(thirdPersonParts.armR){thirdPersonParts.armR.rotation.x=-0.12;thirdPersonParts.armR.rotation.z=0.05;}\n    if(thirdPersonParts.legL)thirdPersonParts.legL.rotation.x=0;\n    if(thirdPersonParts.legR)thirdPersonParts.legR.rotation.x=0;\n  }\n}\n\nfunction unarmedAttack(){\n  const now=performance.now();\n  if(now-lastUnarmedAttackTime<430)return;\n  lastUnarmedAttackTime=now;\n  sfx('melee');\n  screenShake(.45,80);\n\n  if(gunGroup){\n    gunGroup.position.z=-.55;\n    setTimeout(()=>{if(gunGroup)setGunPose()},80);\n  }\n\n  const target=findClosestEnemyToPlayer(unarmedRange);\n  if(!target){\n    showMessage('Yumruk bo\u015fa gitti!');\n    return;\n  }\n\n  const forward=new THREE.Vector3(Math.sin(yaw),0,Math.cos(yaw)).normalize();\n  const toEnemy=target.position.clone().sub(player.position);\n  toEnemy.y=0;\n  if(toEnemy.lengthSq()<.01)return;\n  toEnemy.normalize();\n\n  if(forward.dot(toEnemy)<.12){\n    showMessage('D\u00fc\u015fmana d\u00f6n\u00fck de\u011filsin!');\n    return;\n  }\n\n  damageBot(target,unarmedDamage,'Yumruk isabet!');\n}\n\nfunction botMelee(bot,target){\n  const now=performance.now();\n  if(now-bot.userData.lastMelee<650)return;\n\n  const dist=bot.position.distanceTo(target.position);\n  if(dist>2.4)return;\n\n  bot.userData.lastMelee=now;\n\n  if(target.userData&&target.userData.isPlayer){\n    playerHealth-=10*((characters[currentCharacter]||characters.rookie).damageMul||1);\n    lastPlayerDamageTime=performance.now();\n    showMessage('Bot yumruk att\u0131!');\n    if(playerHealth<=0)loseGame();\n  }else{\n    target.userData.health-=10;\n    if(target.userData.health<=0){\n      scene.remove(target);\n      allies=allies.filter(a=>a!==target);\n      bots=bots.filter(b=>b!==target);\n    }\n  }\n}\n\nfunction updateHUD(){\n  healthBar.style.width=Math.max(0,playerHealth\/playerMaxHealth*100)+'%';\n  staminaBar.style.width=Math.max(0,stamina)+'%';\n\n  const totalEnemyHealth=enemies.reduce((sum,e)=>sum+Math.max(0,e.userData.health),0);\n  const maxEnemyHealth=Math.max(100,enemies.length*100);\n  enemyBar.style.width=Math.max(0,totalEnemyHealth\/maxEnemyHealth*100)+'%';\n\n  const bossHud=bossRoundActive&&currentBossType ? ' | BOSS: '+(bossTypes[currentBossType]||bossTypes.giantRobot).name : '';\n  const onlineHud=onlineMode?(' | ONLINE: '+onlineRoomCode+' | Rakip: '+Object.keys(remotePlayers).length):'';\n  teamText.textContent=(parkourActive?'Parkur | '+parkourEarnedPreview+' Unapark \u00f6n izleme':'Mod: '+selectedMode+' | '+(combatMode==='armed'?'Silahl\u0131':'Silahs\u0131z'))+' | Kamera: '+(thirdPerson?'3. \u015fah\u0131s':'1. \u015fah\u0131s')+' | Karakter: '+(characters[currentCharacter]||characters.rookie).name+' | U Gemisi aktif | Dost: '+allies.length+' | D\u00fc\u015fman: '+enemies.length+bossHud+onlineHud;\n\n  const weapon=weapons[currentWeapon]||weapons.rifle;\n\n  if(combatMode==='unarmed'&&!temporaryWeaponId){\n    ammoText.textContent='Silahs\u0131z: Yumruk | Sand\u0131k bulunca rastgele silah \u00e7\u0131kar | 0 kamera';\n  }else if(combatMode==='unarmed'&&temporaryWeaponId){\n    const tempWeapon=weapons[temporaryWeaponId]||weapons.rifle;\n    ammoText.textContent='Sand\u0131k silah\u0131: '+tempWeapon.name+' | Cephane: '+ammoInClip+' \/ '+ammoReserve;\n  }else if(weapon.type==='boomerang'){\n    ammoText.textContent=boomerangActive\n      ? 'Bumerang: Havada | Space ile \u0131\u015f\u0131nlan'\n      : 'Bumerang: Haz\u0131r | Sol t\u0131k f\u0131rlat, sa\u011f t\u0131k yak\u0131n sald\u0131r\u0131';\n  }else{\n    ammoText.textContent=isReloading\n      ? 'Cephane: dolduruluyor...'\n      : 'Cephane: '+ammoInClip+' \/ '+ammoReserve+' | R: doldur';\n  }\n\n  itemText.textContent='Item: Sis Bombas\u0131 x'+smokeBombCount+' | 2 ile kullan';\n\n  buildText.textContent=buildMode\n    ? 'Build: '+buildNames[buildType]+' | 1 Duvar 2 Zemin 3 Rampa'\n    : 'Build: Kapal\u0131 | B ile a\u00e7';\n\n  difficultyText.textContent='Zorluk: '+difficultyLevel;\n}\n\nfunction rand(min,max){return min+Math.random()*(max-min);}\n\nfunction onWindowResize(){\n  camera.aspect=innerWidth\/innerHeight;\n  camera.updateProjectionMatrix();\n  renderer.setSize(innerWidth,innerHeight);\n}\n<\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Ana Sayfa Arena Build \u2013 1v1 \/ 2v2 \/ 3v3 \/ Parkur ARENA BUILD \ud83d\udfe1 0 U Papel \ud83c\udfc3 0 Unapark \u2b50 Zorluk 1 \ud83c\udfab PASS 1 \ud83d\ude0e Oyuncu Build ustas\u0131 Mod:1v1 Silah:Standart T\u00fcfek \ud83c\udfab ARENA PASS 1. Sezon: U Gemisine Kar\u015f\u0131 Kademe: 1 0 \/ 15 U Papel \ud83e\ude83 TEAM ARENA Hangi cihazdan oynuyorsun? [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-123","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/pages\/123","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/comments?post=123"}],"version-history":[{"count":52,"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/pages\/123\/revisions"}],"predecessor-version":[{"id":201,"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/pages\/123\/revisions\/201"}],"wp:attachment":[{"href":"https:\/\/urasuzay.com\/index.php\/wp-json\/wp\/v2\/media?parent=123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}