From ab9e3a80f50029505f874d2453553a27acb9ca45 Mon Sep 17 00:00:00 2001 From: haunter Date: Thu, 7 Aug 2025 20:43:40 +0000 Subject: [PATCH] Add GrepoAutoRuralResources.js --- GrepoAutoRuralResources.js | 168 +++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 GrepoAutoRuralResources.js diff --git a/GrepoAutoRuralResources.js b/GrepoAutoRuralResources.js new file mode 100644 index 0000000..3066308 --- /dev/null +++ b/GrepoAutoRuralResources.js @@ -0,0 +1,168 @@ +// ==UserScript== +// @name GrepoTweaks-AutoRuralResources (Captain-Free Version) +// @namespace sentinelindicator +// @version 2.2.0 +// @description Auto farm loot from rural villages using per-town AJAX requests, no Captain required +// @author Dimitrios +// @match http://*.grepolis.com/game/* +// @match https://*.grepolis.com/game/* +// @grant none +// ==/UserScript== + +(function () { + 'use strict'; + const uw = typeof unsafeWindow !== "undefined" ? unsafeWindow : window; + + const timerInterval = 300000; // 5 minutes + const delta_time = 10000; + let loop, lastTime, timer; + let startOnLogin = true; + + const buttonHtml = + '
' + + '
' + + '

' + + '
'; + + function getPolisList() { + const islandList = []; + const polisList = []; + const towns = uw.MM.getCollections().Town[0].models; + + for (let town of towns) { + const { on_small_island, island_id, id } = town.attributes; + if (on_small_island) continue; + if (!islandList.includes(island_id)) { + islandList.push(island_id); + polisList.push(id); + } + } + return polisList; + } + + function getLootableFarms(town_id) { + const { models: farms } = uw.MM.getOnlyCollectionByName('FarmTown'); + const { models: relations } = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation'); + const town = uw.ITowns.towns[town_id]; + const x = town.getIslandCoordinateX(); + const y = town.getIslandCoordinateY(); + const now = Math.floor(Date.now() / 1000); + const result = []; + + for (let farm of farms) { + if (farm.attributes.island_x !== x || farm.attributes.island_y !== y) continue; + + for (let rel of relations) { + if ( + rel.attributes.farm_town_id === farm.attributes.id && + rel.attributes.relation_status === 1 && + (!rel.attributes.lootable_at || now >= rel.attributes.lootable_at) + ) { + result.push({ town_id, farm_town_id: rel.attributes.farm_town_id, relation_id: rel.id }); + } + } + } + + return result; + } + + function claimSingle(town_id, farm_town_id, relation_id) { + const data = { + model_url: `FarmTownPlayerRelation/${relation_id}`, + action_name: 'claim', + arguments: { farm_town_id: farm_town_id, type: 'resources', option: 1 }, + town_id: town_id, + }; + uw.gpAjax.ajaxPost('frontend_bridge', 'execute', data); + } + + async function claimIndividually(polisList) { + let max = 60; + for (let town_id of polisList) { + const farms = getLootableFarms(town_id); + for (let farm of farms) { + claimSingle(farm.town_id, farm.farm_town_id, farm.relation_id); + await new Promise(r => setTimeout(r, 500)); + if (!--max) return; + } + } + } + + function getNextCollectionTime() { + const models = uw.MM.getCollections().FarmTownPlayerRelation[0].models; + let lootTimes = {}; + + for (let m of models) { + const time = m.attributes.lootable_at; + lootTimes[time] = (lootTimes[time] || 0) + 1; + } + + let max = 0; + let next = 0; + for (let key in lootTimes) { + if (lootTimes[key] > max) { + max = lootTimes[key]; + next = key; + } + } + + const seconds = next - Math.floor(Date.now() / 1000); + return seconds > 0 ? seconds * 1000 : 0; + } + + async function mainLoop() { + const next = getNextCollectionTime(); + if (timer + 2 * delta_time < next) { + timer = next + Math.floor(Math.random() * delta_time); + } + + if (timer < 1) { + const polisList = getPolisList(); + await claimIndividually(polisList); + timer = timerInterval + Math.floor(Math.random() * delta_time); + setTimeout(() => uw.WMap.removeFarmTownLootCooldownIconAndRefreshLootTimers(), 2000); + } + + const now = Date.now(); + timer -= now - lastTime; + lastTime = now; + + const bt = document.getElementById('ptimer'); + bt.innerHTML = timer > 0 ? parseInt(timer / 1000) : '0'; + } + + function handleClick() { + const isCaptainActive = uw.GameDataPremium.isAdvisorActivated('captain'); + + if (!loop) { + timer = getNextCollectionTime() + Math.random() * delta_time; + lastTime = Date.now(); + loop = setInterval(mainLoop, 1000); + + uw.$('#btbutton').css( + 'filter', + isCaptainActive + ? 'brightness(100%) sepia(100%) hue-rotate(90deg) saturate(1500%) contrast(0.8)' + : 'brightness(294%) sepia(100%) hue-rotate(15deg) saturate(1000%) contrast(0.8)' + ); + } else { + clearInterval(loop); + loop = null; + uw.$('#btbutton').css( + 'filter', + 'brightness(70%) sepia(100%) hue-rotate(-50deg) saturate(1000%) contrast(0.8)' + ); + } + } + + setTimeout(() => { + if (!document.getElementById('btbutton')) { + uw.$('.tb_activities, .toolbar_activities').find('.middle').append(buttonHtml); + } + if (startOnLogin) handleClick(); + }, 4000); + + uw.$(document).on('click', '#btbutton', handleClick); + + console.log('[GrepoTweaks-AutoRuralResources] Captain-free script loaded 🚀'); +})();