commit f5cd057e8aaf75af819911cf2f13d86e2801a29b Author: TheAgain Date: Tue Jul 28 23:33:52 2026 -0230 Main Main files for Loot distribution. requires you to download the items.json from the Albion tools repository diff --git a/app.js b/app.js new file mode 100644 index 0000000..70a39f5 --- /dev/null +++ b/app.js @@ -0,0 +1,125 @@ + +let items=[],players=[],loot=[],selected=null; +const fmt=n=>Number(n).toLocaleString(); + +async function loadDatabase(){ + try{ + const response=await fetch('items.json'); + if(!response.ok) throw new Error('load'); + const raw=await response.json(); + items=raw.map(x=>{ + const name=(x.LocalizedNames&&x.LocalizedNames["EN-US"])||""; + const u=x.UniqueName||""; + const ench=(u.match(/@(\d)/)||[])[1]||"0"; + const tier=(u.match(/T(\d)/)||[])[1]||""; + return {name,label:`${name} (T${tier}.${ench})`}; + }).filter(i=>i.name); + const s=document.getElementById('dbStatus')||document.getElementById('status'); + if(s)s.textContent=''; + search.disabled=false; + }catch(e){ + const s=document.getElementById('dbStatus')||document.getElementById('status'); + if(s)s.textContent='Unable to load items.json'; + } +} +loadDatabase(); + +function renderPlayers(){ + playersDiv=playersEl=document.getElementById("players"); + playersEl.innerHTML=""; + players.forEach((p,i)=>{ + const d=document.createElement("span"); + d.className="player"; + d.innerHTML=`${p} `; + d.querySelector("button").onclick=()=>{players.splice(i,1);renderPlayers();}; + playersEl.appendChild(d); + + }); + updateSummary(); +} +function addPlayerFn(){ + const n=playerName.value.trim(); + if(!n)return; + players.push(n); + playerName.value=""; + renderPlayers(); +} +addPlayer.onclick=addPlayerFn; +playerName.addEventListener("keydown",e=>{if(e.key==="Enter"){e.preventDefault();addPlayerFn();}}); + +search.oninput=()=>{ + const q=search.value.toLowerCase(); + results.innerHTML=""; + if(q.length<2)return; + const groups={}; + items.filter(i=>i.name.toLowerCase().includes(q)).slice(0,80).forEach(i=>(groups[i.name]??=[]).push(i)); + Object.keys(groups).sort().forEach(name=>{ + const g=document.createElement("div");g.className="group"; + g.innerHTML=`
${name}
`; + groups[name].forEach(it=>{ + const c=document.createElement("div");c.className="child";c.textContent=it.label; + c.onclick=()=>{selected=it;selectedSpan.textContent=it.label;search.value=it.name;results.innerHTML="";updatePriceInfo();qty.focus();}; + g.appendChild(c); + }); + results.appendChild(g); + + }); + updateSummary(); +}; + +const priceHistory=JSON.parse(localStorage.getItem("priceHistory")||"{}"); +function fmtAge(ts){if(!ts)return"Never";let m=Math.floor((Date.now()-ts)/60000);if(m<60)return m+"m ago";let h=Math.floor(m/60);if(h<24)return h+"h ago";let d=Math.floor(h/24);h%=24;return d+"d "+h+"h ago";} +function updatePriceInfo(){if(!selected){lastUpdated.textContent="Never";return;}const p=priceHistory[selected.label];if(p){priceEach.value=p.price;updateTotal();lastUpdated.textContent=fmtAge(p.updated);}else lastUpdated.textContent="Never";} + +const selectedSpan=document.getElementById("selected"); +function updateTotal(){totalValue.textContent=fmt((+qty.value||0)*(+priceEach.value||0));} +qty.oninput=updateTotal;priceEach.oninput=updateTotal;updateTotal(); + +addLoot.onclick=()=>{ + if(!selected)return alert("Select an item."); + const q=+qty.value||1,e=+priceEach.value||0,total=q*e; + const existing=loot.find(x=>x.item===selected.label&&x.each===e); + if(existing){existing.qty+=q;existing.total=existing.qty*existing.each;} + else loot.push({item:selected.label,qty:q,each:e,total,keep:keepTogether.checked}); + priceHistory[selected.label]={price:e,updated:Date.now()};localStorage.setItem("priceHistory",JSON.stringify(priceHistory));renderLoot(); + selected=null;selectedSpan.textContent="None";search.value="";qty.value=1;priceEach.value=0;updateTotal(); +}; + +function updateSummary(){let stacks=loot.length,items=0,val=0;loot.forEach(l=>{items+=l.qty;val+=l.total;});sumStacks.textContent=stacks;sumItems.textContent=items;sumValue.textContent=fmt(val);targetShare.textContent=players.length?fmt(Math.round(val/players.length)):0;} + +function renderLoot(){ + const tb=document.querySelector("#loot tbody");tb.innerHTML=""; + loot.forEach((l,i)=>{ + const r=tb.insertRow(); + r.insertCell().textContent=l.item; + r.insertCell().textContent=l.qty; + r.insertCell().textContent=fmt(l.each); + r.insertCell().textContent=fmt(l.total); + r.insertCell().textContent=l.keep?'✓':''; + const b=document.createElement("button");b.textContent="❌";b.onclick=()=>{loot.splice(i,1);renderLoot();}; + r.insertCell().appendChild(b); + + }); + updateSummary(); +} + +distribute.onclick=()=>{ + if(players.length===0)return alert("Add players first."); + let best=null,bestSpread=Infinity; + for(let t=0;t<300;t++){ + let expanded=[];loot.forEach(l=>{if(l.keep){expanded.push({...l});}else{for(let i=0;iMath.random()-0.5).sort((a,b)=>b.total-a.total); + let p=players.map(n=>({name:n,total:0,items:[]})); + work.forEach(it=>{p.sort((a,b)=>a.total-b.total);p[0].items.push(it);p[0].total+=it.total;}); + let vals=p.map(x=>x.total);let spread=Math.max(...vals)-Math.min(...vals); + if(spread{ +const merged={};pl.items.forEach(i=>{const k=i.item+'|'+i.each;if(!merged[k])merged[k]={...i};else{merged[k].qty+=i.qty;merged[k].total+=i.total;}}); +const d=document.createElement("div"); +d.innerHTML=`

${pl.name} — ${fmt(pl.total)} silver

    ${Object.values(merged).map(i=>`
  • ${i.item} ×${i.qty}
  • `).join("")}
`; +distribution.appendChild(d); + + }); + updateSummary(); +}; diff --git a/index.htm b/index.htm new file mode 100644 index 0000000..8c9619a --- /dev/null +++ b/index.htm @@ -0,0 +1,29 @@ + +Tanuki loot distribution + + +

Tanuki loot distribution

+ + +

Players

+ + +
+ +

Loot

+ +
+

Selected: None

+ +
Last Updated: Never
+

Total: 0

+
+ + + +
ItemQtyEachTotal

Total Stacks: 0 | Total Items: 0 | Total Value: 0 | Target Share: 0

+ +
+ +

Distribution

+ \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..787eb0d --- /dev/null +++ b/style.css @@ -0,0 +1,8 @@ +body{font-family:Arial;max-width:1100px;margin:20px} +section{border:1px solid #bbb;border-radius:8px;padding:12px;margin:12px 0} +.player{display:inline-block;background:#e7eefc;padding:6px 10px;margin:4px;border-radius:18px} +.player button{margin-left:8px} +.group{border:1px solid #ccc;margin:4px 0}.title{background:#eee;padding:5px;font-weight:bold} +.child{padding:4px 12px;cursor:pointer}.child:hover{background:#def} +table{width:100%;border-collapse:collapse}th,td{border:1px solid #ccc;padding:6px} +#lastUpdated{font-weight:bold;color:#555}