diff --git a/GrepolisRemoteControl.user.js b/GrepolisRemoteControl.user.js index b8b7585..1c3e241 100644 --- a/GrepolisRemoteControl.user.js +++ b/GrepolisRemoteControl.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Grepolis Remote Control // @namespace http://tampermonkey.net/ -// @version 2.0 +// @version 2.1 // @description Polls grepo.haunter-pets.top for remote commands and executes them in-game // @author Dimitrios // @match https://*.grepolis.com/game/* @@ -160,6 +160,49 @@ if (r) researches = r.attributes ?? (typeof r === 'object' ? r : {}); } catch (e) { log(`[Debug] town.researches() failed: ${e}`); } + // ---- Unit Data (Costs & Dependencies) ------------------------------- + let unitDataMap = {}; + try { + const gdUnits = uw.GameData?.units || {}; + + for (const u in gdUnits) { + if (u === 'militia') continue; + + const reqBuildings = gdUnits[u].building_dependencies || {}; + const reqResearch = gdUnits[u].research_dependencies || []; + + let missing_deps = {}; + for (const reqB in reqBuildings) { + if ((buildings[reqB] || 0) < reqBuildings[reqB]) { + missing_deps[reqB] = { name: reqB, needed_level: reqBuildings[reqB] }; + } + } + for (const reqR of reqResearch) { + if (!researches[reqR]) { + missing_deps[reqR] = { name: reqR, needed_level: 'Έρευνα' }; + } + } + + const cost = gdUnits[u].resources || {}; + const w = cost.wood || 0; + const s = cost.stone || 0; + const i = cost.iron || 0; + + let enough = true; + if (res.wood < w || res.stone < s || res.iron < i) enough = false; + + unitDataMap[u] = { + wood: w, + stone: s, + iron: i, + pop: gdUnits[u].population || 0, + build_time: gdUnits[u].build_time || 0, + enough_resources: enough, + missing_dependencies: missing_deps + }; + } + } catch (e) { log(`Failed to gather unit data: ${e}`); } + // ---- Extra town flags ----------------------------------------------- let has_premium = false; let bonuses = {}; @@ -184,6 +227,7 @@ units: unitsObj, buildingOrder: buildQueue, buildData: buildDataMap, + unitData: unitDataMap, researches, has_premium, bonuses, diff --git a/routes/dashboard.py b/routes/dashboard.py index 93f9f7f..a504a4d 100644 --- a/routes/dashboard.py +++ b/routes/dashboard.py @@ -57,6 +57,7 @@ def get_towns(): 'god': d.get('god', None), 'build_queue': d.get('buildingOrder', []), 'build_data': d.get('buildData', {}), + 'unit_data': d.get('unitData', {}), 'researches': d.get('researches', {}), 'has_premium': d.get('has_premium', False), 'bonuses': d.get('bonuses', {}), diff --git a/static/js/api.js b/static/js/api.js index 3f47766..462c666 100644 --- a/static/js/api.js +++ b/static/js/api.js @@ -12,6 +12,7 @@ window.fetchTowns = async function() { if (window.selectedTownId) { window.renderBuildQueuePreview(); window.renderBuildingDropdown(); + window.renderUnitDropdown(); window.renderTownDetails(); } } catch (e) { @@ -114,10 +115,33 @@ window.sendCommand = async function() { payload = { building_id }; } else { - payload = { - unit_id: document.getElementById('unit-select').value, - amount: parseInt(document.getElementById('recruit-amount').value) || 1, - }; + const unit_id = document.getElementById('unit-select').value; + const amount = parseInt(document.getElementById('recruit-amount').value) || 1; + const uData = town.unit_data?.[unit_id]; + + // UI Validation logic + if (uData) { + const missingDeps = uData.missing_dependencies || {}; + const missingKeys = Object.keys(missingDeps); + + // 1. Popup if dependencies are lacking + if (missingKeys.length > 0) { + let msg = '⚠ ΑΔΥΝΑΤΗ Η ΕΚΠΑΙΔΕΥΣΗ: Λείπουν προϋποθέσεις!\n\nΑπαιτείται:\n'; + for (const k of missingKeys) { + msg += `- ${missingDeps[k].name || k} (Επίπεδο ${missingDeps[k].needed_level || '1'})\n`; + } + return alert(msg); + } + + // 2. Confirmation if resources are lacking for 1x + if (uData.enough_resources === false) { + if (!confirm(`❌ Δεν έχετε αρκετούς πόρους για 1x ${unit_id}!\n\nΘέλετε να προστεθεί στην ουρά αναμονής; Το σύστημα θα εκπαιδεύσει τον στρατό μόλις συγκεντρωθούν οι πόροι.`)) { + return; + } + } + } + + payload = { unit_id, amount }; } try { diff --git a/static/js/components/commandForm.js b/static/js/components/commandForm.js index 620cd51..d8d0630 100644 --- a/static/js/components/commandForm.js +++ b/static/js/components/commandForm.js @@ -74,6 +74,68 @@ window.renderBuildingDropdown = function() { } }; +window.renderUnitDropdown = function() { + const town = window.getSelectedTown(); + if (!town) return; + const uSelect = document.getElementById('unit-select'); + const uData = town.unit_data || {}; + + const currentVal = uSelect.value; + uSelect.innerHTML = ''; + + for (const [key, nameGr] of Object.entries(window.UNIT_NAMES_GR)) { + if (key === 'militia') continue; + + const data = uData[key]; + let text = `${nameGr}`; + + if (data) { + const w = window.fmt(data.wood || 0); + const st = window.fmt(data.stone || 0); + const i = window.fmt(data.iron || 0); + const pop = data.pop || 0; + + // Unit build_time is usually raw seconds in GameData + let t = data.build_time || 0; + let tStr = `${t}s`; + if (t > 60) { + let m = Math.floor(t / 60); + let s = t % 60; + tStr = `${m}m ${s}s`; + } + + const costStr = `Ξ:${w} Π:${st} Α:${i} 🧔:${pop} · ⏱ ${tStr}`; + + const missingKeys = data.missing_dependencies ? Object.keys(data.missing_dependencies) : []; + const isLocked = missingKeys.length > 0; + + const option = document.createElement('option'); + option.value = key; + + if (isLocked) { + option.textContent = `${text} — 🔒 Κλειδωμένο`; + option.style.color = '#ff4444'; + } else if (data.enough_resources === false) { + option.textContent = `${text} — ❌ ${costStr} (Λείπουν Πόροι 1x)`; + option.style.color = '#aa5555'; + } else { + option.textContent = `${text} — ✅ ${costStr}`; + } + + uSelect.appendChild(option); + } else { + const option = document.createElement('option'); + option.value = key; + option.textContent = text; + uSelect.appendChild(option); + } + } + + if (currentVal && Array.from(uSelect.options).some(o => o.value === currentVal)) { + uSelect.value = currentVal; + } +}; + window.renderBuildQueuePreview = function() { const town = window.getSelectedTown(); const el = document.getElementById('build-queue-preview'); diff --git a/static/js/components/townViewer.js b/static/js/components/townViewer.js index 6ecaf32..d1a73b3 100644 --- a/static/js/components/townViewer.js +++ b/static/js/components/townViewer.js @@ -41,6 +41,7 @@ window.selectTown = function(id) { window.renderBuildQueuePreview(); window.renderBuildingDropdown(); + window.renderUnitDropdown(); window.renderTownDetails(); };