Compare commits

..
9 Commits
Author SHA1 Message Date
yilsemandClaude Fable 5 ff59171296 Daisy duzeltmeleri: kullanici ID'leri (33,59,63,75,77,99) + 2-seed akustik tarama
- ID 33,63=Daisy; 59,77=Manong; 75=Kuya1; 99=Daisy (kullanici kesin)
- 33 oncesi Daisy etiketleri -> Marissa (kullanici kurali)
- 2-seed Daisy parmak-iziyle 14 blok Marissa->Daisy (metin paneli onayli)
- Ihtilafli 5 blok (48,93,121,147,263) Marissa'da birakildi
- Degisen bloklar Lily/dogru seslerle yeniden dublaj, timeline yeniden kuruldu

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 15:24:04 +03:00
yilsemandClaude Fable 5 2e8eef14ee Konusmaci atamalari sifirdan yeniden yapildi (12 seed + 3 motor + 9 ajan)
- Kullanicinin 12 kesin ID seed'i temel alindi
- AssemblyAI + Scribe + Resemblyzer ses parmak-izi + Fable ajan filosu
- 190 blogun konusmacisi degisti, dogru seslerle yeniden dublaj (0 hata)
- Dagilim: Marissa 78, Kuya1 63, Kaan 52, Manong 36, Kuya2 33, Litoy 30, Daisy 5
- Zaman cizelgesi yeniden kuruldu (22:28, iki dil esit/senkron)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 09:20:59 +03:00
yilsemandClaude Fable 5 9384a96e7e Isimler: Barangay Kuya 1/2 ve Nanay Daisy (saygi hitabi)
- Kuya 1 -> Barangay Kuya 1, Kuya 2 -> Barangay Kuya 2
- Daisy -> Nanay Daisy (Filipince saygideger anne hitabi)
- Diyalog avatar rozetleri netlesti (K1, K2, ND, Mn...)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:24:46 +03:00
yilsemandClaude Fable 5 41c33d65ce Diyalog kutusuna ID rozeti + sahne gelistirmeleri
- Konusan kisinin repligi oynarken diyalog kutusunda 'ID <n>' rozeti
  gorunur (yanlis ses atamasini bildirmek icin referans numarasi)
- Kullanicinin sahne guncellemeleri dahil (isiklandirma, duvar/pencere,
  diyalog kutusu, typewriter, isin efekti)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 21:21:12 +03:00
yilsemandClaude Fable 5 7c62b88d0e Mobil: dar ekrana gecince kamera baz konuma doner
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:49:07 +03:00
yilsemandClaude Fable 5 39660d07b7 Oyun arayuzu + tam kapasite denetim: 42 atama duzeltmesi, hedef (kime dedi), yeni 3D oda
Konusmaci dogrulama (cift-yargic, 18+8 ajan):
- 42 ek atama duzeltmesi (cogu erken bolum Kuya2->Litoy; Cebuano bloklar Kaan olamaz->Marissa/Litoy vb)
- Kuya 2 gercekte sadece 2 soz soylemis; erken sorgular Litoy'du
- Her 296 bloga 'hedef' (kime soyledi) alani eklendi

Oyun arayuzu (yeniden tasarim):
- Giris ekrani (BASLAT), RPG diyalog kutusu (portre + KONUSAN ➜ HEDEF + daktilo)
- Konusandan hedefe isikli yay + gezen isik noktasi + hedef halkasi
- Oto-kamera (konusana odaklanir, kullanici surukleyince durur)
- Canli konusma istatistigi paneli, bas cevirme animasyonu
- Gece penceresi, tavan lambasi + golgeler, ahsap dokular, bitki, kupalar
- Sabit boyutlu isim etiketleri (sizeAttenuation)
- 8 kod-inceleme bulgusu duzeltildi (intro klavye kacagi, autocam kavgasi,
  sag-tik/pinch filtresi, beamDot dispose, ended reset, DPR, touchcancel)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 18:47:02 +03:00
yilsemandClaude Fable 5 ab0670f227 Konusmaci atama denetimi: 17 onaylanan duzeltme uygulandi
- 18 ajanli denetim (icerik + AAI ses kumesi + capraz dogrulama)
- Kullanici onayli: blok 243 'Fazla durustluk tasliyorsun' -> Marissa
- Erken bolum sorgulari (13,23,25,27,38,69,71,73) Kuya2 -> Litoy
- 47 Daisy->Manong; 236 Kaan->Litoy; 238,246 Kaan->Marissa;
  247,256,294 ->Kaan; 288 ->Kuya1
- Degisen bloklar dogru sesle yeniden seslendirildi (294 haric: kota)
- Timeline yeniden kuruldu (22:23, iki dil esit), cache v=20260707a
- 51 dusuk/orta guvenli oneri kullanici onayina birakildi
  (BEKLEYEN-KONUSMACI-DUZELTMELERI.md)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:50:33 +03:00
yilsemandClaude Fable 5 8bf89df5c9 Taraf duzeltmesi: panel 'Kisiler & Taraflar' olarak gruplandi
- HATA DUZELTME: Kayinvalide (Daisy) daha once karsi tarafa yakin
  gosteriliyordu; konusma icerigi analizine gore Kaan tarafinin
  bas muzakerecisi (kanitlar: TARAF-ANALIZI.md)
- Sol panel 3 grup: Kaan tarafi (Kaan, Marissa, Daisy),
  Arabulucular (Kuya 1, Kuya 2), Karsi taraf (Litoy, Manong)
