// 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'; const requestedAction = cmd.payload?.action_type; if (requestedAction && requestedAction !== action) { skipped++; continue; } if (paused) { return { ok: false, msg: 'Aborted due to pause/captcha' }; } 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: 1200ms – 2500ms await sleep(randInt(1200, 2500)); } } } 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 // the cmd payload (town_ids list + loot_option). // Between-claim delay: random 500ms–1500ms (never below 500ms) // Between-town-group delay: random 30s–90s // ---------------------------------------------------------------- async function executeFarmLoot(cmd) { const { loot_option } = cmd.payload || {}; const option = parseInt(loot_option) || 1; 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 — open island view first' }; } // Build island groups using MM (all towns, not just visible) const islandTownsMap = {}; // island_id -> [town_id1, town_id2] try { const allTowns = uw.MM.getCollections().Town[0].models; for (const town of allTowns) { const { on_small_island, island_id, id } = town.attributes; if (on_small_island) continue; if (!islandTownsMap[island_id]) islandTownsMap[island_id] = []; islandTownsMap[island_id].push(id); } } catch (e) { return { ok: false, msg: `Cannot build town list: ${e.message}` }; } const islandList = Object.keys(islandTownsMap); log(`Farm: processing ${islandList.length} islands with option=${option}`); let claimed = 0; let skipped = 0; let errors = 0; for (let i = 0; i < islandList.length; i++) { const island_id = islandList[i]; const townIds = islandTownsMap[island_id]; let selected_town_id = null; let lowest_total_res = Infinity; for (const t_id of townIds) { const t = uw.ITowns?.towns?.[t_id]; if (!t) continue; let storageCapacity = t.getStorageCapacity?.() || 0; if (!storageCapacity) { const buildings = t.buildings?.()?.attributes || {}; const storageLevel = buildings.storage ?? 0; const gd = uw.GameData?.buildingData?.storage; storageCapacity = gd?.max_storage?.[storageLevel] || gd?.storage?.[storageLevel] || 0; } const res = t.resources?.() || {}; const w = res.wood || 0; const s = res.stone || 0; const ir = res.iron || 0; // If completely full (all 3 resources >= max storage), skip this town if (storageCapacity > 0 && w >= storageCapacity && s >= storageCapacity && ir >= storageCapacity) { continue; } // Pick town with most space (lowest total resources) const total_res = w + s + ir; if (total_res < lowest_total_res) { lowest_total_res = total_res; selected_town_id = t_id; } } if (!selected_town_id) { log(`Farm: Skipping island ${island_id} (All towns are 100% full)`); skipped++; continue; } const town_id = selected_town_id; const town = uw.ITowns?.towns?.[town_id]; if (!town) { skipped++; continue; } // Use the same method as the original script const ix = town.getIslandCoordinateX(); const iy = town.getIslandCoordinateY(); if (ix == null || iy == null) { skipped++; continue; } // Find ready farms on this island (mirrors original getLootableFarms exactly) const readyFarms = []; 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 && rel.attributes.relation_status === 1 && (!rel.attributes.lootable_at || now >= rel.attributes.lootable_at) ) { readyFarms.push({ town_id, farm_town_id: rel.attributes.farm_town_id, relation_id: rel.id }); } } } if (readyFarms.length === 0) { skipped++; continue; } log(`Farm: ${readyFarms.length} ready on island of town ${town_id}`); for (const farm of readyFarms) { if (paused) { return { ok: false, msg: 'Aborted due to pause/captcha' }; } try { uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { model_url: `FarmTownPlayerRelation/${farm.relation_id}`, action_name: 'claim', arguments: { farm_town_id: farm.farm_town_id, type: 'resources', option }, town_id: farm.town_id }); claimed++; } catch (e) { errors++; } // Random per-claim delay: 1000ms – 2200ms await sleep(randInt(1000, 2200)); } // Refresh map icons after claiming (same as original) try { uw.WMap.removeFarmTownLootCooldownIconAndRefreshLootTimers(); } catch (e) { } // Random between-island delay: 30s – 90s (only if more islands remain) if (i < islandList.length - 1) { if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' }; const gap = randInt(30000, 90000); log(`Farm: island done. Waiting ${(gap / 1000).toFixed(0)}s before next island...`); await sleep(gap); } } return { ok: true, msg: `Farm done: ${claimed} claimed, ${skipped} islands skipped, ${errors} errors` }; } // ---------------------------------------------------------------- // Execute: Build // ---------------------------------------------------------------- async function executeBuild(cmd) { const { town_id, payload } = cmd; const { building_id } = payload; const town = uw.ITowns?.getTown?.(town_id) || uw.ITowns?.towns?.[town_id]; if (!town) { return { ok: false, msg: `Town ${town_id} not found in ITowns` }; } // Check build queue const queueLen = town.buildingOrders?.()?.length ?? 0; const hasCurator = uw.GameDataPremium?.isAdvisorActivated?.('curator'); const maxQueue = hasCurator ? 7 : 2; if (queueLen >= maxQueue) { return { ok: false, requeue: true, msg: `Build queue full (${queueLen}/${maxQueue})` }; } // Check resources try { const buildData = uw.MM.getModels?.()?.BuildingBuildData?.[town_id] ?.attributes?.building_data?.[building_id]; if (buildData) { const res = town.resources(); const { resources_for, population_for } = buildData; if (town.getAvailablePopulation?.() < population_for) { return { ok: false, requeue: true, msg: `Not enough population for ${building_id}` }; } if (res.wood < resources_for.wood || res.stone < resources_for.stone || res.iron < resources_for.iron) { return { ok: false, requeue: true, msg: `Not enough resources for ${building_id}` }; } } } catch (e) { log(`Resource check skipped: ${e}`); } // Fire the build request — with a human-like reaction delay const reactionMs = randInt(800, 2500); log(`Waiting ${reactionMs}ms before firing buildUp (reaction time)...`); await sleep(reactionMs); if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' }; uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { model_url: 'BuildingOrder', action_name: 'buildUp', arguments: { building_id }, town_id }); await sleep(500); return { ok: true, msg: `buildUp ${building_id} queued` }; } // ---------------------------------------------------------------- // Execute: Recruit // ---------------------------------------------------------------- async function executeRecruit(cmd) { const { town_id, payload } = cmd; const { unit_id, amount } = payload; const town = uw.ITowns?.getTown?.(town_id) || uw.ITowns?.towns?.[town_id]; if (!town) { return { ok: false, msg: `Town ${town_id} not found` }; } // Determine endpoint based on unit type const navalUnits = [ 'big_transporter', 'small_transporter', 'bireme', 'attack_ship', 'trireme', 'colonize_ship', 'sea_monster' ]; const endpoint = navalUnits.includes(unit_id) ? 'building_docks' : 'building_barracks'; // Fire the recruit request — with a human-like reaction delay const reactionMs = randInt(800, 2500); log(`Waiting ${reactionMs}ms before firing recruit (reaction time)...`); await sleep(reactionMs); if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' }; uw.gpAjax.ajaxPost(endpoint, 'build', { unit_id, amount: parseInt(amount) || 1, town_id }); await sleep(500); return { ok: true, msg: `Recruit ${amount}x ${unit_id} submitted` }; } // ---------------------------------------------------------------- // Execute: Market // ---------------------------------------------------------------- async function executeMarketOffer(cmd) { const { town_id, payload } = cmd; const { offer, offer_type, demand, demand_type, max_delivery_time, visibility } = payload; const town = uw.ITowns?.getTown?.(town_id) || uw.ITowns?.towns?.[town_id]; if (!town) { return { ok: false, msg: `Town ${town_id} not found` }; } const reactionMs = randInt(800, 2500); log(`Waiting ${reactionMs}ms before firing market offer (reaction time)...`); await sleep(reactionMs); if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' }; uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { model_url: 'CreateOffers/' + town_id, action_name: 'createOffer', captcha: null, arguments: { offer, offer_type, demand, demand_type, max_delivery_time, visibility } }); await sleep(500); return { ok: true, msg: `Market offer posted: ${offer} ${offer_type} => ${demand} ${demand_type}` }; } // ---------------------------------------------------------------- // Execute: Research (Academy) // ---------------------------------------------------------------- async function executeResearch(cmd) { const { town_id, payload } = cmd; const { research_id } = payload; const town = uw.ITowns?.getTown?.(town_id) || uw.ITowns?.towns?.[town_id]; if (!town) { return { ok: false, msg: `Town ${town_id} not found` }; } const reactionMs = randInt(800, 2500); log(`Waiting ${reactionMs}ms before firing research (reaction time)...`); await sleep(reactionMs); if (paused) return { ok: false, msg: 'Aborted due to pause/captcha' }; uw.gpAjax.ajaxPost('frontend_bridge', 'execute', { model_url: 'ResearchOrder', action_name: 'research', arguments: { id: research_id }, town_id: town_id }); await sleep(500); return { ok: true, msg: `Research ${research_id} queued` }; } // ----------------------------------------------------------------