Yinyangplanner.js
(function () { 'use strict'; const DATA = [{"id": 0, "group": "Ayin", "kind": "Yin", "gear": ["Silah"], "effect": "50 m hareket sonrasında tetiklenme ihtimali (%)", "values": [15, 30, 45, 60]}, {"id": 1, "group": "Ayin", "kind": "Yin", "gear": ["Silah"], "effect": "Ekipman sayfası değişiminde tetiklenme ihtimali (%)", "values": [15, 30, 45, 60]}, {"id": 2, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Tetiklenme sonrası gizlenme süresi (sn)", "values": [3, 7, 10, 15]}, {"id": 3, "group": "Ayin", "kind": "Yang", "gear": ["Ayakkabı"], "effect": "Azami hareket hızında kalma süresi (sn)", "values": [3, 7, 10, 15]}, {"id": 4, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Yakın hedefin element dirençlerindeki azalma; 6 sn", "values": [3, 7, 10, 15]}, {"id": 5, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Oyuncuya verilen beceri hasarındaki artış (%); 6 sn", "values": [3, 7, 10, 15]}, {"id": 6, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Oyuncudan gelen beceri hasarındaki azalma (%); 6 sn", "values": [3, 7, 10, 15]}, {"id": 7, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Kritik isabet ihtimaline eklenen değer (%); 6 sn", "values": [3, 7, 10, 15]}, {"id": 8, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Engelleme nüfuzuna eklenen değer (%); 6 sn", "values": [3, 7, 10, 15]}, {"id": 9, "group": "Ayin", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Bağlı Yang etkilerine ek süre (sn)", "values": [1, 3, 5, 8]}, {"id": 10, "group": "Muska", "kind": "Yin", "gear": ["Silah"], "effect": "Hasarla birlikte 6 sn süren Lanet Damgası uygulama ihtimali (%); oyuncular ve kademe 1–4 canavarlar", "values": [1, 3, 5, 8]}, {"id": 11, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Damgalı canavarı 6 sn bağlama ihtimali (%); kademe 1–4 canavarlar", "values": [15, 30, 45, 60]}, {"id": 12, "group": "Muska", "kind": "Yang", "gear": ["Kask"], "effect": "Bağlı canavara karşı saldırı değeri artışı (%)", "values": [1, 3, 5, 8]}, {"id": 13, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Bağlı canavar öldüğünde damgalama ihtimaline %50 artış sağlayan süre (sn)", "values": [2, 4, 6, 10]}, {"id": 14, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Damgalı hedefte 9 sn ağır zehirlenme ihtimali (%); kademe 1–4 canavarlar", "values": [2, 4, 6, 10]}, {"id": 15, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Damganın tetiklediği Yang etkilerine ek süre (sn)", "values": [2, 4, 6, 10]}, {"id": 16, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Damgalanabilecek ek hedef adedi", "values": [2, 4, 6, 11]}, {"id": 17, "group": "Muska", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Damgalı hedef öldüğünde yakın zamanda alınan bir durum etkisini silme ihtimali (%)", "values": [1, 3, 5, 8]}, {"id": 18, "group": "Muska", "kind": "Yang", "gear": ["Kask"], "effect": "Damgalı hedefe karşı saldırı değeri artışı (%); oyuncular ve kademe 1–4 canavarlar", "values": [1, 3, 5, 8]}, {"id": 19, "group": "Güç Darbesi", "kind": "Yin", "gear": ["Silah"], "effect": "Normal beceri hasarında tetiklenme için hedef HP üst eşiği (%); HP bu değerin altında olmalıdır", "values": [11, 12, 14, 20]}, {"id": 20, "group": "Güç Darbesi", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Binicilik becerileriyle tetiklenebilme", "values": ["Var", "Var", "Var", "Var"]}, {"id": 21, "group": "Güç Darbesi", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Binicilik becerisiyle tetiklenince 6 sn çift EXP ihtimali (%); kademe 1–4 canavarlar ve Metin taşları", "values": [15, 30, 45, 60]}, {"id": 22, "group": "Güç Darbesi", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Etkiye dahil edilen ek hedef adedi", "values": [2, 4, 6, 11]}, {"id": 23, "group": "Güç Darbesi", "kind": "Yang", "gear": ["Eldiven", "Kask", "Zırh", "Ayakkabı"], "effect": "Normal vuruşla tetiklenme ihtimali (%)", "values": [6, 6, 9, 15]}, {"id": 24, "group": "Kan Öfkesi", "kind": "Yin", "gear": ["Eldiven"], "effect": "Savaşta HP %50 altındayken başlayan Kan Öfkesi süresi (sn); etki boyunca büyü/yakın dövüş +%8", "values": [1, 3, 5, 8]}, {"id": 25, "group": "Kan Öfkesi", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "Kan Öfkesi için ek süre (sn)", "values": [1, 3, 5, 8]}, {"id": 26, "group": "Kan Öfkesi", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "Kan Öfkesi sürerken normal vuruştan gelen hasarda azalma (%)", "values": [3, 9, 15, 25]}, {"id": 27, "group": "Diriliş", "kind": "Yin", "gear": ["Eldiven"], "effect": "Dirilme anındaki HP oranı (%)", "values": [10, 20, 30, 50]}, {"id": 28, "group": "Diriliş", "kind": "Yin", "gear": ["Eldiven"], "effect": "Yeniden canlanma beklemesiyle ilgili kaynak değeri (sn); aşağıdaki notu okuyun", "values": [8, 6, 4, 1]}, {"id": 29, "group": "Diriliş", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "HP yenilenmesine %100 artışın süresi (sn)", "values": [1, 3, 6, 9]}, {"id": 30, "group": "Diriliş", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "Dirilme sonrasında gizlenme süresi (sn)", "values": [6, 7, 9, 13]}, {"id": 31, "group": "Diriliş", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "Dirilme sonrasındaki tecrübe kaybında azalma (%)", "values": [10, 20, 30, 50]}, {"id": 32, "group": "Diriliş", "kind": "Yang", "gear": ["Ayakkabı"], "effect": "Dirilme sonrası 6 sn hareket hızı artışı (%)", "values": [20, 45, 70, 100]}, {"id": 33, "group": "Disiplin", "kind": "Yin", "gear": ["Eldiven"], "effect": "Azami MP içinden ayrılan pay (%)", "values": [15, 30, 45, 60]}, {"id": 34, "group": "Disiplin", "kind": "Yin", "gear": ["Eldiven"], "effect": "Azami HP içinden ayrılan pay (%)", "values": [10, 20, 30, 50]}, {"id": 35, "group": "Disiplin", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "Gelen hasarın MP üzerinden karşılanan payı (%)", "values": [2, 4, 6, 10]}, {"id": 36, "group": "Disiplin", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "MP en fazla %40 iken beceri hasarı artışı (%)", "values": [1, 3, 5, 8]}, {"id": 37, "group": "Disiplin", "kind": "Yang", "gear": ["Silah", "Kask", "Zırh", "Ayakkabı"], "effect": "HP en fazla %50 iken beceri hasarı artışı (%)", "values": [1, 3, 5, 8]}]; const SLOTS = ['Silah','Eldiven','Kask','Zırh','Ayakkabı']; const COLORS = {'Ayin':'#39b7f4','Muska':'#b39aee','Güç Darbesi':'#f29538','Kan Öfkesi':'#ee7373','Diriliş':'#58cbb4','Disiplin':'#e5bc55'}; const PRESETS = [
['Back in the Game',[[10,10],[28,10],[18,10],[13,10],[16,10]]], ['Fluchbringer',[[10,10],[15,10],[18,10],[13,10],[16,10]]], ['Mana Cannon 1',[[0,10],[33,10],[7,10],[4,10],[36,10]]], ['Mana Cannon 2',[[0,10],[33,10],[36,10],[4,10],[35,10]]], ['Hit',[[10,10],[33,1],[18,10],[16,10],[35,10]]], ['Shield & Smash',[[19,10],[33,1],[35,10],[22,10],[23,10]]], ['PvM / PvP',[[10,10],[24,10],[18,10],[25,10],[26,10]]], ['Run & Gun',[[0,10],[28,10],[30,10],[5,10],[6,10]]]
]; function value(id,level) { return DATA[id].values[level<=3?0:level<=6?1:level<=9?2:3]; } function normalize(input) { return SLOTS.map((slot,i)=>{const x=Array.isArray(input)&&input[i];return x&&Number.isInteger(x[0])&&DATA[x[0]]&&DATA[x[0]].gear.includes(slot)&&Number.isInteger(x[1])&&x[1]>=1&&x[1]<=10?[x[0],x[1]]:null;}); } function analyze(input) {
const state=normalize(input), yin=state.filter(Boolean).map(x=>DATA[x[0]]).filter(x=>x.kind==='Yin').map(x=>x.group);
const active=[],inactive=[];
state.forEach((x,slot)=>{if(x){const d=DATA[x[0]],entry={id:x[0],level:x[1],slot,...d,value:value(...x)};(d.kind==='Yin'||yin.includes(d.group)?active:inactive).push(entry);}});
const best={};active.forEach(e=>{if(e.kind==='Yang'&&(!best[e.id]||e.value>best[e.id].value))best[e.id]=e;});
active.forEach(e=>{e.duplicate=e.kind==='Yang'&&best[e.id]!==e;});
return {state,active,inactive};
} if(typeof module!=='undefined'&&module.exports)module.exports={DATA,SLOTS,PRESETS,normalize,analyze,value}; if(typeof document==='undefined')return; function boot(root){
if(root.dataset.yyReady)return;root.dataset.yyReady='1';
let state=Array(5).fill(null), selected=0;
try{const raw=new URLSearchParams(location.hash.slice(1)).get('yy');if(raw)state=normalize(JSON.parse(raw));}catch(e){}
const el=(tag,cls,text)=>{const n=document.createElement(tag);if(cls)n.className=cls;if(text!==undefined)n.textContent=text;return n;};
root.replaceChildren();
const header=el('header','yy-head');header.append(el('span','yy-kicker','METİN2 • YIN & YANG'),el('h2',,'Mühür Planlayıcı'),el('p',,'Ekipmanını seç. Mühürleri birleştir. Uyumunu gör.'));root.append(header);
const layout=el('div','yy-layout'),left=el('aside','yy-left'),right=el('div','yy-right');layout.append(left,right);root.append(layout);
const board=el('div','yy-board');left.append(board);
const svg=document.createElementNS('http://www.w3.org/2000/svg','svg');svg.setAttribute('viewBox','0 0 300 320');svg.setAttribute('aria-hidden','true');board.append(svg);
const positions=[[57,105],[243,105],[150,48],[150,176],[150,267]], symbols=['⚔','✋','♜','♜','♟'];
const buttons=SLOTS.map((slot,i)=>{const b=el('button','yy-slot');b.type='button';b.style.left=positions[i][0]/3+'%';b.style.top=positions[i][1]/3.2+'%';b.append(el('span','yy-symbol',symbols[i]),el('span',,slot));b.addEventListener('click',()=>{selected=i;render();});board.append(b);return b;});
left.append(el('p','yy-caption','Renkli çizgiler aynı gruptaki Yin ve Yang bağlantılarını gösterir.'));
const stats=el('div','yy-stats');left.append(stats);
const bar=el('div','yy-toolbar'),preset=el('select'),reset=el('button',,'Sıfırla'),share=el('button',,'Bağlantıyı kopyala');preset.setAttribute('aria-label','Örnek kombinasyon');preset.append(new Option('Örnek kombinasyon seç…',));PRESETS.forEach((p,i)=>preset.append(new Option(p[0],i)));bar.append(preset,reset,share);right.append(bar);
const status=el('p','yy-status');status.setAttribute('role','status');right.append(status);
preset.addEventListener('change',()=>{if(preset.value!==){state=normalize(PRESETS[+preset.value][1]);render();}});
reset.addEventListener('click',()=>{state=Array(5).fill(null);preset.value=;status.textContent='Seçimler temizlendi.';render();});
share.addEventListener('click',async()=>{const url=new URL(location.href);url.hash='yy='+encodeURIComponent(JSON.stringify(state));try{await navigator.clipboard.writeText(url.href);status.textContent='Kombinasyon bağlantısı kopyalandı.';}catch(e){status.textContent='Bağlantı: '+url.href;}if(location.protocol==='file:')status.textContent='Yerel önizleme: paylaşılabilir bağlantı Wiki’ye kurulduğunda kullanılabilir.';});
const editor=el('section','yy-editor'),result=el('section','yy-results');result.setAttribute('aria-live','polite');right.append(editor,result);
function render(){
const a=analyze(state);svg.replaceChildren();
a.active.filter(e=>e.kind==='Yang').forEach(e=>{const y=a.active.find(v=>v.kind==='Yin'&&v.group===e.group);const path=document.createElementNS(svg.namespaceURI,'path');path.setAttribute('d',`M ${positions[y.slot][0]} ${positions[y.slot][1]} L ${positions[e.slot][0]} ${positions[y.slot][1]} L ${positions[e.slot][0]} ${positions[e.slot][1]}`);path.setAttribute('stroke',COLORS[e.group]);path.setAttribute('fill','none');path.setAttribute('stroke-width',e.duplicate?'2':'4');if(e.duplicate)path.setAttribute('stroke-dasharray','5 4');svg.append(path);});
buttons.forEach((b,i)=>{b.classList.toggle('yy-selected',i===selected);b.setAttribute('aria-pressed',String(i===selected));b.style.borderColor=state[i]?COLORS[DATA[state[i][0]].group]:;b.title=state[i]?DATA[state[i][0]].group+' · '+DATA[state[i][0]].kind+' · Sv. '+state[i][1]:SLOTS[i]+' — boş';});
stats.textContent=`${state.filter(Boolean).length}/5 ekipman · ${a.active.filter(e=>!e.duplicate).length} uyumlu mühür · ${a.inactive.length} bağlantısız`;
editor.replaceChildren(el('h3',,SLOTS[selected]),el('p','yy-muted',selected<2?'Bu ekipmana bir Yin veya bir Yang mührü seçebilirsin.':'Bu ekipmana yalnızca uygun Yang mühürleri seçilebilir.'));
const form=el('div','yy-fields'),label=el('label',,'Mühür'),seal=el('select'),levelLabel=el('label',,'Seviye'),level=el('select');seal.setAttribute('aria-label',SLOTS[selected]+' mührü');level.setAttribute('aria-label',SLOTS[selected]+' seviyesi');seal.append(new Option('Mühür yok',));
['Yin','Yang'].forEach(kind=>{Object.keys(COLORS).forEach(group=>{const opts=DATA.filter(d=>d.kind===kind&&d.group===group&&d.gear.includes(SLOTS[selected]));if(opts.length){const og=el('optgroup');og.label=kind+' · '+group;opts.forEach(d=>og.append(new Option(d.effect,d.id)));seal.append(og);}});});
for(let l=1;l<=10;l++)level.append(new Option('Seviye '+l,l));seal.value=state[selected]?String(state[selected][0]):;level.value=state[selected]?state[selected][1]:10;level.disabled=!state[selected];label.append(seal);levelLabel.append(level);form.append(label,levelLabel);editor.append(form);
if(state[selected]){const d=DATA[state[selected][0]];editor.append(el('p','yy-value',d.group+' · '+d.kind+' | Seçilen değer: '+value(...state[selected])));}
seal.addEventListener('change',()=>{state[selected]=seal.value===?null:[+seal.value,+level.value];preset.value=;render();});level.addEventListener('change',()=>{if(state[selected])state[selected][1]=+level.value;preset.value=;render();});
result.replaceChildren(el('h3',,'Uyumlu mühürler'));
if(!a.active.length)result.append(el('p','yy-empty','Silah veya eldivene Yin seçerek başla. Aynı gruptaki Yang mühürlerini diğer ekipmanlarla bağla.'));
Object.keys(COLORS).forEach(group=>{const entries=a.active.filter(e=>e.group===group);if(!entries.length)return;const card=el('article','yy-card');card.style.borderLeftColor=COLORS[group];card.append(el('h4',,group));entries.forEach(e=>{const row=el('div','yy-effect'+(e.duplicate?' yy-dim':));row.append(el('span','yy-badge',e.kind),el('span','yy-meta',SLOTS[e.slot]+' · Seviye '+e.level),el('p',,e.effect+' → '+e.value+(e.duplicate?' — yinelenen etki; eklenmez.':)));card.append(row);});result.append(card);});
if(a.inactive.length){const box=el('div','yy-warning');box.append(el('strong',,'Bağlantısız Yang mühürleri'));a.inactive.forEach(e=>box.append(el('p',,SLOTS[e.slot]+' · '+e.group+' — bu grupta Yin seçilmedi.')));result.append(box);}
if(a.active.some(e=>[12,13].includes(e.id))&&!a.active.some(e=>e.id===11))result.append(el('p','yy-warning','Bağlı canavar koşulu var, fakat bağlama mührü seçilmedi. Etkinin koşulu bu dağılım tarafından sağlanmıyor.'));
if(a.active.some(e=>e.id===21)&&!a.active.some(e=>e.id===20))result.append(el('p','yy-warning','Çift EXP etkisi binicilik tetiklemesi ister; binicilik etkinleştirme mührü seçilmedi.'));
if(a.active.some(e=>e.id===28))result.append(el('p','yy-warning','Diriliş bekleme değeri kaynakta 8 / 6 / 4 / 1 sn. Açıklamanın azalma miktarı mı kalan süre mi olduğu doğrulanmalıdır.'));
}
const foot=el('footer','yy-foot');foot.textContent='Uyum, tetiklenme garantisi değildir. Etkilerin koşulları geçerlidir; aynı bonuslar toplanmaz. Veriler: M2-Helper, 6 Kasım 2026. Bağımsız uygulamadır.';root.append(foot);render();
} function start(){document.querySelectorAll('.yy-planner').forEach(boot);} if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start);else start(); if(typeof mw!=='undefined'&&mw.hook)mw.hook('wikipage.content').add(start); })();
