// ==UserScript== // @name Grepolis Town Stats Extended Clean (with Sea, Buildings, Researches) // @namespace http://tampermonkey.net/ // @version 1.4 // @description Sends town stats with coords, sea, full building orders, and researches // @author Dimitrios // @match https://*.grepolis.com/game/* // @grant unsafeWindow // ==/UserScript== (function () { 'use strict'; const uw = unsafeWindow || window; let paused = false; console.log("πŸš€ Town Stats Extended Clean (with Sea, Buildings, Researches) loaded"); // ----------------------------- // Toolbar pause/resume button // ----------------------------- const toolbarButtonHtml = `

TownScan

`; function togglePause() { paused = !paused; const label = document.getElementById('tslabel'); const btn = document.getElementById('tsbutton'); if (paused) { label.textContent = 'Paused'; btn.style.filter = 'brightness(70%) sepia(100%) hue-rotate(-50deg) saturate(1000%) contrast(0.8)'; } else { label.textContent = 'TownScan'; btn.style.filter = 'brightness(294%) sepia(100%) hue-rotate(15deg) saturate(1000%) contrast(0.8)'; } console.log(`πŸ”˜ TownScan is now ${paused ? 'paused' : 'running'}`); } setTimeout(() => { if (!document.getElementById('tsbutton')) { uw.$('.tb_activities, .toolbar_activities').find('.middle').append(toolbarButtonHtml); } }, 4000); uw.$(document).on('click', '#tsbutton', togglePause); // ----------------------------- // Compute Sea from x,y // ----------------------------- function computeSea(x, y) { if (typeof x !== 'number' || typeof y !== 'number') return null; const sx = Math.floor(x / 100); // col (0-based) const sy = Math.floor(y / 100); // row (0-based) return sx * 10 + sy; } // ----------------------------- // Gather Town Stats // ----------------------------- function gatherTownStats() { const towns = uw.ITowns?.towns || {}; const player = uw.Game?.player_name || "unknown"; const player_id = uw.Game?.player_id || "unknown"; const townStats = Object.values(towns).map(town => { const res = town.resources(); const buildings = town.buildings()?.attributes ?? {}; // Units fix const unitsObj = {}; const units = town.units(); if (units) { Object.keys(units).forEach(type => { if (typeof units[type] === 'number') { unitsObj[type] = units[type]; } else if (typeof units[type]?.getAmount === 'function') { unitsObj[type] = units[type].getAmount(); } else { unitsObj[type] = 0; } }); } // Coordinates & sea const x = town.getIslandCoordinateX ? town.getIslandCoordinateX() : null; const y = town.getIslandCoordinateY ? town.getIslandCoordinateY() : null; const sea = (x !== null && y !== null) ? computeSea(x, y) : null; // Full building orders let buildingOrder = []; if (typeof town.buildingOrders === "function") { const bo = town.buildingOrders(); if (bo && bo.models) { buildingOrder = bo.models.map(m => m.attributes); } } // Researches let researches = {}; if (typeof town.researches === "function") { researches = town.researches(); } return { town_id: town.id, town_name: town.name, x, y, sea, wood: res.wood, stone: res.stone, iron: res.iron, population: res.population, points: town.points || 0, buildings, units: unitsObj, buildingOrder, researches, has_premium: town.hasPremium?.() || false, bonuses: town.getBonus ? town.getBonus() : {}, wonder_points: town.wonder_points || 0 }; }); return { player, player_id, towns: townStats }; } // ----------------------------- // Send Data // ----------------------------- function sendData() { if (paused) return console.log("⏸️ TownScan is paused β€” skipping data send"); try { const payload = { type: "extended", timestamp: new Date().toISOString(), ...gatherTownStats(), server_received_at: new Date().toISOString() }; console.log("πŸ“€ Sending payload:", payload); fetch("https://grepo.haunter-pets.top/api/grepolis-data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) }) .then(res => res.text()) .then(txt => console.log("βœ… Data sent:", txt)) .catch(err => console.error("❌ Failed to send:", err)); } catch (e) { console.error("πŸ’₯ Error sending stats:", e); } } // ----------------------------- // Main Loop // ----------------------------- function startCycle() { setInterval(() => { console.log("πŸ•’ TownScan cycle tick"); sendData(); }, 30000); } window.addEventListener("load", () => { console.log("πŸš€ TownScan booting..."); startCycle(); }); })();