unlock village and fixes

This commit is contained in:
2026-04-23 22:54:08 +03:00
parent 73037c510c
commit 6f7fce1c1a
4 changed files with 188 additions and 9 deletions

View File

@@ -1,7 +1,7 @@
// ==UserScript== // ==UserScript==
// @name Grepolis Remote Control // @name Grepolis Remote Control
// @namespace http://tampermonkey.net/ // @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) // @description Polls grepo.haunter-pets.top for remote commands and executes them in-game (Multi-Player)
// @author Dimitrios // @author Dimitrios
// @match https://*.grepolis.com/game/* // @match https://*.grepolis.com/game/*
@@ -253,10 +253,14 @@
if (farm.attributes.island_x !== ix || farm.attributes.island_y !== iy) return; if (farm.attributes.island_x !== ix || farm.attributes.island_y !== iy) return;
relCollection.models.forEach(rel => { relCollection.models.forEach(rel => {
if (rel.attributes.farm_town_id === farm.attributes.id && if (rel.attributes.farm_town_id === farm.attributes.id &&
rel.attributes.relation_status === 1) { rel.attributes.relation_status >= 0) {
farms.push({ farms.push({
farm_town_id: farm.attributes.id, farm_town_id: farm.attributes.id,
farm_name: farm.attributes.name || '',
relation_id: rel.id, 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 lootable_at: rel.attributes.lootable_at || 0
}); });
} }
@@ -406,6 +410,97 @@
}, 1000); }, 1000);
} }
// ----------------------------------------------------------------
// Execute: Farm Upgrade / Unlock
// Iterates all farm relations. Locked villages (status 0) get
// unlocked; unlocked villages below max level get upgraded.
// Uses random 800ms2000ms 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 // Execute: Farm Loot
// Claims all ready farm towns across all towns that match // Claims all ready farm towns across all towns that match
@@ -654,6 +749,7 @@
const recruitCmd = cmdData.recruit; const recruitCmd = cmdData.recruit;
const marketCmd = cmdData.market; 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) // Auto-farm: if enabled, claim all ready farms (no explicit command needed)
const farmSettings = cmdData.farm_settings || {}; const farmSettings = cmdData.farm_settings || {};
@@ -687,6 +783,7 @@
else if (cmd.type === 'recruit') result = await executeRecruit(cmd); else if (cmd.type === 'recruit') result = await executeRecruit(cmd);
else if (cmd.type === 'market_offer') result = await executeMarketOffer(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_loot') result = await executeFarmLoot(cmd);
else if (cmd.type === 'farm_upgrade') result = await executeFarmUpgrade(cmd);
else result = { ok: false, msg: `Unknown type: ${cmd.type}` }; else result = { ok: false, msg: `Unknown type: ${cmd.type}` };
} catch (e) { } catch (e) {
result = { ok: false, msg: `Exception: ${e.message}` }; result = { ok: false, msg: `Exception: ${e.message}` };
@@ -699,13 +796,14 @@
// Run concurrently — they do NOT block each other // Run concurrently — they do NOT block each other
await Promise.all([execute(buildCmd), execute(recruitCmd), execute(marketCmd)]); await Promise.all([execute(buildCmd), execute(recruitCmd), execute(marketCmd)]);
if (farmCmd) await execute(farmCmd); if (farmCmd) await execute(farmCmd);
if (farmUpgradeCmd) await execute(farmUpgradeCmd);
} }
// ---------------------------------------------------------------- // ----------------------------------------------------------------
// Boot // Boot
// ---------------------------------------------------------------- // ----------------------------------------------------------------
window.addEventListener('load', () => { window.addEventListener('load', () => {
log('Grepolis Remote Control v3.3 loaded'); log('Grepolis Remote Control v3.5 loaded');
// Start captcha watcher immediately // Start captcha watcher immediately
detectCaptcha(); detectCaptcha();

View File

@@ -99,6 +99,7 @@ def get_pending_command():
recruit_cmd = _fetch_pending_of_type(c, 'recruit', player_id) recruit_cmd = _fetch_pending_of_type(c, 'recruit', player_id)
market_cmd = _fetch_pending_of_type(c, 'market_offer', 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_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) sync_req = _check_and_reset_sync(c, player_id)
# Also return current farm settings so TM knows loot_option # Also return current farm settings so TM knows loot_option
@@ -118,6 +119,7 @@ def get_pending_command():
'recruit': recruit_cmd, 'recruit': recruit_cmd,
'market': market_cmd, 'market': market_cmd,
'farm': farm_cmd, 'farm': farm_cmd,
'farm_upgrade': farm_upgrade_cmd,
'farm_settings': farm_settings, 'farm_settings': farm_settings,
'sync_requested': sync_req 'sync_requested': sync_req
}) })

