Files
grepo-remote/static/js/components/commandForm.js
2026-04-25 15:18:56 +03:00

321 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ================================================================
// Command Form Component
// ================================================================
window.onCmdTypeChange = function() {
const type = document.getElementById('cmd-type').value;
document.getElementById('build-options').style.display = type === 'build' ? '' : 'none';
document.getElementById('recruit-options').style.display = type === 'recruit' ? '' : 'none';
document.getElementById('amount-group').style.display = type === 'recruit' ? '' : 'none';
document.getElementById('market-options').style.display = type === 'market_offer' ? '' : 'none';
document.getElementById('research-options').style.display = type === 'research' ? '' : 'none';
};
// Building emoji icons for the visual grid
const BUILDING_ICONS = {
main: '🏛️', storage: '🏚️', farm: '🌾', academy: '📜',
temple: '⛩️', barracks: '⚔️', docks: '⚓', market: '🛒',
hide: '🕳️', lumber: '🪵', stoner: '🪨', ironer: '⛏️', wall: '🧱'
};
window.selectedBuildingId = null;
window.renderBuildingDropdown = function() {
// No-op — selection now happens via the modal
};
window.openBuildingModal = function() {
const town = window.getSelectedTown();
if (!town) return;
const grid = document.getElementById('building-grid');
const bLevels = town.buildings || {};
const bData = town.build_data || {};
grid.innerHTML = Object.entries(window.BUILDING_NAMES_GR).map(([key, nameGr]) => {
const level = bLevels[key] !== undefined ? bLevels[key] : '?';
const data = bData[key];
const icon = BUILDING_ICONS[key] || '🏗️';
const isSelected = key === window.selectedBuildingId;
if (!data) {
return `<div class="bld-card${isSelected ? ' bld-selected' : ''}" onclick="window.selectBuilding('${key}','${nameGr}')">
<span class="bld-level">${level}</span>
<span class="bld-icon">${icon}</span>
<span class="bld-name">${nameGr}</span>
<span class="bld-status queue-warn">Αγνωστο</span>
</div>`;
}
const missingKeys = data.missing_dependencies ? Object.keys(data.missing_dependencies) : [];
const isLocked = !data.dependencies || missingKeys.length > 0;
const isMaxed = data.has_max_level;
let statusClass, statusLabel, cardClass = '';
if (isMaxed) {
statusClass = 'maxed'; statusLabel = '✓ Μέγιστο'; cardClass = 'bld-maxed';
} else if (isLocked) {
statusClass = 'locked'; statusLabel = '🔒 Κλειδωμένο'; cardClass = 'bld-locked';
} else if (data.can_upgrade) {
statusClass = 'can-build'; statusLabel = '✅ Αναβάθμιση';
} else if (data.enough_resources === false) {
statusClass = 'no-resources'; statusLabel = '❌ Λείπουν Πόροι';
} else {
statusClass = 'queue-warn'; statusLabel = '⚠️ Πληθ./Ουρά';
}
const w = data.wood ? window.fmt(data.wood) : 0;
const st = data.stone ? window.fmt(data.stone) : 0;
const i = data.iron ? window.fmt(data.iron) : 0;
let t = data.build_time || '';
if (t.startsWith('00:')) t = t.substring(3);
const costStr = (w || st || i) ? `Ξ:${w} Π:${st} Α:${i}${t ? ' · ⏱'+t : ''}` : '';
const clickable = !isMaxed && !isLocked;
const onclick = clickable ? `onclick="window.selectBuilding('${key}','${nameGr}')"` : '';
// Build tooltip for locked buildings
let tooltip = '';
if (isLocked && missingKeys.length > 0) {
const missingDeps = data.missing_dependencies;
const lines = missingKeys.map(k => {
const dep = missingDeps[k];
const depNameGr = window.BUILDING_NAMES_GR[k] || k;
return `${depNameGr} (Επίπεδο ${dep.needed_level || dep.needed || '?'})`;
});
tooltip = `title="Απαιτείται:\n${lines.join('\n')}"`;
}
return `<div class="bld-card ${cardClass}${isSelected ? ' bld-selected' : ''}" ${onclick} ${tooltip}>
<span class="bld-level">${level}</span>
<span class="bld-icon">${icon}</span>
<span class="bld-name">${nameGr}</span>
<span class="bld-status ${statusClass}">${statusLabel}</span>
${costStr ? `<span class="bld-cost">${costStr}</span>` : ''}
</div>`;
}).join('');
// Render current build queue in modal footer
const qEl = document.getElementById('building-modal-queue-items');
if (town.build_queue && town.build_queue.length) {
qEl.innerHTML = town.build_queue.map(o => {
const raw = o.building_type || o.name || '';
const gr = window.BUILDING_NAMES_GR[raw] || raw;
return `<span style="background:rgba(0,0,0,0.3);padding:2px 8px;border-radius:4px;margin-right:4px;font-size:0.8rem;">${gr}</span>`;
}).join('');
} else {
qEl.innerHTML = '<span style="color:#555;font-size:0.8rem;">Κενή</span>';
}
document.getElementById('building-modal-overlay').classList.add('open');
};
window.closeBuildingModal = function(e) {
// Close only if clicking the overlay background or the X button
if (e && e.target !== document.getElementById('building-modal-overlay') && e.target !== document.getElementById('building-modal-close')) return;
document.getElementById('building-modal-overlay').classList.remove('open');
};
window.selectBuilding = function(key, nameGr) {
window.selectedBuildingId = key;
// Update the trigger button label
document.getElementById('selected-building-label').textContent = `🏗️ ${nameGr}`;
// Re-render grid to show new selection highlight
window.openBuildingModal();
// Close after brief visual feedback
setTimeout(() => document.getElementById('building-modal-overlay').classList.remove('open'), 180);
};
// ================================================================
// Academy Research Logic
// ================================================================
window.selectedResearchId = null;
window.RESEARCH_DATA = {
"slinger": { "name": "Εκσφενδονιστές", "academy_level": 1, "wood": 300, "stone": 500, "iron": 200, "points": 4 },
"archer": { "name": "Τοξότες", "academy_level": 1, "wood": 550, "stone": 100, "iron": 400, "points": 8 },
"town_guard": { "name": "Φρουρά πόλης", "academy_level": 1, "wood": 400, "stone": 300, "iron": 300, "points": 3 },
"hoplite": { "name": "Οπλίτες", "academy_level": 4, "wood": 600, "stone": 200, "iron": 850, "points": 8 },
"meteorology": { "name": "Μετεωρολογία", "academy_level": 4, "wood": 2500, "stone": 1700, "iron": 6500, "points": 4 },
"espionage": { "name": "Κατασκοπεία", "academy_level": 7, "wood": 900, "stone": 900, "iron": 1100, "points": 3 },
"booty": { "name": "Αφοσίωση χωρικών", "academy_level": 7, "wood": 1300, "stone": 1300, "iron": 1300, "points": 6 },
"pottery": { "name": "Κεραμικά", "academy_level": 7, "wood": 700, "stone": 1500, "iron": 900, "points": 4 },
"rider": { "name": "Ιππείς", "academy_level": 10, "wood": 1400, "stone": 700, "iron": 1800, "points": 8 },
"architecture": { "name": "Αρχιτεκτονική", "academy_level": 10, "wood": 1900, "stone": 2100, "iron": 1300, "points": 6 },
"instructor": { "name": "Εκπαιδευτής", "academy_level": 10, "wood": 800, "stone": 1300, "iron": 1600, "points": 4 },
"bireme": { "name": "Διήρεις", "academy_level": 13, "wood": 2800, "stone": 1300, "iron": 2200, "points": 8 },
"building_crane": { "name": "Γερανός", "academy_level": 13, "wood": 3000, "stone": 1800, "iron": 1400, "points": 4 },
"shipwright": { "name": "Ναυπηγός", "academy_level": 13, "wood": 5000, "stone": 2000, "iron": 3900, "points": 6 },
"colonize_ship": { "name": "Αποικιακά πλοία", "academy_level": 13, "wood": 7500, "stone": 7500, "iron": 9500, "points": 0 },
"chariot": { "name": "Άρματα", "academy_level": 16, "wood": 3700, "stone": 1900, "iron": 2800, "points": 8 },
"attack_ship": { "name": "Πλοία-φάροι", "academy_level": 16, "wood": 4400, "stone": 2000, "iron": 2400, "points": 8 },
"conscription": { "name": "Στρατολόγηση", "academy_level": 16, "wood": 3800, "stone": 4200, "iron": 6000, "points": 4 },
"demolition_ship": { "name": "Πυρπολικά", "academy_level": 19, "wood": 5300, "stone": 2600, "iron": 2700, "points": 8 },
"catapult": { "name": "Καταπέλτες", "academy_level": 19, "wood": 5500, "stone": 2900, "iron": 3600, "points": 8 },
"cryptography": { "name": "Κρυπτογραφία", "academy_level": 19, "wood": 2500, "stone": 3000, "iron": 5100, "points": 6 },
"small_transporter": { "name": "Γρήγορα μεταφορικά πλοία", "academy_level": 22, "wood": 6500, "stone": 2800, "iron": 3200, "points": 8 },
"plow": { "name": "Άροτρο", "academy_level": 22, "wood": 3000, "stone": 3300, "iron": 2100, "points": 4 },
"berth": { "name": "Κουκέτες", "academy_level": 22, "wood": 8900, "stone": 5200, "iron": 7800, "points": 6 },
"trireme": { "name": "Τριήρεις", "academy_level": 25, "wood": 6500, "stone": 3800, "iron": 4700, "points": 8 },
"phalanx": { "name": "Φάλαγγα", "academy_level": 25, "wood": 4000, "stone": 4000, "iron": 15000, "points": 9 },
"breach": { "name": "Διάσπαση εχθρικού μετώπου", "academy_level": 25, "wood": 8000, "stone": 8000, "iron": 9000, "points": 6 },
"mathematics": { "name": "Μαθηματικά", "academy_level": 25, "wood": 7100, "stone": 4400, "iron": 8600, "points": 6 },
"ram": { "name": "Πολιορκητικός κριός", "academy_level": 28, "wood": 7900, "stone": 9200, "iron": 14000, "points": 10 },
"cartography": { "name": "Χαρτογραφία", "academy_level": 28, "wood": 10000, "stone": 6700, "iron": 12500, "points": 8 },
"take_over": { "name": "Κατάκτηση", "academy_level": 28, "wood": 12000, "stone": 12000, "iron": 16000, "points": 0 },
"stone_storm": { "name": "Καταιγισμός από πέτρες", "academy_level": 31, "wood": 8500, "stone": 5900, "iron": 6600, "points": 4 },
"temple_looting": { "name": "Λεηλασία ναού", "academy_level": 31, "wood": 9200, "stone": 5300, "iron": 10000, "points": 6 },
"divine_selection": { "name": "Θεϊκή επιλογή", "academy_level": 31, "wood": 10000, "stone": 8000, "iron": 12000, "points": 10 },
"combat_experience": { "name": "Εμπειρία μάχης", "academy_level": 34, "wood": 9800, "stone": 11400, "iron": 14200, "points": 6 },
"strong_wine": { "name": "Δυνατό κρασί", "academy_level": 34, "wood": 8000, "stone": 6500, "iron": 11000, "points": 4 },
"set_sail": { "name": "Σαλπάρισμα!", "academy_level": 34, "wood": 13000, "stone": 9700, "iron": 15500, "points": 8 }
};
window.openAcademyModal = function() {
const town = window.getSelectedTown();
if (!town) return;
const grid = document.getElementById('academy-grid');
// Group researches by academy level
const levels = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34];
const townResearches = town.researches || {};
const townBuildings = town.buildings || {};
const townResources = town.resources || { wood: 0, stone: 0, iron: 0 };
const academyLvl = townBuildings.academy || 0;
let html = '';
for (const lvl of levels) {
const researchesInLvl = Object.entries(window.RESEARCH_DATA).filter(([k, v]) => v.academy_level === lvl);
if (researchesInLvl.length === 0) continue;
html += `<div class="academy-col">
<div class="academy-col-header">Επίπεδο ${lvl}</div>`;
for (const [key, data] of researchesInLvl) {
const isResearched = !!townResearches[key];
const isSelected = key === window.selectedResearchId;
const isLocked = academyLvl < lvl;
const noResources = townResources.wood < data.wood || townResources.stone < data.stone || townResources.iron < data.iron;
let statusClass, statusLabel, cardClass = '';
if (isResearched) {
statusClass = 'maxed'; statusLabel = '✓ Ερευνήθηκε'; cardClass = 'bld-maxed';
} else if (isLocked) {
statusClass = 'locked'; statusLabel = '🔒 Κλειδωμένο'; cardClass = 'bld-locked';
} else if (noResources) {
statusClass = 'no-resources'; statusLabel = '❌ Λείπουν Πόροι';
} else {
statusClass = 'can-build'; statusLabel = '✅ Έρευνα';
}
const clickable = !isResearched && !isLocked;
const onclick = clickable ? `onclick="window.selectResearch('${key}', '${data.name}')"` : '';
const costStr = `Ξ:${window.fmt(data.wood)} Π:${window.fmt(data.stone)} Α:${window.fmt(data.iron)}`;
html += `<div class="bld-card ${cardClass}${isSelected ? ' bld-selected' : ''}" ${onclick}>
<span class="bld-name" style="margin-top:6px; font-size:0.8rem;">${data.name}</span>
<span class="bld-status ${statusClass}">${statusLabel}</span>
<span class="bld-cost" style="color:#a88; margin-top:4px;">Πόντοι: ${data.points}</span>
<span class="bld-cost">${costStr}</span>
</div>`;
}
html += `</div>`;
}
grid.innerHTML = html;
document.getElementById('academy-modal-overlay').classList.add('open');
};
window.closeAcademyModal = function(e) {
if (e && e.target !== document.getElementById('academy-modal-overlay') && e.target !== document.getElementById('building-modal-close')) return;
document.getElementById('academy-modal-overlay').classList.remove('open');
};
window.selectResearch = function(key, name) {
window.selectedResearchId = key;
document.getElementById('selected-research-label').textContent = `🧪 ${name}`;
window.openAcademyModal();
setTimeout(() => document.getElementById('academy-modal-overlay').classList.remove('open'), 180);
};
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 = '<option value="" disabled selected>-- Επιλέξτε Μονάδα --</option>';
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');
if (!town || !town.build_queue || !town.build_queue.length) {
el.innerHTML = '<span style="color:#444">Build queue: empty</span>';
return;
}
const items = town.build_queue.map(o => {
const raw = o.building_type || o.name || "";
const nameGr = window.BUILDING_NAMES_GR[raw] || raw || JSON.stringify(o);
return `<span style="background: rgba(0,0,0,0.3); padding: 2px 6px; border-radius: 4px; margin-right: 4px; font-size: 0.8rem;">${nameGr}</span>`;
}).join('');
el.innerHTML = `<div style="margin-top:6px;color:#888;font-size:0.72rem;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:4px;">Current queue</div>${items}`;
};