Files
grepo-remote/static/js/components/townViewer.js

207 lines
9.0 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.
// ================================================================
// Town Viewer Component (Left panel + Town detail view)
// ================================================================
window.renderTowns = function() {
const container = document.getElementById('town-list');
if (!window.towns || !window.towns.length) {
container.innerHTML = '<p style="color:#444;font-size:0.8rem;">No towns received yet.</p>';
return;
}
// Get active filters
const searchTerm = (document.getElementById('town-search')?.value || '').toLowerCase();
const reqFullWh = document.getElementById('filter-full-wh')?.checked;
const reqFestival = document.getElementById('filter-festival')?.checked;
const reqPoints = document.getElementById('filter-points')?.checked;
const filteredTowns = window.towns.filter(t => {
// 1. Search by name
const tName = t.town_name || '';
if (searchTerm && !tName.toLowerCase().includes(searchTerm)) return false;
const res = t.resources || {};
const cap = res.storage || 1;
const wPct = (res.wood || 0) / cap;
const sPct = (res.stone || 0) / cap;
const iPct = (res.iron || 0) / cap;
// 2. Full WH (>95%)
if (reqFullWh && Math.max(wPct, sPct, iPct) < 0.95) return false;
// 3. Festival (Wood>15k, Stone>18k, Iron>15k)
// City Festival exact costs = 15000, 18000, 15000
if (reqFestival && ((res.wood || 0) < 15000 || (res.stone || 0) < 18000 || (res.iron || 0) < 15000)) return false;
// 4. Large Points
const pts = typeof t.points === 'number' ? t.points : parseInt(t.points) || 0;
if (reqPoints && pts < 10000) return false;
return true;
});
if (filteredTowns.length === 0) {
container.innerHTML = '<p style="color:#888;font-size:0.85rem;padding:10px;">Καμία πόλη δεν ταιριάζει στα κριτήρια.</p>';
return;
}
const now = Date.now();
container.innerHTML = filteredTowns.map(t => {
const updatedMs = new Date(t.updated_at + 'Z').getTime();
const ageMin = Math.round((now - updatedMs) / 60000);
const stale = ageMin > 3;
const selected = t.town_id === window.selectedTownId ? 'selected' : '';
const res = t.resources || {};
const cap = res.storage || 1;
const wPct = (res.wood || 0) / cap;
const sPct = (res.stone || 0) / cap;
const iPct = (res.iron || 0) / cap;
let markers = '';
if (wPct >= 0.95 || sPct >= 0.95 || iPct >= 0.95) {
markers += '<span title="Γεμάτη Αποθήκη!" style="margin-right:4px;">⚠️</span>';
}
if ((res.wood || 0) >= 15000 && (res.stone || 0) >= 18000 && (res.iron || 0) >= 15000) {
markers += '<span title="Αρκετοί πόροι για Φεστιβάλ!" style="margin-right:4px;">🎭</span>';
}
const getC = (pct) => pct >= 0.95 ? 'color:#ff4a4a;font-weight:bold;' : '';
return `
<div class="town-card ${selected} ${stale ? 'town-stale' : ''}"
onclick="selectTown('${t.town_id}')">
<div class="town-name">${markers}${t.town_name || 'Άγνωστη'}${t.has_premium ? ' <span style="color:#c8a44a;font-size:0.7rem;">★</span>' : ''}</div>
<div class="town-meta">${t.sea != null ? `🌊 Θ${t.sea} · ` : ''}${t.points || 0} pts · ${t.god || 'No god'} · ${ageMin}m ago</div>
<div class="town-res">
<div class="res-item" style="${getC(wPct)}"><div class="res-icon res-wood"></div>${window.fmt(res.wood || 0)}</div>
<div class="res-item" style="${getC(sPct)}"><div class="res-icon res-stone"></div>${window.fmt(res.stone || 0)}</div>
<div class="res-item" style="${getC(iPct)}"><div class="res-icon res-iron"></div>${window.fmt(res.iron || 0)}</div>
<div class="res-item"><div class="res-icon res-pop"></div>${window.fmt(res.population || 0)}</div>
</div>
</div>`;
}).join('');
};
window.selectTown = function(id) {
window.selectedTownId = id;
window.renderTowns();
document.getElementById('no-town-selected').style.display = 'none';
document.getElementById('command-form-wrap').style.display = 'block';
document.getElementById('town-details-panel').style.display = 'block';
window.renderBuildQueuePreview();
window.renderBuildingDropdown();
window.renderUnitDropdown();
window.renderTownDetails();
};
window.getSelectedTown = function() {
return window.towns.find(t => t.town_id === window.selectedTownId);
};
window.renderTownDetails = function() {
const t = window.getSelectedTown();
if(!t) return;
document.getElementById('td-name').textContent = t.town_name || 'Άγνωστη';
const resObj = t.resources || {};
const cap = resObj.storage || 1;
const getCol = (amt) => {
const pct = amt / cap;
if (pct >= 0.95) return 'color: #ff4a4a;'; // Red
if (pct >= 0.85) return 'color: #ffa500;'; // Orange
return 'color: #fff;';
};
// Calculate resources reserved in the bot's pending queues
let reservedWood = 0, reservedStone = 0, reservedIron = 0;
if (window.cmds) {
window.cmds.forEach(cmd => {
if ((cmd.status === 'pending' || cmd.status === 'executing') && cmd.town_id == t.town_id) {
if (cmd.type === 'build' && t.build_data) {
const p = typeof cmd.payload === 'string' ? JSON.parse(cmd.payload) : cmd.payload;
const cost = t.build_data[p.building_id];
if (cost) {
reservedWood += cost.wood || 0;
reservedStone += cost.stone || 0;
reservedIron += cost.iron || 0;
}
} else if (cmd.type === 'recruit' && t.unit_data) {
const p = typeof cmd.payload === 'string' ? JSON.parse(cmd.payload) : cmd.payload;
const amt = parseInt(p.amount) || 1;
const cost = t.unit_data[p.unit_id];
if (cost) {
reservedWood += (cost.wood || 0) * amt;
reservedStone += (cost.stone || 0) * amt;
reservedIron += (cost.iron || 0) * amt;
}
}
}
});
}
const resHtml = (actual, reserved) => {
let base = `<strong style="${getCol(actual)}">${window.fmt(actual)}</strong>`;
if (reserved > 0) {
base += ` <span style="color:#ff6666; font-size:0.75rem;">(-${window.fmt(reserved)})</span>`;
}
return base;
};
const capFmt = (resObj.storage != null && resObj.storage !== '') ? window.fmt(resObj.storage) : '?';
document.getElementById('td-resources').innerHTML = `
<div style="font-size:0.75rem; color:#aaa; margin-bottom: 4px;">Χωρητικότητα: <strong style="color:#eee">${capFmt}</strong></div>
<div>${window.RES_ICONS.wood} Ξύλο: ${resHtml(resObj.wood || 0, reservedWood)}</div>
<div>${window.RES_ICONS.stone} Πέτρα: ${resHtml(resObj.stone || 0, reservedStone)}</div>
<div>${window.RES_ICONS.iron} Ασήμι: ${resHtml(resObj.iron || 0, reservedIron)}</div>
<div style="margin-top: 6px;">${window.RES_ICONS.pop} Πληθυσμός: <strong style="color:#fff">${resObj.population || 0}</strong></div>
`;
const mCap = resObj.market_capacity || 0;
document.getElementById('td-market').innerHTML = `
📦 Εμπορική Χωρητικότητα: <strong>${window.fmt(mCap)}</strong>
`;
const godName = t.god ? t.god.charAt(0).toUpperCase() + t.god.slice(1) : 'Κανένας';
const seaStr = t.sea != null ? `Θ${t.sea}` : '—';
const coordStr = (t.x != null && t.y != null) ? ` (${Math.round(t.x)}:${Math.round(t.y)})` : '';
document.getElementById('td-general').innerHTML = `
<div>Πόντοι: <strong>${t.points}</strong>${t.wonder_points ? ` / Θαύμα: <strong>${t.wonder_points}</strong>` : ''}</div>
<div>Θεός: <strong>${godName}</strong></div>
<div>Θάλασσα: <strong style="color:#6fcfcf">${seaStr}</strong><span style="color:#666;font-size:0.72rem">${coordStr}</span></div>
<div>Παίκτης: <strong style="color:#aaa">${t.player}</strong>${t.has_premium ? ' <span style="color:#c8a44a" title="Premium">★</span>' : ''}</div>
${t.alliance_id ? `<div>Συμμαχία: <strong style="color:#aaa">${t.alliance_id}</strong></div>` : ''}
`;
// ---- Researches ----
const researchObj = t.researches || {};
const researchEntries = Object.entries(researchObj).filter(([k, v]) =>
k !== 'id' && k !== 'town_id' && (v === true || v === 1 || Number(v) > 0)
);
let researchHtml = '';
if (researchEntries.length) {
researchHtml = researchEntries.map(([k]) =>
`<div>✓ ${k.replace(/_/g, ' ')}</div>`
).join('');
} else {
researchHtml = '<div style="color:#555">Καμία έρευνα</div>';
}
document.getElementById('td-researches').innerHTML = researchHtml;
const unitsObj = t.units || {};
let unitsHtml = '';
for(const [unitKey, count] of Object.entries(unitsObj)) {
if(count > 0 && unitKey !== 'militia') {
const name = window.UNIT_NAMES_GR[unitKey] || unitKey;
unitsHtml += `<div>${name}: <strong style="color:#fff">${count}</strong></div>`;
}
}
if(unitsHtml === '') unitsHtml = '<div style="color:#666">Κανένα στράτευμα</div>';
document.getElementById('td-units').innerHTML = unitsHtml;
};