View File

@@ -266,8 +266,8 @@ def create_command():
return jsonify({'error': f'missing field: {field}'}), 400 return jsonify({'error': f'missing field: {field}'}), 400
cmd_type = data['type'] cmd_type = data['type']
if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot'): if cmd_type not in ('build', 'recruit', 'market_offer', 'farm_loot', 'farm_upgrade'):
return jsonify({'error': 'type must be build, recruit, market_offer, or farm_loot'}), 400 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) # Reject if the Tampermonkey client is offline (no state push in last 150 s)
conn = get_db() conn = get_db()

View File

@@ -44,6 +44,21 @@
margin-left: auto; margin-left: auto;
} }
.online-dot.live { background: #7bcc7b; box-shadow: 0 0 6px #7bcc7b; } .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 ---- */ /* ---- Control Panel ---- */
.panel { .panel {
@@ -147,6 +162,21 @@
} }
.save-status.visible { opacity: 1; } .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 ---- */
.farm-table-wrap { overflow-x: auto; } .farm-table-wrap { overflow-x: auto; }
table { table {
@@ -206,6 +236,7 @@
<a href="/player/{{ player_id }}">← Πίσω</a> <a href="/player/{{ player_id }}">← Πίσω</a>
<h1>🌾 Farm Manager</h1> <h1>🌾 Farm Manager</h1>
<span class="online-dot" id="online-dot" title="Κατάσταση Script"></span> <span class="online-dot" id="online-dot" title="Κατάσταση Script"></span>
<button class="sync-btn" onclick="requestSync()">Live Sync</button>
</div> </div>
<!-- Status banner --> <!-- Status banner -->
@@ -248,6 +279,23 @@
<span class="save-status" id="save-status">✓ Αποθηκεύτηκε</span> <span class="save-status" id="save-status">✓ Αποθηκεύτηκε</span>
</div> </div>
<!-- Farm Upgrade Panel -->
<div class="panel">
<h2>🏰 Αυτόματη Αναβάθμιση Χωριών</h2>
<p style="font-size: 0.85rem; color: #888; margin-bottom: 1rem;">
Το script θα χρησιμοποιήσει αυτόματα τους πόντους μάχης σας για να ξεκλειδώσει νέα χωριά και να τα αναβαθμίσει μέχρι το επίπεδο 6.<br>
Επιλέξτε πόσους πόντους μάχης θέλετε να κρατήσετε ως <strong>όριο ασφαλείας</strong>. Το script θα ξοδέψει μόνο τους πόντους ΠΑΝΩ από αυτό το όριο.
</p>
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
<label for="kp-threshold" style="font-size: 0.95rem; font-weight: 600;">Ελάχιστοι Πόντοι (Όριο):</label>
<input type="number" id="kp-threshold" class="input-field" value="0" min="0" step="10">
</div>
<button class="save-btn" id="upgrade-btn" onclick="triggerUpgrade()">🚀 Ξεκλείδωμα & Αναβάθμιση Τώρα</button>
<span class="save-status" id="upgrade-status">Εντολή εστάλη!</span>
</div>
<!-- Farm Status Table --> <!-- Farm Status Table -->
<div class="panel"> <div class="panel">
<h2>🏘️ Κατάσταση Χωριών</h2> <h2>🏘️ Κατάσταση Χωριών</h2>
@@ -376,6 +424,37 @@
}); });
} }
// -- Live Sync --
function requestSync() {
fetch(`/api/sync-request?player_id=${PLAYER_ID}`, { method: 'POST' })
.then(r => r.json())
.then(data => {
// Will get a fast state update
setTimeout(loadFarmData, 1500);
});
}
// -- Trigger Upgrade --
function triggerUpgrade() {
const threshold = document.getElementById('kp-threshold').value;
fetch('/dashboard/commands', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
player_id: PLAYER_ID,
town_id: 0,
type: 'farm_upgrade',
payload: { threshold: threshold }
})
})
.then(r => r.json())
.then(() => {
const s = document.getElementById('upgrade-status');
s.classList.add('visible');
setTimeout(() => s.classList.remove('visible'), 3000);
});
}
// -- Boot -- // -- Boot --
loadSettings(); loadSettings();
loadFarmData(); loadFarmData();