diff --git a/GrepolisRemoteControl.user.js b/GrepolisRemoteControl.user.js index 940edd3..8f9c8e5 100644 --- a/GrepolisRemoteControl.user.js +++ b/GrepolisRemoteControl.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Grepolis Remote Control // @namespace http://tampermonkey.net/ -// @version 3.4 +// @version 3.5 // @description Polls grepo.haunter-pets.top for remote commands and executes them in-game (Multi-Player) // @author Dimitrios // @match https://*.grepolis.com/game/* @@ -253,11 +253,15 @@ if (farm.attributes.island_x !== ix || farm.attributes.island_y !== iy) return; relCollection.models.forEach(rel => { if (rel.attributes.farm_town_id === farm.attributes.id && - rel.attributes.relation_status === 1) { + rel.attributes.relation_status >= 0) { farms.push({ - farm_town_id: farm.attributes.id, - relation_id: rel.id, - lootable_at: rel.attributes.lootable_at || 0 + farm_town_id: farm.attributes.id, + farm_name: farm.attributes.name || '', + relation_id: rel.id, + relation_status: rel.attributes.relation_status, + expansion_stage: rel.attributes.expansion_stage || 0, + expansion_at: rel.attributes.expansion_at || 0, + lootable_at: rel.attributes.lootable_at || 0 }); } }); @@ -406,6 +410,97 @@ }, 1000); } + // ---------------------------------------------------------------- + // Execute: Farm Upgrade / Unlock + // Iterates all farm relations. Locked villages (status 0) get + // unlocked; unlocked villages below max level get upgraded. + // Uses random 800ms–2000ms delay between each action. + // payload.threshold = minimum kill points to keep (default 0) + // ---------------------------------------------------------------- + async function executeFarmUpgrade(cmd) { + const threshold = parseInt(cmd.payload?.threshold ?? 0); + const now = Math.floor(Date.now() / 1000); + + let farmModels, relModels; + try { + farmModels = uw.MM.getOnlyCollectionByName('FarmTown')?.models; + relModels = uw.MM.getOnlyCollectionByName('FarmTownPlayerRelation')?.models; + } catch (e) { + return { ok: false, msg: `Cannot access farm collections: ${e.message}` }; + } + if (!farmModels || !relModels) { + return { ok: false, msg: 'Farm collections not loaded yet' }; + } + + // Build polis list (one town per island) + const islandsSeen = new Set(); + const polisList = []; + try { + for (const town of uw.MM.getCollections().Town[0].models) { + const { on_small_island, island_id, id } = town.attributes; + if (on_small_island) continue; + if (!islandsSeen.has(island_id)) { + islandsSeen.add(island_id); + polisList.push(id); + } + } + } catch (e) { + return { ok: false, msg: `Cannot build town list: ${e.message}` }; + } + + let upgraded = 0; + let unlocked = 0; + let skipped = 0; + let errors = 0; + + for (const town_id of polisList) { + const town = uw.ITowns?.towns?.[town_id]; + if (!town) continue; + const ix = town.getIslandCoordinateX(); + const iy = town.getIslandCoordinateY(); + if (ix == null || iy == null) continue; + + for (const farm of farmModels) { + if (farm.attributes.island_x !== ix || farm.attributes.island_y !== iy) continue; + + for (const rel of relModels) { + if (rel.attributes.farm_town_id !== farm.attributes.id) continue; + + const status = rel.attributes.relation_status; + const level = rel.attributes.expansion_stage || 0; + const expAt = rel.attributes.expansion_at || 0; + + // Skip if upgrade already in progress + if (expAt > now) { skipped++; continue; } + // Skip if already max level + if (status === 1 && level >= 5) { skipped++; continue; } + // Skip if locked and we can't unlock (status -1 means enemy) + if (status < 0) { skipped++; continue; } + + const isLocked = status === 0; + const action = isLocked ? 'unlock' : 'upgrade'; + + log(`Farm ${action}: farm_id=${farm.attributes.id} level=${level} town=${town_id}`); + try { + uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { + model_url: `FarmTownPlayerRelation/${rel.id}`, + action_name: action, + arguments: { farm_town_id: farm.attributes.id }, + town_id + }); + isLocked ? unlocked++ : upgraded++; + } catch (e) { errors++; } + + // Random delay between actions: 800ms – 2000ms + await sleep(randInt(800, 2000)); + } + } + } + + pushState(); // refresh farm data after upgrades + return { ok: true, msg: `Farm upgrade done: ${unlocked} unlocked, ${upgraded} upgraded, ${skipped} skipped, ${errors} errors` }; + } + // ---------------------------------------------------------------- // Execute: Farm Loot // Claims all ready farm towns across all towns that match @@ -653,7 +748,8 @@ const buildCmd = cmdData.build; const recruitCmd = cmdData.recruit; const marketCmd = cmdData.market; - const farmCmd = cmdData.farm; + const farmCmd = cmdData.farm; + const farmUpgradeCmd = cmdData.farm_upgrade; // Auto-farm: if enabled, claim all ready farms (no explicit command needed) const farmSettings = cmdData.farm_settings || {}; @@ -687,6 +783,7 @@ else if (cmd.type === 'recruit') result = await executeRecruit(cmd); else if (cmd.type === 'market_offer') result = await executeMarketOffer(cmd); else if (cmd.type === 'farm_loot') result = await executeFarmLoot(cmd); + else if (cmd.type === 'farm_upgrade') result = await executeFarmUpgrade(cmd); else result = { ok: false, msg: `Unknown type: ${cmd.type}` }; } catch (e) { result = { ok: false, msg: `Exception: ${e.message}` }; @@ -699,13 +796,14 @@ // Run concurrently — they do NOT block each other await Promise.all([execute(buildCmd), execute(recruitCmd), execute(marketCmd)]); if (farmCmd) await execute(farmCmd); + if (farmUpgradeCmd) await execute(farmUpgradeCmd); } // ---------------------------------------------------------------- // Boot // ---------------------------------------------------------------- window.addEventListener('load', () => { - log('Grepolis Remote Control v3.3 loaded'); + log('Grepolis Remote Control v3.5 loaded'); // Start captcha watcher immediately detectCaptcha(); diff --git a/routes/api.py b/routes/api.py index 270aedc..ac504de 100644 --- a/routes/api.py +++ b/routes/api.py @@ -99,6 +99,7 @@ def get_pending_command(): recruit_cmd = _fetch_pending_of_type(c, 'recruit', player_id) market_cmd = _fetch_pending_of_type(c, 'market_offer', player_id) farm_cmd = _fetch_pending_of_type(c, 'farm_loot', player_id) + farm_upgrade_cmd = _fetch_pending_of_type(c, 'farm_upgrade', player_id) sync_req = _check_and_reset_sync(c, player_id) # Also return current farm settings so TM knows loot_option @@ -118,6 +119,7 @@ def get_pending_command(): 'recruit': recruit_cmd, 'market': market_cmd, 'farm': farm_cmd, + 'farm_upgrade': farm_upgrade_cmd, 'farm_settings': farm_settings, 'sync_requested': sync_req }) diff --git a/routes/dashboard.py b/routes/dashboard.py index de5b2a6..e2e5d86 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -266,8 +266,8 @@ def create_command(): return jsonify({'error': f'missing field: {field}'}), 400 cmd_type = data['type'] - if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot'): - return jsonify({'error': 'type must be build, recruit, market_offer, or farm_loot'}), 400 + if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot', 'farm_upgrade'): + return jsonify({'error': 'type must be build, recruit, market_offer, farm_loot, or farm_upgrade'}), 400 # Reject if the Tampermonkey client is offline (no state push in last 150 s) conn = get_db() diff --git a/templates/farm.html b/templates/farm.html index a761997..b591aff 100644 --- a/templates/farm.html +++ b/templates/farm.html @@ -44,6 +44,21 @@ margin-left: auto; } .online-dot.live { background: #7bcc7b; box-shadow: 0 0 6px #7bcc7b; } + .sync-btn { + background: transparent; + border: 1px solid #4acc64; + color: #4acc64; + border-radius: 6px; + padding: 0.3rem 0.6rem; + font-size: 0.8rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + margin-left: 0.5rem; + } + .sync-btn:hover { + background: rgba(74, 204, 100, 0.1); + } /* ---- Control Panel ---- */ .panel { @@ -147,6 +162,21 @@ } .save-status.visible { opacity: 1; } + /* Input field */ + .input-field { + background: #12121e; + border: 1px solid #2a2a40; + color: #e0e0e0; + border-radius: 6px; + padding: 0.6rem; + width: 100px; + font-family: inherit; + font-size: 0.95rem; + outline: none; + transition: border-color 0.2s; + } + .input-field:focus { border-color: #4acc64; } + /* ---- Farm Table ---- */ .farm-table-wrap { overflow-x: auto; } table { @@ -206,6 +236,7 @@ ← Πίσω
+ Το script θα χρησιμοποιήσει αυτόματα τους πόντους μάχης σας για να ξεκλειδώσει νέα χωριά και να τα αναβαθμίσει μέχρι το επίπεδο 6.
+ Επιλέξτε πόσους πόντους μάχης θέλετε να κρατήσετε ως όριο ασφαλείας. Το script θα ξοδέψει μόνο τους πόντους ΠΑΝΩ από αυτό το όριο.
+