diff --git a/index.html b/index.html index deddf23..34c82ab 100644 --- a/index.html +++ b/index.html @@ -233,6 +233,9 @@ font-size:12px;max-width:330px;border-left:4px solid var(--green);animation:tin .25s ease;} .toast.warn{border-left-color:var(--orange);} .toast.bad{border-left-color:var(--red);} .toast.info{border-left-color:var(--blue);} .toast.prestige{border-left-color:#8a2fb0;} + .toast.drop-common{border-left-color:#8a8a8a;} .toast.drop-uncommon{border-left-color:#4f9e3a;} + .toast.drop-rare{border-left-color:#2f7fd0;} .toast.drop-epic{border-left-color:#8a2fb0;} + .toast.drop-legendary{border-left-color:#d98a1f;box-shadow:0 4px 14px rgba(0,0,0,.35),0 0 0 1px #d98a1f;} .toast b{display:block;margin-bottom:2px;} @keyframes tin{from{transform:translateX(40px);opacity:0;}to{transform:none;opacity:1;}} @@ -240,6 +243,7 @@ #event-banner{display:none;align-items:center;gap:10px;padding:4px 12px;font-weight:600;font-size:11px;flex:0 0 auto;} #event-banner.burst{display:flex;background:linear-gradient(#dff5d0,#cdeeb6);color:#2c5e16;border-bottom:1px solid #9fce7e;} #event-banner.throttle{display:flex;background:linear-gradient(#ffe0d6,#ffcbbb);color:#8a3115;border-bottom:1px solid #e7a78f;} + #event-banner.buff{display:flex;background:linear-gradient(#efe1f7,#e2cdf0);color:#5e1d7e;border-bottom:1px solid #c79fe0;} .scroller{font-family:var(--mono);} ::-webkit-scrollbar{width:14px;height:14px;} ::-webkit-scrollbar-thumb{background:#c3c3c3;border:3px solid var(--bg);border-radius:8px;} @@ -297,7 +301,7 @@
- +
Data0 B
@@ -325,6 +329,7 @@
General
+
Bonuses
Trackers
Peers
IRC / Swarm
@@ -542,6 +547,11 @@ function freshState(){ dtab:'general', sci:false, seenCatalog:{}, // catalog ids the player has seen in the Add dialog + // download loot β€” completed downloads occasionally "contain" bonuses (reset on prestige) + bonusPct:{down:0, income:0, seeds:0}, // accumulated permanent % bonuses per stat + buffs:[], // active timed buffs {stat,mult,until,name,rarity} + dropLog:[], // recent drop history + drops:0, // lifetime-this-run drop count trackerList:defaultTrackers(), // announced trackers, each {url,seeds,peers,status} rssFilter:'*', // glob filter for RSS auto-download rssOn:true, // auto-download matching releases when rss is unlocked @@ -565,11 +575,18 @@ const hasPerkUnlock = id => (G.perks[id]||0)>0; function perkMult(id){ const p=PERK_BY_ID[id]; return Math.pow(p.mult, plvl(id)); } +// download-loot multipliers: permanent % bonuses + active timed buffs, per stat (down/income/seeds) +function permMult(stat){ return 1 + ((G.bonusPct&&G.bonusPct[stat])||0)/100; } +// strongest active buff per stat (no same-stat stacking β†’ no runaway); different stats still combine +function buffMult(stat){ let m=1; const n=now(); for(const b of (G.buffs||[])){ if(b.stat===stat && b.until>n && b.mult>m) m=b.mult; } return m; } +function bonusMult(stat){ return permMult(stat)*buffMult(stat); } + function downMult(){ let m=1; m*=Math.pow(UP_BY_ID.bandwidth.mult, lvl('bandwidth')); m*=Math.pow(UP_BY_ID.overclock.mult, lvl('overclock')); m*=perkMult('dpi'); + m*=bonusMult('down'); // download-loot bonus if(now()= c.size){ t.downloaded = c.size; t.status='seeding'; t.completedAt=now(); toast('info','Download complete', t.name.slice(0,40)+'
Now seeding for '+fmtSpd(torrentUpSpeed(t))); + rollLoot(t); // the completed download may have "contained" a bonus } } else if(t.status==='seeding'){ const sp=torrentUpSpeed(t); @@ -711,6 +731,76 @@ function handleEvents(){ function rand(a,b){ return a + Math.random()*(b-a); } +/* ---- download loot / drops ------------------------------------------------ + Every COMPLETED download rolls for a bonus it "contained": + - occasional PERMANENT % bonus to a stat (rarer = bigger), or + - much rarer, a TIMED multiplier buff ("better server access"). Bigger torrents + drop bigger bonuses (tierFactor). All loot resets on prestige. */ +const DROP = { timedChance:0.05, permChance:0.34 }; // per completion +const RARITY = { + Common: {c:'#8a8a8a', g:'β€’'}, + Uncommon: {c:'#4f9e3a', g:'β—†'}, + Rare: {c:'#2f7fd0', g:'β˜…'}, + Epic: {c:'#8a2fb0', g:'✦'}, + Legendary: {c:'#d98a1f', g:'✸'}, +}; +const STAT_LABEL = { down:'Download Speed', income:'Seed Income', seeds:'Connected Seeds' }; +const STAT_ICON = { down:'⬇️', income:'⬆️', seeds:'🌱' }; +function tierFactor(c){ + switch(c.req){ + case 'singularity': return 2.4; case 'darknet': return 2.0; case 'fiber': return 1.6; + case 'private': return 1.3; case 'cat6': return 1.1; default: return 1.0; + } +} +// permanent % bonus β€” small common, big rare (the rarity curve) +function rollPermBonus(c){ + const r=Math.random(); let pct, rarity; + if(r<0.56){ pct=rand(1,3); rarity='Common'; } + else if(r<0.83){ pct=rand(4,7); rarity='Uncommon'; } + else if(r<0.95){ pct=rand(8,15); rarity='Rare'; } + else if(r<0.99){ pct=rand(16,28); rarity='Epic'; } + else { pct=rand(30,55); rarity='Legendary'; } + pct *= tierFactor(c); + const sr=Math.random(); const stat = sr<0.40?'income' : sr<0.72?'down' : 'seeds'; + return { stat, pct:+pct.toFixed(1), rarity }; +} +// timed buff β€” rarer, multipliers from better server access +const BUFF_TABLE = [ + {p:0.50, stat:'seeds', mult:2, dur:30, name:'Private Tracker Invite', rarity:'Uncommon'}, + {p:0.28, stat:'seeds', mult:3, dur:45, name:'Seedbox Trial Access', rarity:'Rare'}, + {p:0.15, stat:'down', mult:5, dur:45, name:'Fiber Burst', rarity:'Epic'}, + {p:0.07, stat:'income', mult:10, dur:60, name:'0-Day Scene Access', rarity:'Legendary'}, +]; +function rollTimedBuff(){ let r=Math.random(),a=0; for(const b of BUFF_TABLE){ a+=b.p; if(r40) G.dropLog.pop(); + if(!silent) toast('drop-'+b.rarity.toLowerCase(), RARITY[b.rarity].g+' '+b.rarity+' β€” +'+b.pct+'% '+STAT_LABEL[b.stat], + 'contained in '+CAT_BY_ID[t.cid].name.slice(0,34)); +} +function grantTimedBuff(t, b){ + G.buffs.push({ stat:b.stat, mult:b.mult, until:now()+b.dur*1000, name:b.name, rarity:b.rarity }); + G.drops = (G.drops||0)+1; + G.dropLog.unshift({ kind:'buff', name:CAT_BY_ID[t.cid].name, stat:b.stat, mult:b.mult, dur:b.dur, rarity:b.rarity, label:b.name }); + if(G.dropLog.length>40) G.dropLog.pop(); + toast('drop-'+b.rarity.toLowerCase(), '⚑ '+b.name+' β€” Γ—'+b.mult+' '+STAT_LABEL[b.stat]+' for '+b.dur+'s', + 'unpacked from '+CAT_BY_ID[t.cid].name.slice(0,30)); +} +function pruneBuffs(){ const n=now(); if(G.buffs.some(b=>b.until<=n)) G.buffs=G.buffs.filter(b=>b.until>n); } + // glob like *.upg or *REPACK* -> case-insensitive regex function globToRe(glob){ if(!glob||glob==='*'||glob.trim()==='') return /.*/i; @@ -888,6 +978,7 @@ function doPrestige(){ // reset run state β€” abandon local hardware & trackers, keep credits/perks G.data=0; G.runUp=0; G.torrents=[]; G.upgrades={}; G.sel=null; G.trackerList=defaultTrackers(); + G.bonusPct={down:0,income:0,seeds:0}; G.buffs=[]; G.dropLog=[]; G.drops=0; // loot resets on migration G.burstUntil=0;G.nextBurst=0;G.throttleUntil=0;G.nextThrottle=0;G.nextMagnet=0; toast('prestige','πŸ›°οΈ Migration Complete','Local hardware scrubbed. Redeployed onto an encrypted Virtual Seedbox Cluster.
Earned '+gain+' Crypto-Credits.'); ircSys('--- ISP throttle evaded. migrated to decentralized seedbox cluster. new node online ---'); @@ -1097,8 +1188,10 @@ function renderStatus(){ `${dl} downloading Β· ${sd} seeding Β· earning ${fmtSpd(totalUpSpeed())} Data`; // event banner const eb=document.getElementById('event-banner'); - if(now()b.until>now()); + if(now()1?' Β· +'+(aBuffs.length-1)+' more':''); } else { eb.className=''; eb.style.display='none'; } } @@ -1107,17 +1200,40 @@ function renderDetail(){ document.querySelectorAll('.dtab').forEach(d=>d.classList.toggle('active',d.dataset.dtab===G.dtab)); const body=document.getElementById('detail-body'); const t=G.torrents.find(x=>x.uid===G.sel); - if(!t && G.dtab!=='irc' && G.dtab!=='trackers'){ + if(!t && G.dtab!=='irc' && G.dtab!=='trackers' && G.dtab!=='bonuses'){ body.innerHTML='
Select a torrent to see its details.
'; if(G.dtab==='speed') drawSpeed(); return; } if(G.dtab==='general'){ body.innerHTML=detGeneral(t); } + else if(G.dtab==='bonuses'){ body.innerHTML=detBonuses(); } else if(G.dtab==='trackers'){ body.innerHTML=detTrackers(t); } else if(G.dtab==='peers'){ body.innerHTML=detPeers(t); } else if(G.dtab==='irc'){ body.innerHTML=detIrc(t); const log=document.getElementById('irc-log'); if(log) log.scrollTop=log.scrollHeight; } else if(G.dtab==='speed'){ body.innerHTML=detSpeed(t); drawSpeed(); } } +function detBonuses(){ + const n=now(); + const rows=['down','income','seeds'].map(s=>{ + const perm=G.bonusPct[s]||0, bf=buffMult(s), tot=permMult(s)*bf; + return `
${STAT_ICON[s]} ${STAT_LABEL[s]}
+${perm.toFixed(1)}% permanent${bf>1?` Β· Γ—${bf.toFixed(2)} buff`:''} β†’ Γ—${tot.toFixed(2)}
`; + }).join(''); + const active=G.buffs.filter(b=>b.until>n); + const buffHtml = active.length ? active.map(b=>{ + const left=Math.max(0,(b.until-n)/1000); + return `
${b.name} β€” Γ—${b.mult} ${STAT_LABEL[b.stat]}${left.toFixed(0)}s
`; + }).join('') : '
None active. A rare few downloads contain temporary server-access multipliers.
'; + const log = G.dropLog.slice(0,16).map(d=>{ + const col=RARITY[d.rarity]?RARITY[d.rarity].c:'#888'; + if(d.kind==='buff') return `
⚑ ${d.label} Γ—${d.mult} ${STAT_LABEL[d.stat]} β€” ${d.name.slice(0,30)}
`; + return `
${(RARITY[d.rarity]||{}).g||'β€’'} ${d.rarity} +${d.pct}% ${STAT_LABEL[d.stat]} β€” ${d.name.slice(0,30)}
`; + }).join('') || '
No drops yet β€” complete downloads to find bonuses.
'; + return `
Completed downloads occasionally contain bonuses β€” small % boosts are common, big ones rare; a rare few hold temporary multipliers. ${G.drops} found this run (resets on Migration).
+
${rows}
+
⚑ Active Timed Buffs
${buffHtml} +
Recent Drops
+
${log}
`; +} function detTrackers(t){ const totSeeds=trackerSeedBonus(); const cost=announceCost(); @@ -1381,7 +1497,7 @@ function renderStoreModal(){ const ups=UPGRADES.filter(u=>u.tab===storeTab); const cards=ups.map(u=>upgradeCard(u)).join(''); openModal(` - +
${tabHtml}