Files
grepo-remote/static/js/components/commandForm.js
2026-05-02 00:08:43 +03:00

474 lines
23 KiB
JavaScript
Raw Permalink 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.currentCmdType = null;
window.setCmdType = function(type, openModal = false) {
window.currentCmdType = type;
// Update segmented control active state
document.querySelectorAll('.seg-btn').forEach(btn => btn.classList.remove('active'));
const activeBtn = document.getElementById('seg-' + (type === 'market_offer' ? 'market' : type));
if (activeBtn) activeBtn.classList.add('active');
// Show selection area
document.getElementById('selection-area').style.display = 'flex';
document.getElementById('recruit-amount-wrap').style.display = type === 'recruit' ? 'flex' : 'none';
// Update selection label with current choice if any, or default
window.updateSelectionDisplay();
if (openModal) {
window.reopenActiveModal();
}
};
window.updateSelectionDisplay = function() {
const type = window.currentCmdType;
const labelEl = document.getElementById('selection-label');
if (type === 'build') {
if (window.selectedBuildingId) {
labelEl.innerHTML = `🏗️ ${window.BUILDING_NAMES_GR[window.selectedBuildingId] || window.selectedBuildingId}`;
} else {
labelEl.innerHTML = `-- Επιλέξτε Κατασκευή --`;
}
} else if (type === 'recruit') {
if (window.selectedUnitId) {
labelEl.innerHTML = `⚔️ ${window.UNIT_NAMES_GR[window.selectedUnitId] || window.selectedUnitId}`;
} else {
labelEl.innerHTML = `-- Επιλέξτε Μονάδα --`;
}
} else if (type === 'research') {
if (window.selectedResearchId) {
const rd = window.RESEARCH_DATA[window.selectedResearchId];
labelEl.innerHTML = `🦉 ${rd ? rd.name : window.selectedResearchId}`;
} else {
labelEl.innerHTML = `-- Επιλέξτε Έρευνα --`;
}
} else if (type === 'market_offer') {
labelEl.innerHTML = `🛒 Ρυθμίσεις Παζαριού`;
} else if (type === 'blueprints') {
if (window.selectedBlueprintName) {
labelEl.innerHTML = `📜 ${window.selectedBlueprintName}`;
} else {
labelEl.innerHTML = `-- Επιλέξτε Blueprint --`;
}
}
};
window.reopenActiveModal = function() {
const type = window.currentCmdType;
if (type === 'build') window.openBuildingModal();
else if (type === 'recruit') window.openUnitModal();
else if (type === 'research') window.openAcademyModal();
else if (type === 'market_offer') window.openMarketModal();
else if (type === 'blueprints') window.openBlueprintsModal();
};
window.selectedBlueprintName = null;
window.selectBlueprint = function(name) {
window.selectedBlueprintName = name;
window.updateSelectionDisplay();
window.closeBlueprintsModal();
};
window.openBlueprintsModal = function() {
document.getElementById('blueprints-modal-overlay').classList.add('open');
};
window.closeBlueprintsModal = function(e) {
if (e && e.target.id !== 'blueprints-modal-overlay' && !e.target.classList.contains('modal-close')) return;
document.getElementById('blueprints-modal-overlay').classList.remove('open');
};
window.openMarketModal = function() {
document.getElementById('market-modal-overlay').classList.add('open');
};
window.closeMarketModal = function(e) {
if (e && !e.target.classList.contains('modal-overlay') && !e.target.classList.contains('modal-close')) return;
document.getElementById('market-modal-overlay').classList.remove('open');
};
window.saveMarketModal = function() {
const oType = document.getElementById('market-offer-type').value;
const oAmt = document.getElementById('market-offer-amount').value;
const dType = document.getElementById('market-demand-type').value;
const dAmt = document.getElementById('market-demand-amount').value;
const labelEl = document.getElementById('selection-label');
const resGr = { wood: 'Ξύλο', stone: 'Πέτρα', iron: 'Ασήμι' };
labelEl.innerHTML = `🛒 ${oAmt} ${resGr[oType]}${dAmt} ${resGr[dType]}`;
window.closeMarketModal();
};
// Building emoji icons for the visual grid
window.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 = window.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;
window.updateSelectionDisplay();
window.openBuildingModal();
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('academy-modal-close')) return;
document.getElementById('academy-modal-overlay').classList.remove('open');
};
window.selectResearch = function(key, name) {
window.selectedResearchId = key;
window.updateSelectionDisplay();
window.openAcademyModal();
setTimeout(() => document.getElementById('academy-modal-overlay').classList.remove('open'), 180);
};
window.selectedUnitId = null;
window.UNIT_ICONS = {
sword: '⚔️', slinger: '🪨', archer: '🏹', hoplite: '🛡️',
rider: '🐎', chariot: '🛷', catapult: '☄️', godsent: '👼',
big_transporter: '⛴️', small_transporter: '🚤', bireme: '🛶',
attack_ship: '🔥', trireme: '🔱', colonize_ship: '⚓',
medusa: '🐍', zyklop: '👁️', harpy: '🦅', pegasus: '🐴',
minotaur: '🐂', manticore: '🦁', cerberus: '🐕',
hydra: '🐉', sea_monster: '🦑', militia: '🧑‍🌾'
};
window.renderUnitDropdown = function() {
// No-op - selection now happens via modal
};
window.openUnitModal = function() {
const town = window.getSelectedTown();
if (!town) return;
const grid = document.getElementById('unit-grid');
const uData = town.unit_data || {};
// Group units into categories for better display
const categories = {
'Ξηρά': ['sword', 'slinger', 'archer', 'hoplite', 'rider', 'chariot', 'catapult', 'godsent'],
'Ναυτικές': ['big_transporter', 'small_transporter', 'bireme', 'attack_ship', 'trireme', 'colonize_ship'],
'Μυθικές': ['medusa', 'zyklop', 'harpy', 'pegasus', 'minotaur', 'manticore', 'cerberus', 'hydra', 'sea_monster']
};
let html = '';
for (const [catName, units] of Object.entries(categories)) {
let catHtml = '';
for (const key of units) {
if (key === 'militia') continue;
const nameGr = window.UNIT_NAMES_GR[key] || key;
const data = uData[key];
const icon = window.UNIT_ICONS[key] || '💂';
const isSelected = key === window.selectedUnitId;
let statusClass, statusLabel, cardClass = '';
let costStr = '';
let clickable = false;
const requiredGod = window.UNIT_GODS ? window.UNIT_GODS[key] : null;
const isWrongGod = requiredGod && town.god !== requiredGod;
const isNoGod = key === 'godsent' && !town.god;
if (isWrongGod) {
statusClass = 'locked'; statusLabel = '🔒 Άλλος Θεός'; cardClass = 'bld-locked';
const greekGods = { zeus: 'Δία', poseidon: 'Ποσειδώνα', hera: 'Ήρα', athena: 'Αθηνά', hades: 'Άδη', artemis: 'Άρτεμις', aphrodite: 'Αφροδίτη', ares: 'Άρη' };
costStr = `Απαιτεί: ${greekGods[requiredGod] || requiredGod}`;
} else if (isNoGod) {
statusClass = 'locked'; statusLabel = '🔒 Χωρίς Θεό'; cardClass = 'bld-locked';
costStr = `Απαιτείται Ναός`;
} else if (data) {
const missingKeys = data.missing_dependencies ? Object.keys(data.missing_dependencies) : [];
const isLocked = missingKeys.length > 0;
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;
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`;
}
// Show favor if it's a mythical unit or godsent
const favorStr = (data.favor && data.favor > 0) ? ` ⚡:${data.favor}` : '';
costStr = `Ξ:${w} Π:${st} Α:${i}${favorStr} 🧔:${pop} · ⏱ ${tStr}`;
if (isLocked) {
statusClass = 'locked'; statusLabel = '🔒 Κλειδωμένο'; cardClass = 'bld-locked';
} else if (data.enough_resources === false) {
statusClass = 'no-resources'; statusLabel = '❌ Λείπουν Πόροι';
} else {
statusClass = 'can-build'; statusLabel = '✅ Διαθέσιμο';
clickable = true;
}
} else {
// If no data is available for this unit, treat it as locked/unknown
statusClass = 'locked'; statusLabel = '🔒 Άγνωστο'; cardClass = 'bld-locked';
}
const onclick = clickable ? `onclick="window.selectUnit('${key}', '${nameGr}')"` : '';
catHtml += `<div class="bld-card ${cardClass}${isSelected ? ' bld-selected' : ''}" ${onclick} style="width:140px; justify-content:flex-start;">
<span class="bld-icon">${icon}</span>
<span class="bld-name" style="margin-top:6px; font-size:0.85rem;">${nameGr}</span>
<span class="bld-status ${statusClass}">${statusLabel}</span>
<span class="bld-cost" style="font-size:0.65rem;">${costStr}</span>
</div>`;
}
if (catHtml) {
html += `<div style="width:100%; margin-top:10px;"><h4 style="color:#c8a44a; margin-bottom:10px; border-bottom:1px solid #2a4a6a; padding-bottom:4px;">${catName}</h4>
<div style="display:flex; flex-wrap:wrap; gap:10px;">${catHtml}</div>
</div>`;
}
}
grid.innerHTML = html;
document.getElementById('unit-modal-overlay').classList.add('open');
};
window.closeUnitModal = function(e) {
if (e && e.target !== document.getElementById('unit-modal-overlay') && e.target !== document.getElementById('unit-modal-close')) return;
document.getElementById('unit-modal-overlay').classList.remove('open');
};
window.selectUnit = function(key, nameGr) {
window.selectedUnitId = key;
window.updateSelectionDisplay();
window.openUnitModal();
setTimeout(() => document.getElementById('unit-modal-overlay').classList.remove('open'), 180);
};
// ================================================================
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}`;
};