'use strict'; /* ================= util ================= */ const U = { rnd:(a,b)=>a+Math.random()*(b-a), ri:(a,b)=>Math.floor(a+Math.random()*(b-a+1)), pick:a=>a[Math.floor(Math.random()*a.length)], pickW:(items,wfn)=>{ // weighted pick let tot=0; const ws=items.map(it=>{const w=Math.max(0.0001,wfn(it)); tot+=w; return w;}); let r=Math.random()*tot; for(let i=0;iMath.random(){let u=0,v=0;while(!u)u=Math.random();while(!v)v=Math.random();return m+s*Math.sqrt(-2*Math.log(u))*Math.cos(2*Math.PI*v);}, clamp:(v,a,b)=>Math.max(a,Math.min(b,v)), lerp:(a,b,t)=>a+(b-a)*t, sum:arr=>arr.reduce((s,x)=>s+x,0), avg:arr=>arr.length?U.sum(arr)/arr.length:0, shuffle:a=>{for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1));[a[i],a[j]]=[a[j],a[i]];}return a;}, esc:s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])), uid:()=> 'x'+Math.random().toString(36).slice(2,9)+Date.now().toString(36).slice(-4), fmtM(n){ if(n===Infinity||n===-Infinity) return '∞'; const neg=n<0; n=Math.abs(Math.round(n)); let s; if(n>=1e9) s=(n/1e9).toFixed(2)+'B'; else if(n>=1e6) s=(n/1e6).toFixed(n>=1e7?1:2)+'M'; else if(n>=1e3) s=(n/1e3).toFixed(0)+'K'; else s=String(n); return (neg?'-$':'$')+s; }, fmtNum(n){return Math.round(n).toLocaleString('en-US');}, /* day 0 = Mon 5 Jan 2026 */ dateFromDay(d){const dt=new Date(Date.UTC(2026,0,5)); dt.setUTCDate(dt.getUTCDate()+d); return dt;}, fmtDate(d){const dt=U.dateFromDay(d); return dt.toLocaleDateString('en-GB',{day:'numeric',month:'short',year:'numeric',timeZone:'UTC'});}, fmtDateShort(d){const dt=U.dateFromDay(d);return dt.toLocaleDateString('en-GB',{day:'numeric',month:'short',timeZone:'UTC'});}, dow(d){return U.dateFromDay(d).getUTCDay();}, // 0 sun .. 6 sat weekOfYear(d){return Math.floor((d+4)/7);}, logistic(x){return 1/(1+Math.exp(-x));}, }; /* role weights for overall rating (attrs: aim,pos,clutch,util,iq,move,comm) */ const ROLE_W={ IGL: {iq:.32,comm:.22,util:.14,pos:.12,aim:.08,clutch:.04,move:.08}, AWP: {aim:.38,pos:.18,iq:.13,clutch:.10,move:.10,comm:.07,util:.04}, Entry: {aim:.33,move:.20,pos:.12,clutch:.09,iq:.10,util:.08,comm:.08}, Rifler:{aim:.26,iq:.16,pos:.16,move:.12,clutch:.10,util:.10,comm:.10}, Support:{util:.28,iq:.20,comm:.17,pos:.15,aim:.10,move:.06,clutch:.04}, Lurker:{pos:.26,iq:.22,aim:.16,clutch:.14,move:.10,util:.06,comm:.06}, }; const ROLES=Object.keys(ROLE_W); function playerOverall(p){ const w=ROLE_W[p.role]||ROLE_W.Rifler; let s=0; for(const k in w) s+=p.attrs[k]*w[k]; return s; } /* market value & wage model. attrs 40..99 scale */ function playerValue(p,G){ const ovr=playerOverall(p), potGap=p.potential-ovr; const ageF = p.age<=19?1.45 : p.age<=22?1.35 : p.age<=25?1.2 : p.age<=28?1.0 : p.age<=31?0.72 : 0.42; let v = Math.pow(Math.max(ovr-40,1),2.75) * 14 * ageF; v *= (1 + Math.max(potGap,0)*0.022); v *= (0.75 + p.form*0.05); // form 0..10 v *= (0.85 + p.morale/100*0.3); const yrsLeft = Math.max(0,(p.contractUntil-G.day)/364); if(yrsLeft<0.5 && G.players[p.teamId]!==undefined) v*=0.65; // expiring contract discount if(p.injuryDays>7) v*=0.85; return Math.round(v/1000)*1000; } function wageDemand(p,G){ const ovr=playerOverall(p); let base = Math.pow(Math.max(ovr-40,1),2.6)*0.75; // monthly usd base *= (0.8 + p.form*0.045); base *= (1 + (p.potential-ovr)*0.008); const yrsLeft=Math.max(0,(p.contractUntil-G.day)/364); if(yrsLeft<0.5) base*=1.25; base *= G.diff.wageMul; return Math.round(base/500)*500; } const NATIONS={ // flag emoji UA:'🇺🇦',RU:'🇷🇺',DK:'🇩🇰',LV:'🇱🇻',IL:'🇮🇱',DE:'🇩🇪',FR:'🇫🇷',GB:'🇬🇧',PL:'🇵🇱',CZ:'🇨🇿', SE:'🇸🇪',NO:'🇳🇴',FI:'🇫🇮',EE:'🇪🇪',LT:'🇱🇹',NL:'🇳🇱',BG:'🇧🇬',RO:'🇷🇴',RS:'🇷🇸', US:'🇺🇸',CA:'🇨🇦',BR:'🇧🇷',AR:'🇦🇷',UY:'🇺🇾',MX:'🇲🇽', TR:'🇹🇷',KZ:'🇰🇿',MN:'🇲🇳',CN:'🇨🇳',KR:'🇰🇷',JP:'🇯🇵', AU:'🇦🇺',NZ:'🇳🇿',ES:'🇪🇸',PT:'🇵🇹',IT:'🇮🇹',BA:'🇧🇦',MK:'🇲🇰',GR:'🇬🇷',SA:'🇸🇦',MA:'🇲🇦',ID:'🇮🇩' }; const FLAG=n=>NATIONS[n]||'🏳️'; function faceFor(m){ // morale emoji if(m>=80)return '😄'; if(m>=60)return '🙂'; if(m>=40)return '😐'; if(m>=20)return '🙁'; return '😡'; } function formStars(f){ // f 0..10 → stars display const s=Math.round(f/2); return '★'.repeat(s)+'☆'.repeat(5-s); } function ovrClass(o){return o>=90?'s90':o>=80?'s80':'';}