Torn display + abroad stock with flight selection, restock timer. Collapsible, color-coded abroad, lowest items based on display.
// ==UserScript==
// @name 💫 Points Maker (Optimized + Abroad Color + Flight & Restock)
// @namespace http://tampermonkey.net/
// @version 1.2.7
// @description Torn display + abroad stock with flight selection, restock timer. Collapsible, color-coded abroad, lowest items based on display.
// @match https://www.torn.com/*
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
// CONFIG
const PANEL_ID = 'points_maker_pda';
const POLL_INTERVAL_MS = 45 * 1000;
const SPECIAL_DRUG = 'Xanax';
const MAX_ABROAD = { flowers: 50, plushies: 50, drugs: 50 };
// Restock times (minutes)
const RESTOCK_TIMES = {
Camel: 22, Panda: 18, Peony: 112, Tribulus: 107, Lion: 19, Violet: 98,
Jaguar: 27, Cherry: 121, Monkey: 23, Heather: 129, Ceibo: 129, RedFox: 28,
Nessie: 23, Stingray: 22, Banana: 95, Orchid: 118, Crocus: 114, Chaimos: 21,
Dahlia: 113, Edelweiss: 100
};
// Flight times
const FLIGHT_TIMES = {
Standard: { Mexico: 26, Cayman: 35, Canada: 41, Hawaii: 134, UK: 159, Argentina: 167, Switzerland: 175, Japan: 225, China: 242, UAE: 271, SouthAfrica: 297 },
Airstrip: { Mexico: 18, Cayman: 25, Canada: 29, Hawaii: 94, UK: 111, Argentina: 117, Switzerland: 123, Japan: 158, China: 169, UAE: 190, SouthAfrica: 208 },
WLT: { Mexico: 13, Cayman: 18, Canada: 20, Hawaii: 67, UK: 80, Argentina: 83, Switzerland: 88, Japan: 113, China: 121, UAE: 135, SouthAfrica: 149 },
Business: { Mexico: 8, Cayman: 11, Canada: 12, Hawaii: 40, UK: 48, Argentina: 50, Switzerland: 53, Japan: 68, China: 72, UAE: 81, SouthAfrica: 89 }
};
let selectedFlightType = GM_getValue('selectedFlightType', 'Standard');
// DATA
const FLOWERS = {
"Dahlia": { short: "Dahlia", loc: "MX 🇲🇽", country: "Mexico" },
"Orchid": { short: "Orchid", loc: "HW 🏝️", country: "Hawaii" },
"African Violet": { short: "Violet", loc: "SA 🇿🇦", country: "South Africa" },
"Cherry Blossom": { short: "Cherry", loc: "JP 🇯🇵", country: "Japan" },
"Peony": { short: "Peony", loc: "CN 🇨🇳", country: "China" },
"Ceibo Flower": { short: "Ceibo", loc: "AR 🇦🇷", country: "Argentina" },
"Edelweiss": { short: "Edelweiss", loc: "CH 🇨🇭", country: "Switzerland" },
"Crocus": { short: "Crocus", loc: "CA 🇨🇦", country: "Canada" },
"Heather": { short: "Heather", loc: "UK 🇬🇧", country: "United Kingdom" },
"Tribulus Omanense": { short: "Tribulus", loc: "AE 🇦🇪", country: "UAE" },
"Banana Orchid": { short: "Banana", loc: "KY 🇰🇾", country: "Cayman Islands" }
};
const PLUSHIES = {
"Sheep Plushie": { short: "Sheep", loc: "B.B 🏪", country: "Torn City" },
"Teddy Bear Plushie": { short: "Teddy", loc: "B.B 🏪", country: "Torn City" },
"Kitten Plushie": { short: "Kitten", loc: "B.B 🏪", country: "Torn City" },
"Jaguar Plushie": { short: "Jaguar", loc: "MX 🇲🇽", country: "Mexico" },
"Wolverine Plushie": { short: "Wolverine", loc: "CA 🇨🇦", country: "Canada" },
"Nessie Plushie": { short: "Nessie", loc: "UK 🇬🇧", country: "United Kingdom" },
"Red Fox Plushie": { short: "RedFox", loc: "UK 🇬🇧", country: "United Kingdom" },
"Monkey Plushie": { short: "Monkey", loc: "AR 🇦🇷", country: "Argentina" },
"Chamois Plushie": { short: "Chaimos", loc: "CH 🇨🇭", country: "Switzerland" },
"Panda Plushie": { short: "Panda", loc: "CN 🇨🇳", country: "China" },
"Lion Plushie": { short: "Lion", loc: "SA 🇿🇦", country: "South Africa" },
"Camel Plushie": { short: "Camel", loc: "AE 🇦🇪", country: "UAE" },
"Stingray Plushie": { short: "Stingray", loc: "KY 🇰🇾", country: "Cayman Islands" }
};
// UTILS
function escapeHtml(s){ if(!s) return ''; return String(s).replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[m])); }
function colorForPercent(value,max){ if(!max||max===0) return '#bdbdbd'; const pct=(value/max)*100; if(pct>=75) return '#00c853'; if(pct>=40) return '#3399ff'; return '#ff1744'; }
function stockClassByQty(q){ q=Number(q||0); if(q===0) return 'stock-red'; if(q>=1500) return 'stock-green'; if(q>=321&&q<=749) return 'stock-orange'; return 'stock-gray'; }
// BUILD PANEL
let statusEl, summaryEl, contentEl;
function buildUI(){
if(document.getElementById(PANEL_ID)) return;
const root=document.createElement('div');
root.id=PANEL_ID;
root.innerHTML=`
<div id="tc_header" class="header">▶ 💫 Points Maker</div>
<div id="tc_content_wrapper">
<div id="tc_controls" class="controls">
<select id="flight_type">
<option value="Standard">Standard</option>
<option value="Airstrip">Airstrip</option>
<option value="WLT">WLT</option>
<option value="Business">Business</option>
</select>
<button id="tc_refresh">Refresh</button>
</div>
<div id="tc_status">Waiting...</div>
<div id="tc_summary"></div>
<div id="tc_content"></div>
</div>`;
document.body.appendChild(root);
const headerEl=root.querySelector('#tc_header');
const contentWrapper=root.querySelector('#tc_content_wrapper');
let collapsed=false;
function updateCollapse(){ headerEl.textContent=(collapsed?'▶ ':'▼ ')+'💫 Points Maker'; contentWrapper.style.display=collapsed?'none':'block'; }
updateCollapse();
headerEl.addEventListener('click',()=>{ collapsed=!collapsed; updateCollapse(); });
statusEl=root.querySelector('#tc_status');
summaryEl=root.querySelector('#tc_summary');
contentEl=root.querySelector('#tc_content');
root.querySelector('#tc_refresh').addEventListener('click',()=>refreshAll(true));
root.querySelector('#flight_type').value=selectedFlightType;
root.querySelector('#flight_type').addEventListener('change',e=>{ selectedFlightType=e.target.value; GM_setValue('selectedFlightType',selectedFlightType); refreshAll(true); });
}
buildUI();
// MOCKED FETCH FUNCTIONS (replace with your Torn/Yata/Prom fetch)
async function fetchTornDisplayInventory(){ return {}; }
function countsForReq(itemsAgg, req, mapObj){
const counts={};
req.shortNames.forEach(s=>counts[s]=0);
Object.keys(mapObj).forEach(fn=>{ const s=mapObj[fn].short; counts[s]=(itemsAgg[fn]||0)+counts[s]; });
return counts;
}
function buildHorizontalList(counts, req, mapObj){
let html='';
req.shortNames.forEach(s=>{
const qty=counts[s]||0;
html+=`<div style="display:inline-block;margin-right:10px;"><b>${escapeHtml(s)}:</b> ${qty}</div>`;
});
return html;
}
function formatFlightTimes(itemShort){
const flightData=FLIGHT_TIMES[selectedFlightType];
const restock=RESTOCK_TIMES[itemShort]||9999;
return Object.entries(flightData).map(([country,time])=>{
const mark=(time>=restock)?'✅':'🛬';
return `${country} ${time}min ${mark}`;
}).join(' | ');
}
async function refreshAll(force=false){
if(!contentEl||!summaryEl||!statusEl) return;
statusEl.textContent='Fetching...';
try{
const apiData=await fetchTornDisplayInventory();
const flowerCounts=countsForReq(apiData,{shortNames:Object.values(FLOWERS).map(f=>f.short)},FLOWERS);
const plushCounts=countsForReq(apiData,{shortNames:Object.values(PLUSHIES).map(f=>f.short)},PLUSHIES);
summaryEl.innerHTML=`<div>Total Flowers: ${Object.values(flowerCounts).reduce((a,b)=>a+b,0)}, Total Plushies: ${Object.values(plushCounts).reduce((a,b)=>a+b,0)}</div>`;
let html='';
html+=`<div><b>Flowers:</b> ${buildHorizontalList(flowerCounts,{shortNames:Object.values(FLOWERS).map(f=>f.short)},FLOWERS)}</div>`;
html+=`<div><b>Plushies:</b> ${buildHorizontalList(plushCounts,{shortNames:Object.values(PLUSHIES).map(f=>f.short)},PLUSHIES)}</div>`;
// low stock check
const lowItems=[];
Object.values(FLOWERS).forEach(f=>{
if((flowerCounts[f.short]||0)===0) lowItems.push(`🛫 Low on ${f.short}: ${formatFlightTimes(f.short)}`);
});
Object.values(PLUSHIES).forEach(p=>{
if((plushCounts[p.short]||0)===0) lowItems.push(`🛫 Low on ${p.short}: ${formatFlightTimes(p.short)}`);
});
if(lowItems.length) html+=`<div style="margin-top:5px;color:red;">${lowItems.join(' | ')}</div>`;
contentEl.innerHTML=html;
statusEl.textContent='Updated: '+new Date().toLocaleTimeString();
}catch(e){ console.warn(e); statusEl.textContent='Error'; }
}
refreshAll();
setInterval(refreshAll,POLL_INTERVAL_MS);
})();