- Her isimde kisa rol aciklamasi (mobilde gizli, tooltip'te var)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 14:48:16 +03:00
yilsemandClaude Sonnet 5 577f8bd61a Masaya tiklanabilir sahte fatura yigini + tik davranisi + cache surumu
- Kaan onunde masada 3D "Faturalar" kagit yigini; tiklaninca
  "Alper Tarafindan Duzenlenen Sahte faturalar" sayfasi aciliyor
- Karakterlere tek tik: o kisinin siradaki sozune atlar
- data.js cache-busting surumu ?v=20260704e'ye yukseltildi

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 10:13:08 +03:00
4 changed files with 704 additions and 326 deletions
+3 -3
View File
File diff suppressed because one or more lines are too long
+504 -126
View File
@@ -3,73 +3,143 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Toplantı 3D — Orijinal / Türkçe</title>
<title>Toplantı Odası — İnteraktif 3D Kayıt</title>
<style>
:root{ --bg:#0d1117; --panel:#161b22cc; --border:#30363d; --txt:#e6edf3; --mut:#8b949e; --accent:#2f81f7; }
:root{ --bg:#0a0e14; --panel:#12161fdd; --border:#2b3240; --txt:#e8edf4; --mut:#8b95a6;
--accent:#2f81f7; --gold:#e0b400; --glow:0 0 18px rgba(47,129,247,.35); }
*{box-sizing:border-box}
html,body{margin:0;height:100%;background:var(--bg);color:var(--txt);font-family:"Segoe UI",system-ui,sans-serif;overflow:hidden}
html,body{margin:0;height:100%;background:var(--bg);color:var(--txt);
font-family:"Segoe UI",system-ui,sans-serif;overflow:hidden;user-select:none}
#scene{position:fixed;inset:0}
#topbar{position:fixed;top:0;left:0;right:0;height:52px;display:flex;align-items:center;gap:14px;
padding:0 16px;background:linear-gradient(#0d1117ee,#0d111700);z-index:10;pointer-events:none}
#topbar h1{font-size:15px;margin:0;font-weight:600;pointer-events:auto}
#topbar .sub{font-size:12px;color:var(--mut);font-weight:400}
/* ---- ÜST BAR ---- */
#topbar{position:fixed;top:0;left:0;right:0;height:54px;display:flex;align-items:center;gap:12px;
padding:0 16px;background:linear-gradient(#0a0e14f0,#0a0e1400);z-index:10;pointer-events:none}
#topbar h1{font-size:15px;margin:0;font-weight:700;letter-spacing:.4px;pointer-events:auto;
text-shadow:0 0 14px rgba(47,129,247,.5)}
#topbar .sub{font-size:11px;color:var(--mut);font-weight:400;letter-spacing:0}
.spacer{flex:1}
.toggle{pointer-events:auto;display:flex;background:#21262d;border:1px solid var(--border);border-radius:20px;overflow:hidden}
.toggle button{background:none;border:0;color:var(--mut);padding:7px 15px;font-size:13px;cursor:pointer;font-weight:600}
.toggle{pointer-events:auto;display:flex;background:#161b26;border:1px solid var(--border);
border-radius:20px;overflow:hidden;box-shadow:var(--glow)}
.toggle button{background:none;border:0;color:var(--mut);padding:7px 15px;font-size:13px;cursor:pointer;font-weight:700}
.toggle button.on{background:var(--accent);color:#fff}
.toggle button.on.trm{background:#8957e5}
#legend{position:fixed;top:60px;left:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
border-radius:10px;padding:10px 12px;font-size:11.5px;backdrop-filter:blur(6px);max-width:220px}
#legend .row{display:flex;align-items:center;gap:7px;margin-top:5px}
#legend .dot{width:11px;height:11px;border-radius:50%;flex:none}
#subtitle{position:fixed;left:50%;transform:translateX(-50%);bottom:90px;width:min(760px,94vw);z-index:10;
background:var(--panel);border:1px solid var(--border);border-radius:10px;padding:6px 14px;backdrop-filter:blur(8px);
text-align:center;transition:opacity .2s}
#subtitle .who{font-weight:700;font-size:12px;margin-bottom:1px}
#subtitle .orig{font-size:12.5px;line-height:1.24}
#subtitle .tr{font-size:12.5px;line-height:1.24;color:var(--mut);margin-top:1px}
#subtitle.trmode .orig{color:var(--mut);font-size:12.5px}
#subtitle.trmode .tr{color:var(--txt)}
.badge{display:inline-block;font-size:10px;background:#3d2a00;color:#e0b400;border:1px solid #6b4a00;
border-radius:6px;padding:1px 6px;margin-left:6px;vertical-align:middle}
#controls{position:fixed;left:0;right:0;bottom:0;z-index:10;background:linear-gradient(#0d111700,#0d1117ee 42%);padding:12px 18px 14px}
.iconbtn{pointer-events:auto;background:#161b26;border:1px solid var(--border);color:var(--mut);
border-radius:20px;padding:7px 13px;font-size:12.5px;cursor:pointer;font-weight:700}
.iconbtn.on{color:#fff;border-color:var(--accent);box-shadow:var(--glow)}
/* ---- SOL PANEL: taraflar ---- */
#legend{position:fixed;top:62px;left:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
border-radius:12px;padding:10px 12px;font-size:11.5px;backdrop-filter:blur(8px);max-width:228px}
#legend>b{font-size:12px;letter-spacing:.4px}
#legend .row{display:flex;align-items:center;gap:7px;margin-top:5px;cursor:pointer}
#legend .row:hover{color:#fff}
#legend .dot{width:11px;height:11px;border-radius:50%;flex:none;box-shadow:0 0 6px currentColor}
/* ---- SAĞ PANEL: skor ---- */
#stats{position:fixed;top:62px;right:12px;z-index:10;background:var(--panel);border:1px solid var(--border);
border-radius:12px;padding:10px 12px;font-size:11px;backdrop-filter:blur(8px);width:216px}
#stats>b{font-size:12px;letter-spacing:.4px}
.srow{margin-top:6px;transition:transform .15s}
.srow.on{transform:translateX(-3px)}
.srow .nm{display:flex;justify-content:space-between;font-size:11px;margin-bottom:2px}
.srow.on .nm{color:#fff;font-weight:700;text-shadow:0 0 8px rgba(255,255,255,.4)}
.srow .bar{height:5px;border-radius:3px;background:#1c2230;overflow:hidden}
.srow .fill{height:100%;border-radius:3px;opacity:.85}
.srow.on .fill{opacity:1;box-shadow:0 0 8px currentColor}
/* ---- RPG DİYALOG KUTUSU ---- */
#dlg{position:fixed;left:50%;transform:translateX(-50%);bottom:92px;width:min(820px,94vw);z-index:10;
background:linear-gradient(#141a26ee,#0e131de8);border:1px solid #34405a;border-radius:14px;
padding:10px 14px 11px;backdrop-filter:blur(10px);display:flex;gap:12px;align-items:flex-start;
box-shadow:0 10px 40px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.05);transition:opacity .2s}
#dlgAva{width:46px;height:46px;border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;
font-weight:800;font-size:19px;color:#0a0e14;margin-top:3px;box-shadow:0 0 14px currentColor}
#dlgBody{flex:1;min-width:0;text-align:left}
#dlgName{font-weight:800;font-size:13px;letter-spacing:.4px;margin-bottom:3px}
#dlgName .arrow{color:var(--mut);font-weight:400;margin:0 5px}
#dlgName .target{color:#cdd6e4}
#dlgMain{font-size:13.5px;line-height:1.38;min-height:19px}
#dlgMain .cursor{display:inline-block;width:7px;background:#e8edf4;height:13px;margin-left:2px;
animation:blink .8s steps(1) infinite;vertical-align:middle}
@keyframes blink{50%{opacity:0}}
#dlgSub{font-size:11.5px;line-height:1.3;color:var(--mut);margin-top:3px}
/* ---- ALT KONTROLLER ---- */
#controls{position:fixed;left:0;right:0;bottom:0;z-index:10;
background:linear-gradient(#0a0e1400,#0a0e14f5 45%);padding:12px 18px 13px}
#tl{width:100%;height:34px;display:block;cursor:pointer;border-radius:6px}
#bar{display:flex;align-items:center;gap:12px;margin-top:8px}
.btn{background:#21262d;border:1px solid var(--border);color:var(--txt);width:40px;height:40px;border-radius:50%;
font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.btn:hover{border-color:var(--accent)}
.btn.play{width:46px;height:46px;font-size:17px;background:var(--accent);border-color:var(--accent)}
.btn{background:#161b26;border:1px solid var(--border);color:var(--txt);width:40px;height:40px;border-radius:50%;
font-size:15px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s}
.btn:hover{border-color:var(--accent);box-shadow:var(--glow)}
.btn.play{width:48px;height:48px;font-size:17px;background:linear-gradient(135deg,#2f81f7,#1a5fd0);
border-color:#2f81f7;box-shadow:0 0 20px rgba(47,129,247,.45)}
#time{font-variant-numeric:tabular-nums;font-size:13px;color:var(--mut);min-width:120px}
#hint{font-size:11px;color:var(--mut)}
@media(max-width:640px){
#subtitle{bottom:138px}
/* ---- GİRİŞ EKRANI ---- */
#intro{position:fixed;inset:0;z-index:50;background:radial-gradient(ellipse at 50% 38%,#131a28 0%,#0a0e14 70%);
display:flex;align-items:center;justify-content:center;text-align:center}
#intro .in{max-width:520px;padding:24px}
#intro h2{font-size:30px;margin:0 0 4px;letter-spacing:1.5px;font-weight:800;
background:linear-gradient(180deg,#fff,#7fb0f5);-webkit-background-clip:text;background-clip:text;color:transparent;
text-shadow:0 0 30px rgba(47,129,247,.35)}
#intro .sm{color:var(--mut);font-size:13px;margin-bottom:22px}
#intro .feats{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin-bottom:26px;font-size:11.5px;color:#aab6c8}
#intro .feats span{background:#141a26;border:1px solid var(--border);border-radius:8px;padding:6px 10px}
#startBtn{background:linear-gradient(135deg,#2f81f7,#1a5fd0);color:#fff;border:0;border-radius:30px;
padding:14px 46px;font-size:17px;font-weight:800;letter-spacing:1px;cursor:pointer;
box-shadow:0 0 34px rgba(47,129,247,.55);animation:pulse 1.8s ease-in-out infinite}
@keyframes pulse{50%{box-shadow:0 0 50px rgba(47,129,247,.85);transform:scale(1.03)}}
#intro .keys{margin-top:22px;font-size:11px;color:#66707f}
.badge{display:inline-block;font-size:10px;background:#3d2a00;color:var(--gold);border:1px solid #6b4a00;
border-radius:6px;padding:1px 6px;margin-left:6px;vertical-align:middle}
@media(max-width:760px){
#stats{display:none}
#legend{max-width:150px;font-size:10px;padding:7px 9px;top:58px}
#legend .desc{display:none}
#dlg{bottom:132px;padding:8px 10px}
#dlgAva{width:36px;height:36px;font-size:15px}
#dlgMain{font-size:12px}
#dlgSub{font-size:10.5px}
#hint{display:none}
#bar{flex-wrap:wrap}
#topbar h1{font-size:13px}
#topbar h1{font-size:12.5px}
#topbar .sub{display:none}
.toggle button{padding:6px 10px;font-size:12px}
#legend{max-width:150px;font-size:10px;padding:7px 9px;top:56px}
#legend b{font-size:10.5px}
.iconbtn{padding:6px 9px;font-size:11px}
#intro h2{font-size:22px}
}
</style>
</head>
<body>
<div id="scene"></div>
<div id="topbar">
<h1>Toplantı — 3D Oda <span class="sub">· uzlaşma görüşmesi (düzenlenmiş kayıt)</span></h1>
<h1>⚖️ TOPLANTI ODASI <span class="sub">· interaktif 3D kayıt · kim ne dedi, kime dedi</span></h1>
<div class="spacer"></div>
<button class="iconbtn on" id="camBtn" title="Oto kamera: konuşana odaklan">🎥 Oto</button>
<div class="toggle" id="langToggle">
<button data-m="orig" class="on">🔊 Orijinal</button>
<button data-m="tr">🇹🇷 Türkçe</button>
</div>
<span id="lineId" style="pointer-events:auto;font-size:9px;color:#484f58;margin-left:8px">#—</span>
</div>
<div id="legend"><b>Masadaki kişiler</b><div id="legendRows"></div></div>
<div id="subtitle">
<div class="who" id="subWho"></div>
<div class="orig" id="subOrig">Başlamak için ▶ tuşuna bas</div>
<div class="tr" id="subTr"></div>
<div id="legend"><b>KİŞİLER & TARAFLAR</b><div id="legendRows"></div></div>
<div id="stats"><b>📊 KONUŞMA İSTATİSTİĞİ</b><div id="statRows"></div></div>
<div id="dlg">
<div id="dlgAva">?</div>
<div id="dlgBody">
<div id="dlgName"></div>
<div id="dlgMain">Başlamak için ▶</div>
<div id="dlgSub"></div>
</div>
</div>
<div id="controls">
<canvas id="tl"></canvas>
<div id="bar">
@@ -78,15 +148,29 @@
<button class="btn" id="next" title="Sonraki konuşma"></button>
<span id="time">00:00 / 00:00</span>
<div class="spacer"></div>
<span id="hint">Zaman çizelgesine tıkla/sürükle · fareyle sahneyi döndür · kişiye tıkla sıradaki sözü</span>
<span id="hint">🖱️ sürükle: kamera · kişiye tıkla: sıradaki sözü · 📄 faturalar · boşluk: oynat/durdur · ←→: 5sn</span>
</div>
</div>
<div id="loadErr" style="display:none;position:fixed;inset:0;z-index:999;background:#0d1117;color:#e6edf3;
<div id="intro">
<div class="in">
<h2>TOPLANTI ODASI</h2>
<div class="sm">İnteraktif 3D uzlaşma kaydı — dolandırıcılık görüşmesi · 22 dk</div>
<div class="feats">
<span>🎧 Orijinal + Türkçe dublaj</span><span>💡 Konuşan kişi ışıkla</span>
<span>🎯 Kime söylediği okla</span><span>🎥 Oto kamera</span><span>📊 Canlı istatistik</span>
</div>
<button id="startBtn">&nbsp;BAŞLAT</button>
<div class="keys">boşluk = oynat/durdur &nbsp;·&nbsp; ← → = 5 sn &nbsp;·&nbsp; fare = kamera &nbsp;·&nbsp; kişiye tıkla = sözleri</div>
</div>
</div>
<div id="loadErr" style="display:none;position:fixed;inset:0;z-index:999;background:#0a0e14;color:#e8edf4;
font-family:'Segoe UI',system-ui,sans-serif;align-items:center;justify-content:center;text-align:center;padding:24px">
<div>
<div style="font-size:15px;font-weight:600;margin-bottom:8px">⚠️ Bir dosya yüklenemedi</div>
<div id="loadErrMsg" style="font-size:13px;color:#8b949e"></div>
<div style="font-size:12px;color:#8b949e;margin-top:10px">Sayfayı yeniden yüklemeyi deneyin veya bir yerel sunucu üzerinden açın (file:// kısıtlamaları olabilir).</div>
<div id="loadErrMsg" style="font-size:13px;color:#8b95a6"></div>
<div style="font-size:12px;color:#8b95a6;margin-top:10px">Sayfayı yeniden yüklemeyi deneyin.</div>
</div>
</div>
<script>
@@ -95,71 +179,169 @@ function showLoadErr(msg){
if(m)m.textContent=msg; if(box){box.style.display='flex';}
}
</script>
<audio id="ao" src="toplanti_orijinal.mp3?v=20260704d" preload="metadata" onerror="showLoadErr('toplanti_orijinal.mp3 yüklenemedi')"></audio>
<audio id="at" preload="metadata" onerror="window.TR_AUDIO_FAILED=true"></audio>
<audio id="ao" src="toplanti_orijinal.mp3?v=20260707a" preload="metadata" onerror="showLoadErr('toplanti_orijinal.mp3 yüklenemedi')"></audio>
<audio id="at" preload="metadata" onerror="window.TR_AUDIO_FAILED=true;try{this.pause();}catch(e){}"></audio>
<script src="three.min.js?v=20260704d" onerror="showLoadErr('three.min.js (3D motoru) yüklenemedi')"></script>
<script src="OrbitControls.js?v=20260704d" onerror="showLoadErr('OrbitControls.js (kamera kontrolü) yüklenemedi')"></script>
<script src="data.js?v=20260704d" onerror="showLoadErr('data.js (toplantı verisi) yüklenemedi')"></script>
<script src="three.min.js?v=20260704f" onerror="showLoadErr('three.min.js (3D motoru) yüklenemedi')"></script>
<script src="OrbitControls.js?v=20260704f" onerror="showLoadErr('OrbitControls.js (kamera kontrolü) yüklenemedi')"></script>
<script src="data.js?v=20260707b" onerror="showLoadErr('data.js (toplantı verisi) yüklenemedi')"></script>
<script>
if(typeof MEET==='undefined'){ showLoadErr('data.js yüklenemedi veya bozuk (MEET tanımsız)'); throw new Error('MEET tanımsız — data.js yüklenemedi'); }
if(typeof MEET==='undefined'){ showLoadErr('data.js yüklenemedi veya bozuk'); throw new Error('MEET tanımsız'); }
const ROLES=MEET.roles, TL=MEET.TL;
const POS3D={
"Kaan":{x:-1.36,z:2.3,stand:false}, "Marissa":{x:1.36,z:2.3,stand:false},
"Kaan":{x:-1.6,z:2.3,stand:false}, "Marissa":{x:1.6,z:2.3,stand:false},
"Dolandırıcı (D-)":{x:-1.6,z:-2.3,stand:false}, "Uzlaşmacı (U)":{x:0.4,z:-2.3,stand:false},
"Yaşlı adam (V)":{x:-3.7,z:0,stand:false}, "Lider Uzlaşmacı (L.U)":{x:3.7,z:0,stand:false},
"Kayınvalide":{x:3.3,z:-2.6,stand:true}
};
const SHORT={"Kaan":"Kaan","Marissa":"Marissa","Dolandırıcı (D-)":"Scammer Litoy","Uzlaşmacı (U)":"Kuya 2",
"Yaşlı adam (V)":"Manong","Lider Uzlaşmacı (L.U)":"Kuya 1","Kayınvalide":"Daisy"};
const SHORT={"Kaan":"Kaan","Marissa":"Marissa","Dolandırıcı (D-)":"Scammer Litoy","Uzlaşmacı (U)":"Barangay Kuya 2",
"Yaşlı adam (V)":"Manong","Lider Uzlaşmacı (L.U)":"Barangay Kuya 1","Kayınvalide":"Nanay Daisy"};
const AVA={"Kaan":"K","Marissa":"M","Dolandırıcı (D-)":"L","Uzlaşmacı (U)":"K2",
"Yaşlı adam (V)":"Mn","Lider Uzlaşmacı (L.U)":"K1","Kayınvalide":"ND"};
const H2L={"Kaan":"Kaan","Marissa":"Marissa","Daisy":"Kayınvalide","Litoy":"Dolandırıcı (D-)",
"Kuya1":"Lider Uzlaşmacı (L.U)","Kuya2":"Uzlaşmacı (U)","Manong":"Yaşlı adam (V)"};
function roundRect(g,x,y,w,h,r){g.beginPath();g.moveTo(x+r,y);g.arcTo(x+w,y,x+w,y+h,r);g.arcTo(x+w,y+h,x,y+h,r);g.arcTo(x,y+h,x,y,r);g.arcTo(x,y,x+w,y,r);g.closePath();}
function makeLabel(text,color,scale){ scale=scale||1;
const c=document.createElement('canvas'); c.width=256; c.height=64; const g=c.getContext('2d');
g.fillStyle="rgba(13,17,23,0.85)"; roundRect(g,4,10,248,44,10); g.fill();
g.fillStyle="rgba(10,14,20,0.88)"; roundRect(g,4,10,248,44,10); g.fill();
g.strokeStyle=color; g.lineWidth=2; roundRect(g,4,10,248,44,10); g.stroke();
g.fillStyle=color; g.font="bold 24px Segoe UI"; g.textAlign="center"; g.textBaseline="middle"; g.fillText(text,128,33);
const spr=new THREE.Sprite(new THREE.SpriteMaterial({map:new THREE.CanvasTexture(c),transparent:true,depthTest:false}));
spr.scale.set(1.9*scale,0.48*scale,1); return spr;
const spr=new THREE.Sprite(new THREE.SpriteMaterial({map:new THREE.CanvasTexture(c),transparent:true,depthTest:false,sizeAttenuation:false}));
spr.scale.set(0.115*scale,0.029*scale,1); return spr;
}
function woodTexture(base,stripe){
const c=document.createElement('canvas'); c.width=256; c.height=256; const g=c.getContext('2d');
g.fillStyle=base; g.fillRect(0,0,256,256);
for(let i=0;i<40;i++){ g.strokeStyle='rgba(0,0,0,'+(0.04+Math.random()*0.08)+')'; g.lineWidth=1+Math.random()*3;
g.beginPath(); const y=Math.random()*256; g.moveTo(0,y);
g.bezierCurveTo(64,y+(Math.random()*8-4),192,y+(Math.random()*8-4),256,y); g.stroke(); }
g.strokeStyle=stripe; g.lineWidth=1;
for(let y=0;y<256;y+=32){ g.strokeRect(0,y,256,32); }
const t=new THREE.CanvasTexture(c); t.wrapS=t.wrapT=THREE.RepeatWrapping; return t;
}
function windowTexture(){
const c=document.createElement('canvas'); c.width=256; c.height=192; const g=c.getContext('2d');
const gr=g.createLinearGradient(0,0,0,192); gr.addColorStop(0,'#0b1530'); gr.addColorStop(1,'#1a2a50');
g.fillStyle=gr; g.fillRect(0,0,256,192);
g.fillStyle='#dfe8ff'; g.beginPath(); g.arc(200,42,17,0,7); g.fill();
g.fillStyle='rgba(223,232,255,0.25)'; g.beginPath(); g.arc(200,42,24,0,7); g.fill();
for(let i=0;i<60;i++){ g.fillStyle='rgba(255,255,255,'+(0.3+Math.random()*0.7)+')';
g.fillRect(Math.random()*256,Math.random()*120,1.4,1.4); }
for(let i=0;i<14;i++){ const bx=10+i*18, bh=30+Math.random()*46;
g.fillStyle='#0a1020'; g.fillRect(bx,192-bh,14,bh);
g.fillStyle='#e8c56a'; for(let w=0;w<5;w++){ if(Math.random()<0.5) g.fillRect(bx+3+(w%2)*6,196-bh+4+Math.floor(w/2)*8,3,4); } }
return new THREE.CanvasTexture(c);
}
/* ================= SAHNE ================= */
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x0d1117);
scene.fog=new THREE.Fog(0x0d1117,16,34);
scene.background=new THREE.Color(0x0a0e14);
scene.fog=new THREE.Fog(0x0a0e14,22,44);
const camera=new THREE.PerspectiveCamera(48,innerWidth/innerHeight,0.1,100);
function fitCamera(){
const narrow=innerWidth<700;
camera.position.set(0, narrow?8.6:6.2, narrow?14.5:9.6);
camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
}
fitCamera();
function baseCamPos(){ const n=innerWidth<700; return new THREE.Vector3(0, n?8.6:6.2, n?14.5:9.6); }
camera.position.copy(baseCamPos());
const renderer=new THREE.WebGLRenderer({antialias:true});
renderer.setSize(innerWidth,innerHeight); renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.shadowMap.enabled=true; renderer.shadowMap.type=THREE.PCFSoftShadowMap;
document.getElementById('scene').appendChild(renderer.domElement);
const controls=new THREE.OrbitControls(camera,renderer.domElement);
controls.enableDamping=true; controls.dampingFactor=0.08; controls.target.set(0,1,0);
controls.maxPolarAngle=Math.PI*0.49; controls.minDistance=5; controls.maxDistance=22;
controls.maxPolarAngle=Math.PI*0.49; controls.minDistance=4.5; controls.maxDistance=24;
scene.add(new THREE.AmbientLight(0xffffff,0.55));
const key=new THREE.DirectionalLight(0xffffff,0.9); key.position.set(6,12,8); scene.add(key);
const fill=new THREE.DirectionalLight(0x88aaff,0.35); fill.position.set(-8,6,-6); scene.add(fill);
scene.add(new THREE.AmbientLight(0x8899bb,0.38));
const key=new THREE.DirectionalLight(0xbfd4ff,0.35); key.position.set(6,12,8); scene.add(key);
// tavan lambası + spot
const lampSpot=new THREE.SpotLight(0xffe2b0,1.25,26,Math.PI/3.4,0.5,1.2);
lampSpot.position.set(0,7.2,0); lampSpot.target.position.set(0,1,0);
lampSpot.castShadow=true; lampSpot.shadow.mapSize.set(1024,1024); lampSpot.shadow.bias=-0.0015;
scene.add(lampSpot); scene.add(lampSpot.target);
const cord=new THREE.Mesh(new THREE.CylinderGeometry(0.02,0.02,1.6,6),new THREE.MeshBasicMaterial({color:0x222831}));
cord.position.set(0,7.0,0); scene.add(cord);
const shade=new THREE.Mesh(new THREE.ConeGeometry(0.75,0.55,24,1,true),
new THREE.MeshStandardMaterial({color:0x2a3242,side:THREE.DoubleSide,emissive:0x110c02}));
shade.position.set(0,6.2,0); scene.add(shade);
const bulbL=new THREE.Mesh(new THREE.SphereGeometry(0.14,12,12),
new THREE.MeshStandardMaterial({color:0xfff3cf,emissive:0xffd88a,emissiveIntensity:1.8}));
bulbL.position.set(0,6.05,0); scene.add(bulbL);
const floor=new THREE.Mesh(new THREE.PlaneGeometry(40,40),new THREE.MeshStandardMaterial({color:0x1c2230,roughness:0.95}));
floor.rotation.x=-Math.PI/2; scene.add(floor);
const rug=new THREE.Mesh(new THREE.CircleGeometry(7.5,48),new THREE.MeshStandardMaterial({color:0x232a3a,roughness:1}));
rug.rotation.x=-Math.PI/2; rug.position.y=0.01; scene.add(rug);
// zemin + halı
const floor=new THREE.Mesh(new THREE.PlaneGeometry(40,40),
new THREE.MeshStandardMaterial({map:woodTexture('#20242e','rgba(255,255,255,0.02)'),roughness:0.9}));
floor.material.map.repeat.set(8,8);
floor.rotation.x=-Math.PI/2; floor.receiveShadow=true; scene.add(floor);
const rug=new THREE.Mesh(new THREE.CircleGeometry(7.2,48),new THREE.MeshStandardMaterial({color:0x232c40,roughness:1}));
rug.rotation.x=-Math.PI/2; rug.position.y=0.012; rug.receiveShadow=true; scene.add(rug);
// duvarlar + pencere + tablo + bitki
const wallMat=new THREE.MeshStandardMaterial({color:0x151b28,roughness:0.95});
const wallB=new THREE.Mesh(new THREE.PlaneGeometry(24,5.2),wallMat); wallB.position.set(0,2.6,-7.5); scene.add(wallB);
const wallL=new THREE.Mesh(new THREE.PlaneGeometry(20,5.2),wallMat); wallL.rotation.y=Math.PI/2; wallL.position.set(-9.5,2.6,0); scene.add(wallL);
const wallR=new THREE.Mesh(new THREE.PlaneGeometry(20,5.2),wallMat); wallR.rotation.y=-Math.PI/2; wallR.position.set(9.5,2.6,0); scene.add(wallR);
const winFrame=new THREE.Mesh(new THREE.BoxGeometry(4.6,2.6,0.12),new THREE.MeshStandardMaterial({color:0x39424f}));
winFrame.position.set(-2.2,2.7,-7.44); scene.add(winFrame);
const winGlass=new THREE.Mesh(new THREE.PlaneGeometry(4.2,2.2),
new THREE.MeshBasicMaterial({map:windowTexture()}));
winGlass.position.set(-2.2,2.7,-7.36); scene.add(winGlass);
const winBar=new THREE.Mesh(new THREE.BoxGeometry(0.08,2.2,0.05),new THREE.MeshStandardMaterial({color:0x39424f}));
winBar.position.set(-2.2,2.7,-7.34); scene.add(winBar);
const pic=new THREE.Mesh(new THREE.PlaneGeometry(1.6,1.1),
new THREE.MeshStandardMaterial({color:0x24405e,emissive:0x0a1522,roughness:0.6}));
pic.rotation.y=-Math.PI/2; pic.position.set(9.44,2.8,-1); scene.add(pic);
const picF=new THREE.Mesh(new THREE.BoxGeometry(0.06,1.3,1.8),new THREE.MeshStandardMaterial({color:0x4d4234}));
picF.position.set(9.47,2.8,-1); scene.add(picF);
const potM=new THREE.Mesh(new THREE.CylinderGeometry(0.32,0.24,0.5,12),new THREE.MeshStandardMaterial({color:0x6b4a2b}));
potM.position.set(-7.6,0.25,-6.2); potM.castShadow=true; scene.add(potM);
const plant=new THREE.Mesh(new THREE.ConeGeometry(0.75,1.9,10),new THREE.MeshStandardMaterial({color:0x2e5c37,roughness:0.9}));
plant.position.set(-7.6,1.4,-6.2); plant.castShadow=true; scene.add(plant);
// masa
const TABLE_H=1.0;
const tableTop=new THREE.Mesh(new THREE.BoxGeometry(6.4,0.16,3.2),new THREE.MeshStandardMaterial({color:0x6b4a2b,roughness:0.6}));
tableTop.position.y=TABLE_H; scene.add(tableTop);
const tableTop=new THREE.Mesh(new THREE.BoxGeometry(6.4,0.16,3.2),
new THREE.MeshStandardMaterial({map:woodTexture('#6b4a2b','rgba(0,0,0,0.06)'),roughness:0.55}));
tableTop.position.y=TABLE_H; tableTop.castShadow=true; tableTop.receiveShadow=true; scene.add(tableTop);
[[2.9,1.4],[-2.9,1.4],[2.9,-1.4],[-2.9,-1.4]].forEach(function(p){
const leg=new THREE.Mesh(new THREE.CylinderGeometry(0.11,0.11,TABLE_H,12),new THREE.MeshStandardMaterial({color:0x4a3220}));
leg.position.set(p[0],TABLE_H/2,p[1]); scene.add(leg);
leg.position.set(p[0],TABLE_H/2,p[1]); leg.castShadow=true; scene.add(leg);
});
// kayıt cihazı (diyagramdaki yerinde: Kaan önü, masada)
const rec=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.12,0.28),
new THREE.MeshStandardMaterial({color:0xd21f1f,emissive:0x550000,roughness:0.4}));
rec.position.set(-1.3,TABLE_H+0.14,0.55); rec.castShadow=true; scene.add(rec);
const recDot=new THREE.Mesh(new THREE.SphereGeometry(0.035,8,8),
new THREE.MeshStandardMaterial({color:0xff3333,emissive:0xff0000,emissiveIntensity:2}));
recDot.position.set(-1.45,TABLE_H+0.21,0.55); scene.add(recDot);
// kupalar
[[0.8,-0.8],[-2.5,-0.4],[2.6,0.7]].forEach(function(p){
const mug=new THREE.Mesh(new THREE.CylinderGeometry(0.09,0.075,0.14,10),
new THREE.MeshStandardMaterial({color:0xcfd6e2,roughness:0.4}));
mug.position.set(p[0],TABLE_H+0.16,p[1]); mug.castShadow=true; scene.add(mug);
});
const rec=new THREE.Mesh(new THREE.BoxGeometry(0.5,0.12,0.28),new THREE.MeshStandardMaterial({color:0xd21f1f,emissive:0x330000,roughness:0.4}));
rec.position.set(0,TABLE_H+0.14,0); scene.add(rec);
const recLbl=makeLabel("🔴 KAYIT","#ff6666",0.55); recLbl.position.set(0,TABLE_H+0.55,0); scene.add(recLbl);
// sahte faturalar (tıklanabilir)
const paperGroup=new THREE.Group();
const paperMat=new THREE.MeshStandardMaterial({color:0xf2ede1,roughness:0.85});
const paperLineMat=new THREE.MeshStandardMaterial({color:0xcfc8b8,roughness:0.9});
const A5_W=0.42, A5_H=0.6, A5_T=0.006;
for(let i=0;i<5;i++){
const sheet=new THREE.Mesh(new THREE.BoxGeometry(A5_W,A5_T,A5_H),paperMat);
sheet.position.set((Math.random()-0.5)*0.03,TABLE_H+0.08+i*(A5_T+0.001),(Math.random()-0.5)*0.03);
sheet.rotation.y=(Math.random()-0.5)*0.28; sheet.castShadow=true;
paperGroup.add(sheet);
if(i===4){
for(let l=0;l<4;l++){
const line=new THREE.Mesh(new THREE.BoxGeometry(A5_W*0.72,0.001,0.012),paperLineMat);
line.position.set(sheet.position.x,sheet.position.y+A5_T/2+0.001,sheet.position.z-A5_H*0.28+l*0.12);
line.rotation.y=sheet.rotation.y;
paperGroup.add(line);
}
}
}
paperGroup.position.set(-0.5,0,1.15);
scene.add(paperGroup);
const paperLbl=makeLabel("📄 Faturalar","#e0b400",0.42); paperLbl.position.set(-0.5,TABLE_H+0.42,1.15); scene.add(paperLbl);
/* ---- karakterler ---- */
const SKIN=0xe8b98c, chars={};
function makeChar(role){
const p=POS3D[role], col=new THREE.Color(ROLES[role].renk);
@@ -176,9 +358,9 @@ function makeChar(role){
const h=new THREE.Mesh(new THREE.SphereGeometry(0.205,18,18,0,Math.PI*2,0,Math.PI*0.62),hair); h.position.y=1.9; grp.add(h);
headY=1.86;
} else {
const seat=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.1,0.56),new THREE.MeshStandardMaterial({color:0x394150,roughness:0.8}));
const seat=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.1,0.56),new THREE.MeshStandardMaterial({color:0x353e50,roughness:0.8}));
seat.position.set(0,0.5,-0.08); grp.add(seat);
const back=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.62,0.08),new THREE.MeshStandardMaterial({color:0x394150,roughness:0.8}));
const back=new THREE.Mesh(new THREE.BoxGeometry(0.56,0.62,0.08),new THREE.MeshStandardMaterial({color:0x353e50,roughness:0.8}));
back.position.set(0,0.8,-0.34); grp.add(back);
const thigh=new THREE.Mesh(new THREE.BoxGeometry(0.34,0.14,0.42),cloth); thigh.position.set(0,0.6,0.14); grp.add(thigh);
const shinL=new THREE.Mesh(new THREE.CylinderGeometry(0.08,0.08,0.5,10),cloth); shinL.position.set(-0.12,0.3,0.32); grp.add(shinL);
@@ -188,49 +370,128 @@ function makeChar(role){
const h=new THREE.Mesh(new THREE.SphereGeometry(0.2,18,18,0,Math.PI*2,0,Math.PI*0.6),hair); h.position.y=1.48; grp.add(h);
headY=1.44;
}
const ring=new THREE.Mesh(new THREE.TorusGeometry(0.55,0.06,10,32),new THREE.MeshStandardMaterial({color:col,emissive:col,emissiveIntensity:1.4}));
grp.traverse(function(o){ if(o.isMesh) o.castShadow=true; });
const ring=new THREE.Mesh(new THREE.TorusGeometry(0.55,0.06,10,32),
new THREE.MeshStandardMaterial({color:col,emissive:col,emissiveIntensity:1.4}));
ring.rotation.x=-Math.PI/2; ring.position.y=0.04; ring.visible=false; grp.add(ring);
// konuşan kişinin üstünde ampul + ışık
const bulb=new THREE.Mesh(new THREE.SphereGeometry(0.1,16,16),new THREE.MeshStandardMaterial({color:0xfff6c0,emissive:0xffca28,emissiveIntensity:2.2}));
const tring=new THREE.Mesh(new THREE.TorusGeometry(0.5,0.035,8,32),
new THREE.MeshBasicMaterial({color:0xffffff,transparent:true,opacity:0.7}));
tring.rotation.x=-Math.PI/2; tring.position.y=0.05; tring.visible=false; grp.add(tring);
const bulb=new THREE.Mesh(new THREE.SphereGeometry(0.1,16,16),
new THREE.MeshStandardMaterial({color:0xfff6c0,emissive:0xffca28,emissiveIntensity:2.2}));
bulb.position.y=headY+1.05; bulb.visible=false; grp.add(bulb);
const halo=new THREE.Mesh(new THREE.SphereGeometry(0.17,16,16),new THREE.MeshBasicMaterial({color:0xffd23a,transparent:true,opacity:0.25}));
const halo=new THREE.Mesh(new THREE.SphereGeometry(0.17,16,16),
new THREE.MeshBasicMaterial({color:0xffd23a,transparent:true,opacity:0.25}));
halo.position.y=headY+1.05; halo.visible=false; grp.add(halo);
const blight=new THREE.PointLight(0xffd23a,0,4,2); blight.position.set(0,headY+1.0,0); grp.add(blight);
const lbl=makeLabel(SHORT[role],"#"+col.getHexString()); lbl.position.y=headY+0.55; grp.add(lbl);
grp.position.set(p.x,0,p.z); grp.rotation.y=Math.atan2(-p.x,-p.z);
scene.add(grp); chars[role]={grp:grp,ring:ring,bulb:bulb,halo:halo,blight:blight,active:false};
grp.position.set(p.x,0,p.z);
const baseY=Math.atan2(-p.x,-p.z);
grp.rotation.y=baseY;
scene.add(grp);
chars[role]={grp:grp,ring:ring,tring:tring,bulb:bulb,halo:halo,blight:blight,active:false,
headY:headY,baseY:baseY,phase:Math.random()*6};
}
Object.keys(POS3D).forEach(makeChar);
// ---- audio + timeline ----
/* ---- konuşmacı -> hedef ışını ---- */
let beam=null, beamDot=null, beamPair='';
function clearBeam(){
if(beam){ scene.remove(beam); beam.geometry.dispose(); beam.material.dispose(); beam=null; }
if(beamDot){ scene.remove(beamDot); beamDot.geometry.dispose(); beamDot.material.dispose(); beamDot=null; }
for(const r in chars) chars[r].tring.visible=false;
beamPair='';
}
function setBeam(fromRole,toRole){
const pairKey=fromRole+'>'+(toRole||'');
if(pairKey===beamPair) return;
clearBeam(); beamPair=pairKey;
if(!fromRole||!toRole||!chars[toRole]||fromRole===toRole) return;
const f=chars[fromRole], t=chars[toRole];
const p1=new THREE.Vector3(POS3D[fromRole].x,f.headY+0.25,POS3D[fromRole].z);
const p2=new THREE.Vector3(POS3D[toRole].x,t.headY+0.25,POS3D[toRole].z);
const mid=p1.clone().add(p2).multiplyScalar(0.5); mid.y+=1.15+p1.distanceTo(p2)*0.12;
const curve=new THREE.QuadraticBezierCurve3(p1,mid,p2);
const col=new THREE.Color(ROLES[fromRole].renk);
beam=new THREE.Mesh(new THREE.TubeGeometry(curve,32,0.022,6,false),
new THREE.MeshBasicMaterial({color:col,transparent:true,opacity:0.4}));
beam.userData.curve=curve; scene.add(beam);
beamDot=new THREE.Mesh(new THREE.SphereGeometry(0.07,10,10),
new THREE.MeshBasicMaterial({color:0xffffff}));
scene.add(beamDot);
t.tring.visible=true;
}
/* ================= SES + ZAMAN ================= */
const ao=document.getElementById('ao'), at=document.getElementById('at');
let mode='orig';
const TL_TR=MEET.TL_tr||null;
if(MEET.trAudio){ at.src=MEET.trAudio+'?v=20260704d'; }
if(MEET.trAudio){ at.src=MEET.trAudio+'?v=20260707a'; }
function TR_READY(){ return !!MEET.trAudio && !!TL_TR && !window.TR_AUDIO_FAILED; }
function A(){ return (mode==='tr'&&TR_READY())?at:ao; }
function curTL(){ return (mode==='tr'&&TR_READY())?TL_TR:TL; }
/* ---- sol panel ---- */
const GROUPS=[
{ad:'🔵 Kaan tarafı', uyeler:[
['Kaan','şikâyetçi'],['Marissa','eşi; fiyat & makbuz takibi'],['Kayınvalide','baş müzakereci']]},
{ad:'⚖️ Arabulucular', uyeler:[
['Lider Uzlaşmacı (L.U)','lider arabulucu'],['Uzlaşmacı (U)','sorgulayan']]},
{ad:'🟡 Karşı taraf', uyeler:[
['Dolandırıcı (D-)','suçlanan'],['Yaşlı adam (V)','usta; Litoy ile çalıştı']]}
];
const lr=document.getElementById('legendRows');
Object.keys(POS3D).forEach(function(r){
const d=document.createElement('div'); d.className='row';
d.innerHTML='<span class="dot" style="background:'+ROLES[r].renk+'"></span>'+SHORT[r]+(POS3D[r].stand?' <span style="color:#8b949e">(ayakta)</span>':'');
GROUPS.forEach(function(g){
const h=document.createElement('div');
h.style.cssText='margin-top:7px;font-weight:700;font-size:10.5px;color:#8b95a6;letter-spacing:.3px';
h.textContent=g.ad; lr.appendChild(h);
g.uyeler.forEach(function(u){
const r=u[0], d=document.createElement('div'); d.className='row'; d.title=u[1]+' — tıkla: ilk sözüne git';
d.innerHTML='<span class="dot" style="background:'+ROLES[r].renk+';color:'+ROLES[r].renk+'"></span>'+SHORT[r]
+(POS3D[r].stand?' <span style="color:#8b95a6">(ayakta)</span>':'')
+' <span class="desc" style="color:#8b95a6;font-size:10px">— '+u[1]+'</span>';
d.onclick=function(){ const T=curTL(), b=T.find(function(x){return x.rol===r;});
if(b){ A().currentTime=b.t; update(); } };
lr.appendChild(d);
});
});
/* ---- skor paneli ---- */
const statTotals={};
TL.forEach(function(b){ const s=statTotals[b.rol]||(statTotals[b.rol]={sec:0,n:0});
s.sec+=(b.e-b.t); s.n++; });
const maxSec=Math.max.apply(null,Object.keys(statTotals).map(function(r){return statTotals[r].sec;}));
const statEls={};
const sr=document.getElementById('statRows');
Object.keys(statTotals).sort(function(a,b){return statTotals[b].sec-statTotals[a].sec;}).forEach(function(r){
const s=statTotals[r], d=document.createElement('div'); d.className='srow';
const tot=Math.round(s.sec), mins=Math.floor(tot/60), secs=tot%60;
d.innerHTML='<div class="nm"><span>'+SHORT[r]+'</span><span style="color:#8b95a6">'
+mins+':'+String(secs).padStart(2,'0')+' · '+s.n+' söz</span></div>'
+'<div class="bar"><div class="fill" style="width:'+(s.sec/maxSec*100).toFixed(0)+'%;background:'
+ROLES[r].renk+';color:'+ROLES[r].renk+'"></div></div>';
sr.appendChild(d); statEls[r]=d;
});
function statHighlight(role){ for(const r in statEls) statEls[r].classList.toggle('on',r===role); }
/* ---- zaman çizelgesi ---- */
const tl=document.getElementById('tl'), tg=tl.getContext('2d');
function curDur(){ const d=A().duration, T=curTL(); return (Number.isFinite(d)&&d>0) ? d : T[T.length-1].e; }
function curDur(){ const d=A().duration, T=curTL(); return (Number.isFinite(d)&&d>0)?d:T[T.length-1].e; }
function fitTL(){ tl.width=tl.clientWidth*devicePixelRatio; tl.height=34*devicePixelRatio; tg.setTransform(devicePixelRatio,0,0,devicePixelRatio,0,0); drawTL(); }
function drawTL(){
const T=curTL(), dur=curDur(), W=tl.clientWidth, H=34;
tg.clearRect(0,0,W,H);
tg.fillStyle="#21262d"; roundRect(tg,0,11,W,12,6); tg.fill();
const T=curTL(), dur=curDur(), W=tl.clientWidth;
tg.clearRect(0,0,W,34);
tg.fillStyle="#161b26"; roundRect(tg,0,11,W,12,6); tg.fill();
tg.globalAlpha=0.85;
T.forEach(function(b){ const x=b.t/dur*W, w=Math.max(1,(b.e-b.t)/dur*W); tg.fillStyle=(ROLES[b.rol]&&ROLES[b.rol].renk)||"#888"; tg.fillRect(x,11,w,12); });
T.forEach(function(b){ const x=b.t/dur*W, w=Math.max(1,(b.e-b.t)/dur*W);
tg.fillStyle=(ROLES[b.rol]&&ROLES[b.rol].renk)||"#888"; tg.fillRect(x,11,w,12); });
tg.globalAlpha=1;
const ph=(A().currentTime/dur)*W; tg.fillStyle="#fff"; tg.fillRect(ph-1,6,2,22); tg.beginPath(); tg.arc(ph,6,4,0,7); tg.fill();
const ph=(A().currentTime/dur)*W;
tg.fillStyle="#fff"; tg.shadowColor="#fff"; tg.shadowBlur=6;
tg.fillRect(ph-1,6,2,22); tg.beginPath(); tg.arc(ph,6,4,0,7); tg.fill(); tg.shadowBlur=0;
}
function seekAtClientX(clientX){ const r=tl.getBoundingClientRect(); const x=(clientX-r.left)/r.width; const a=A(); a.currentTime=Math.max(0,Math.min(1,x))*curDur(); update(); }
function seekAtClientX(clientX){ const r=tl.getBoundingClientRect(); const x=(clientX-r.left)/r.width;
const a=A(); a.currentTime=Math.max(0,Math.min(1,x))*curDur(); update(); }
let drag=false;
tl.addEventListener('mousedown',function(e){drag=true;seekAtClientX(e.clientX);});
addEventListener('mousemove',function(e){if(drag)seekAtClientX(e.clientX);});
@@ -238,6 +499,7 @@ addEventListener('mouseup',function(){drag=false;});
tl.addEventListener('touchstart',function(e){drag=true;seekAtClientX(e.touches[0].clientX);e.preventDefault();},{passive:false});
addEventListener('touchmove',function(e){if(drag){seekAtClientX(e.touches[0].clientX);e.preventDefault();}},{passive:false});
addEventListener('touchend',function(){drag=false;});
addEventListener('touchcancel',function(){drag=false;});
function curIndex(){
const t=A().currentTime, T=curTL();
@@ -246,21 +508,50 @@ function curIndex(){
for(let i=0;i<T.length;i++){ if(T[i].t<=t) ans=i; else break; }
return ans;
}
let lastRole=null;
function highlight(role){ if(role===lastRole)return; lastRole=role; for(const r in chars){ const on=(r===role); chars[r].active=on; chars[r].ring.visible=on; chars[r].bulb.visible=on; chars[r].halo.visible=on; if(!on)chars[r].blight.intensity=0; } }
/* ---- vurgulama + diyalog ---- */
let lastRole=null, speakerRole=null, targetRole=null;
function highlight(role,target){
speakerRole=role; targetRole=target||null;
if(role) setBeam(role,targetRole); else clearBeam();
statHighlight(role);
if(role===lastRole) return; lastRole=role;
for(const r in chars){ const on=(r===role); chars[r].active=on;
chars[r].ring.visible=on; chars[r].bulb.visible=on; chars[r].halo.visible=on;
if(!on)chars[r].blight.intensity=0; }
}
const fmt=function(s){s=Math.max(0,s|0);return String(s/60|0).padStart(2,'0')+':'+String(s%60).padStart(2,'0');};
const sub=document.getElementById('subtitle'),sW=document.getElementById('subWho'),sO=document.getElementById('subOrig'),sT=document.getElementById('subTr');
const lineIdEl=document.getElementById('lineId');
const dlg=document.getElementById('dlg'), dAva=document.getElementById('dlgAva'),
dName=document.getElementById('dlgName'), dMain=document.getElementById('dlgMain'), dSub=document.getElementById('dlgSub');
let twText='', twPos=0, twBlock=-1;
function setDialog(b,i){
const col=(ROLES[b.rol]&&ROLES[b.rol].renk)||'#fff';
dAva.textContent=AVA[b.rol]||SHORT[b.rol].charAt(0);
dAva.style.background=col; dAva.style.color='#0a0e14'; dAva.style.boxShadow='0 0 14px '+col;
const hl=b.hedef&&H2L[b.hedef], hshort=hl?SHORT[hl]:(b.hedef==='Herkes'?'Herkese':null);
dName.innerHTML='<span style="background:#21262d;border:1px solid '+col+';border-radius:6px;padding:1px 7px;margin-right:9px;color:#e6edf3;font-size:12px;font-weight:700">ID '+i+'</span>'
+'<span style="color:'+col+'">'+SHORT[b.rol].toUpperCase()+'</span>'
+(hshort?'<span class="arrow">➜</span><span class="target">'+hshort.toUpperCase()+'</span>':'')
+((mode==='tr'&&!TR_READY())?' <span class="badge">ses hazırlanıyor</span>':'');
const main=(mode==='tr')?b.tr:b.orig, sub=(mode==='tr')?b.orig:b.tr;
if(i!==twBlock){ twBlock=i; twText=main; twPos=0; }
dSub.textContent=sub;
}
function twTick(){
if(twPos<twText.length){
twPos=Math.min(twText.length,twPos+2);
dMain.innerHTML=twText.slice(0,twPos).replace(/</g,'&lt;')+(twPos<twText.length?'<span class="cursor"></span>':'');
}
}
function update(){
const a=A(), T=curTL(), dur=curDur();
document.getElementById('time').textContent=fmt(a.currentTime)+' / '+fmt(dur);
const i=curIndex();
if(i>=0){ const b=T[i]; highlight(b.rol);
let badge=(mode==='tr'&&!TR_READY())?' <span class="badge">ses hazırlanıyor</span>':'';
sW.innerHTML=(SHORT[b.rol]||b.rol)+badge; sW.style.color=(ROLES[b.rol]&&ROLES[b.rol].renk)||'#fff';
sO.textContent=b.orig; sT.textContent=b.tr; sub.style.opacity=1;
lineIdEl.textContent='#'+i;
} else { highlight(null); sub.style.opacity=.6; lineIdEl.textContent='#—'; }
if(i>=0){ const b=T[i];
highlight(b.rol, b.hedef&&H2L[b.hedef]?H2L[b.hedef]:null);
setDialog(b,i);
dlg.style.opacity=1;
} else { highlight(null,null); dlg.style.opacity=.6; }
drawTL();
}
ao.addEventListener('timeupdate',function(){if(mode==='orig'||!TR_READY())update();});
@@ -268,54 +559,141 @@ at.addEventListener('timeupdate',function(){if(mode==='tr'&&TR_READY())update();
ao.addEventListener('loadedmetadata',function(){fitTL();update();});
at.addEventListener('loadedmetadata',function(){if(mode==='tr')fitTL();});
/* ---- kontroller ---- */
const playBtn=document.getElementById('play');
playBtn.onclick=function(){ const a=A(); if(a.paused){a.play();playBtn.textContent='⏸';}else{a.pause();playBtn.textContent='▶';} };
document.getElementById('prev').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A(); if(i>0&&a.currentTime-T[i].t<0.4)i--; a.currentTime=T[Math.max(0,i)].t; update(); };
document.getElementById('next').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A(); a.currentTime=T[Math.min(T.length-1,i+1)].t; update(); };
document.addEventListener('keydown',function(e){ if(e.code==='Space'){e.preventDefault();playBtn.onclick();} if(e.code==='ArrowRight')A().currentTime+=5; if(e.code==='ArrowLeft')A().currentTime-=5; });
document.getElementById('prev').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A();
if(i>0&&a.currentTime-T[i].t<0.4)i--; a.currentTime=T[Math.max(0,i)].t; update(); };
document.getElementById('next').onclick=function(){ const T=curTL(); let i=curIndex(); const a=A();
a.currentTime=T[Math.min(T.length-1,i+1)].t; update(); };
let started=false;
document.addEventListener('keydown',function(e){
if(!started) return;
if(e.code==='Space'){e.preventDefault();playBtn.onclick();}
if(e.code==='ArrowRight')A().currentTime+=5;
if(e.code==='ArrowLeft')A().currentTime-=5; });
[ao,at].forEach(function(a){ a.addEventListener('ended',function(){ playBtn.textContent='▶'; }); });
document.getElementById('langToggle').addEventListener('click',function(e){
const btn=e.target.closest('button'); if(!btn)return; const m=btn.dataset.m; if(m===mode)return;
[].forEach.call(e.currentTarget.children,function(b){ b.classList.toggle('on',b===btn); b.classList.toggle('trm',b===btn&&m==='tr'); });
const wasPlaying=!A().paused; const idx=curIndex(); A().pause();
mode=m; sub.classList.toggle('trmode',m==='tr');
mode=m;
const T=curTL(); const b=T[Math.max(0,idx)]; if(b)A().currentTime=b.t;
fitTL(); update(); if(wasPlaying){A().play();playBtn.textContent='⏸';}
twBlock=-1; fitTL(); update(); if(wasPlaying){A().play();playBtn.textContent='⏸';}
});
/* ---- oto kamera ---- */
let autoCam=true, autoPauseUntil=0, userInteracting=false;
const camBtn=document.getElementById('camBtn');
camBtn.onclick=function(){ autoCam=!autoCam; camBtn.classList.toggle('on',autoCam); };
controls.addEventListener('start',function(){ userInteracting=true; autoPauseUntil=performance.now()+7000; });
controls.addEventListener('end',function(){ userInteracting=false; autoPauseUntil=performance.now()+7000; });
const camTargetPos=new THREE.Vector3();
function autoCamTick(){
if(!autoCam||userInteracting||performance.now()<autoPauseUntil) return;
if(innerWidth<700) return;
let want;
if(speakerRole&&POS3D[speakerRole]){
const p=POS3D[speakerRole];
const d=Math.max(0.35,Math.hypot(p.x,p.z));
const nx=-p.x/d, nz=-p.z/d;
want=new THREE.Vector3(nx*8.8, 5.4, nz*8.8);
if(Math.hypot(want.x-POS3D[speakerRole].x,want.z-POS3D[speakerRole].z)<5){ want.multiplyScalar(1.25); want.y=5.4; }
} else want=baseCamPos();
camTargetPos.copy(want);
camera.position.lerp(camTargetPos,0.022);
}
/* ---- tıklamalar: kişi -> sıradaki sözü, faturalar -> sayfa ---- */
const roleNextIdx={};
function linesForRole(role){ return curTL().filter(function(x){return x.rol===role;}); }
renderer.domElement.addEventListener('click',function(ev){
function openFakeInvoicePage(){
const w=window.open('','_blank');
if(!w)return;
w.document.title='Sahte Faturalar';
w.document.body.style.cssText='background:#0d1117;color:#e6edf3;font-family:"Segoe UI",system-ui,sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;margin:0;text-align:center;font-size:22px;font-weight:600';
w.document.body.textContent='Alper Tarafından Duzenlenen Sahte faturalar';
}
let downXY=null;
renderer.domElement.addEventListener('pointerdown',function(ev){
downXY=(ev.button===0&&ev.isPrimary)?[ev.clientX,ev.clientY]:null; });
renderer.domElement.addEventListener('pointerup',function(ev){
if(ev.button!==0||!ev.isPrimary||!downXY) return;
const moved=Math.hypot(ev.clientX-downXY[0],ev.clientY-downXY[1]); downXY=null;
if(moved>6) return; // sürükleme = kamera, tık değil
const rc=new THREE.Raycaster(), m=new THREE.Vector2((ev.clientX/innerWidth)*2-1,-(ev.clientY/innerHeight)*2+1);
rc.setFromCamera(m,camera);
const gs=Object.keys(chars).map(function(r){return chars[r].grp;});
const hit=rc.intersectObjects(gs,true)[0]; if(!hit)return;
let g=hit.object; while(g.parent&&gs.indexOf(g)<0)g=g.parent;
const clickable=Object.keys(chars).map(function(r){return chars[r].grp;}).concat([paperGroup]);
const hit=rc.intersectObjects(clickable,true)[0]; if(!hit)return;
let g=hit.object; while(g.parent&&clickable.indexOf(g)<0)g=g.parent;
if(g===paperGroup){ openFakeInvoicePage(); return; }
const role=Object.keys(chars).find(function(r){return chars[r].grp===g;}); if(!role)return;
const lines=linesForRole(role); if(!lines.length)return;
let idx=roleNextIdx[role]||0;
if(idx>=lines.length)idx=0;
const b=lines[idx];
const a=A();
const b=lines[idx]; const a=A();
a.currentTime=Math.max(0,b.t-0.1);
roleNextIdx[role]=idx+1;
update();
a.play(); playBtn.textContent='⏸';
update(); a.play(); playBtn.textContent='⏸';
});
/* ---- giriş ekranı ---- */
document.getElementById('startBtn').onclick=function(){
started=true;
document.getElementById('intro').style.display='none';
fitTL(); update();
A().play(); playBtn.textContent='⏸';
};
/* ---- animasyon döngüsü ---- */
let frame=0;
function loop(){
requestAnimationFrame(loop);
const t=performance.now()/1000;
const t=performance.now()/1000; frame++;
for(const r in chars){ const c=chars[r];
if(c.active){ c.grp.position.y=Math.sin(t*4)*0.04; c.ring.rotation.z+=0.02; c.ring.material.emissiveIntensity=1.1+Math.sin(t*6)*0.5;
// nefes alma
const breathe=Math.sin(t*1.4+c.phase)*0.008;
if(c.active){
c.grp.position.y=Math.sin(t*4)*0.04;
c.ring.rotation.z+=0.02; c.ring.material.emissiveIntensity=1.1+Math.sin(t*6)*0.5;
c.bulb.material.emissiveIntensity=1.6+Math.sin(t*6)*0.9; c.blight.intensity=1.4+Math.sin(t*6)*0.9;
c.halo.material.opacity=0.22+Math.sin(t*6)*0.12; c.halo.scale.setScalar(1+Math.sin(t*6)*0.12); }
else if(c.grp.position.y!==0){ c.grp.position.y*=0.85; if(Math.abs(c.grp.position.y)<0.002)c.grp.position.y=0; }
c.halo.material.opacity=0.22+Math.sin(t*6)*0.12; c.halo.scale.setScalar(1+Math.sin(t*6)*0.12);
} else {
c.grp.position.y=c.grp.position.y*0.85+breathe*0.15;
}
// baş çevirme: konuşana (veya konuşmacı hedefe) bak
let lookAt=null;
if(speakerRole&&r===speakerRole&&targetRole&&POS3D[targetRole]) lookAt=POS3D[targetRole];
else if(speakerRole&&r!==speakerRole&&POS3D[speakerRole]) lookAt=POS3D[speakerRole];
let desired=c.baseY;
if(lookAt){
const dx=lookAt.x-POS3D[r].x, dz=lookAt.z-POS3D[r].z;
let want=Math.atan2(dx,dz);
let diff=want-c.baseY;
while(diff>Math.PI)diff-=2*Math.PI; while(diff<-Math.PI)diff+=2*Math.PI;
desired=c.baseY+Math.max(-0.45,Math.min(0.45,diff));
}
let dy=desired-c.grp.rotation.y;
while(dy>Math.PI)dy-=2*Math.PI; while(dy<-Math.PI)dy+=2*Math.PI;
c.grp.rotation.y+=dy*0.06;
}
if(beam&&beamDot){
const tt=(t*0.55)%1;
beamDot.position.copy(beam.userData.curve.getPointAt(tt));
beam.material.opacity=0.3+Math.sin(t*5)*0.12;
}
recDot.material.emissiveIntensity=1.2+Math.sin(t*3.5)*0.9;
if(frame%2===0) twTick();
autoCamTick();
controls.update(); renderer.render(scene,camera);
}
loop();
addEventListener('resize',function(){ fitCamera(); renderer.setSize(innerWidth,innerHeight); fitTL(); });
addEventListener('resize',function(){ camera.aspect=innerWidth/innerHeight; camera.updateProjectionMatrix();
renderer.setPixelRatio(Math.min(devicePixelRatio,2));
renderer.setSize(innerWidth,innerHeight);
if(innerWidth<700) camera.position.copy(baseCamPos());
fitTL(); });
fitTL();
</script>
</body>
Binary file not shown.
Binary file not shown.