463 lines
21 KiB
HTML
463 lines
21 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="el">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||
<title>Attack Planner — Grepolis Remote</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||
<style>
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
:root{--bg:#0f0f1a;--surf:#181824;--border:#2a2a40;--text:#e0e0e0;--muted:#666;
|
||
--gold:#c8a44a;--red:#e05555;--green:#4acc64;--blue:#6fcfcf;--yellow:#f0c040;--orange:#e07830}
|
||
body{min-height:100vh;background:var(--bg);font-family:'Inter',sans-serif;color:var(--text);padding:1.5rem 2rem}
|
||
.topbar{display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem}
|
||
.topbar h1{font-size:1.6rem;font-weight:800;background:linear-gradient(135deg,#c8a44a,#f0c96e);
|
||
-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;flex:1}
|
||
.back{color:var(--muted);text-decoration:none;font-size:.85rem}
|
||
.back:hover{color:var(--text)}
|
||
.layout{display:grid;grid-template-columns:340px 1fr;gap:1.5rem;align-items:start}
|
||
@media(max-width:900px){.layout{grid-template-columns:1fr}}
|
||
.card{background:var(--surf);border:1px solid var(--border);border-radius:14px;padding:1.25rem;margin-bottom:1rem}
|
||
.ct{font-size:.9rem;font-weight:700;color:var(--gold);margin-bottom:.85rem;padding-bottom:.6rem;border-bottom:1px solid var(--border)}
|
||
label{display:block;font-size:.78rem;color:var(--muted);margin-bottom:.25rem;margin-top:.65rem}
|
||
label:first-of-type{margin-top:0}
|
||
input,select{width:100%;padding:8px 11px;background:#0f0f1a;border:1px solid var(--border);
|
||
border-radius:7px;color:var(--text);font-size:.85rem;font-family:inherit}
|
||
input:focus,select:focus{outline:none;border-color:var(--gold)}
|
||
select option{background:#181824}
|
||
.btn{padding:8px 16px;border:none;border-radius:7px;font-family:inherit;font-weight:600;font-size:.82rem;cursor:pointer;transition:all .2s}
|
||
.btn-gold{background:var(--gold);color:#0f0f1a}.btn-gold:hover{background:#e0b85a}
|
||
.btn-red{background:rgba(224,85,85,.15);color:var(--red);border:1px solid rgba(224,85,85,.35)}.btn-red:hover{background:rgba(224,85,85,.25)}
|
||
.btn-green{background:rgba(74,204,100,.15);color:var(--green);border:1px solid rgba(74,204,100,.35)}.btn-green:hover{background:rgba(74,204,100,.25)}
|
||
.btn-blue{background:rgba(111,207,207,.12);color:var(--blue);border:1px solid rgba(111,207,207,.3)}.btn-blue:hover{background:rgba(111,207,207,.22)}
|
||
.btn-sm{padding:4px 11px;font-size:.75rem}
|
||
.mt{margin-top:.65rem}
|
||
.sep{height:1px;background:var(--border);margin:.85rem 0}
|
||
table{width:100%;border-collapse:collapse;font-size:.8rem}
|
||
th{padding:7px 10px;text-align:left;color:var(--muted);font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;border-bottom:1px solid var(--border)}
|
||
td{padding:8px 10px;border-bottom:1px solid rgba(255,255,255,.04);vertical-align:middle}
|
||
tr:last-child td{border-bottom:none}
|
||
.plan-row{cursor:pointer}.plan-row:hover td{background:rgba(200,164,74,.04)}
|
||
.plan-row.selected td{background:rgba(200,164,74,.08)}
|
||
.badge{display:inline-block;padding:2px 7px;border-radius:5px;font-size:.7rem;font-weight:700}
|
||
.badge-draft{background:rgba(102,102,102,.2);color:#888;border:1px solid #444}
|
||
.badge-active{background:rgba(74,204,100,.15);color:var(--green);border:1px solid rgba(74,204,100,.3)}
|
||
.badge-cancelled{background:rgba(224,85,85,.1);color:var(--red);border:1px solid rgba(224,85,85,.2)}
|
||
.badge-completed{background:rgba(111,207,207,.12);color:var(--blue);border:1px solid rgba(111,207,207,.3)}
|
||
.badge-pending{background:rgba(240,192,64,.1);color:var(--yellow);border:1px solid rgba(240,192,64,.3)}
|
||
.badge-armed{background:rgba(224,120,48,.12);color:var(--orange);border:1px solid rgba(224,120,48,.3)}
|
||
.badge-sent{background:rgba(74,204,100,.15);color:var(--green);border:1px solid rgba(74,204,100,.3)}
|
||
.badge-missed{background:rgba(224,85,85,.15);color:var(--red);border:1px solid rgba(224,85,85,.3)}
|
||
.empty{text-align:center;padding:1.5rem;color:var(--muted);font-size:.85rem}
|
||
.err{background:rgba(224,85,85,.1);border:1px solid rgba(224,85,85,.3);color:var(--red);padding:9px 13px;border-radius:7px;font-size:.8rem;margin-top:.65rem}
|
||
.ok{background:rgba(111,207,207,.08);border:1px solid rgba(111,207,207,.25);color:var(--blue);padding:9px 13px;border-radius:7px;font-size:.8rem;margin-top:.65rem}
|
||
.warn{background:rgba(240,192,64,.07);border:1px solid rgba(240,192,64,.25);color:var(--yellow);padding:9px 13px;border-radius:7px;font-size:.8rem;margin-top:.5rem}
|
||
.unit-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(115px,1fr));gap:.4rem;margin-top:.5rem}
|
||
.ui{display:flex;flex-direction:column;gap:2px}
|
||
.ui label{font-size:.7rem;margin:0}
|
||
.ui input{padding:5px 7px;font-size:.8rem}
|
||
.town-meta{font-size:.73rem;color:var(--muted);margin-top:3px}
|
||
.plan-header{display:flex;gap:.5rem;align-items:flex-start;flex-wrap:wrap;margin-bottom:.85rem}
|
||
.plan-stat{background:#0f0f1a;border:1px solid var(--border);border-radius:8px;padding:8px 13px;font-size:.78rem;flex:1;min-width:120px}
|
||
.plan-stat strong{display:block;font-size:1rem;color:var(--gold);font-weight:700}
|
||
.cd{font-family:monospace;font-weight:700;color:var(--yellow)}
|
||
.fok{color:var(--green)}.ferr{color:var(--red)}
|
||
.section-label{font-size:.78rem;font-weight:700;color:var(--gold);margin-bottom:.4rem}
|
||
#msg{display:none;margin-top:.65rem}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<script>
|
||
window.__GRC_CLAN_KEY = "{{ clan_key }}";
|
||
window.PLAYER_ID = "{{ player_id }}";
|
||
window.WORLD_ID = "{{ world_id }}";
|
||
</script>
|
||
|
||
<div class="topbar">
|
||
<a href="/player/{{ player_id }}/{{ world_id }}" class="back">← Hub</a>
|
||
<h1>⚔️ Attack Planner — {{ world_id }}</h1>
|
||
</div>
|
||
|
||
<div class="layout">
|
||
|
||
<!-- ═══════════════ LEFT: Plan list + Create ═══════════════ -->
|
||
<div>
|
||
<div class="card">
|
||
<div class="ct">➕ Νέο Πλάνο</div>
|
||
<label>Όνομα Πλάνου</label>
|
||
<input id="p-name" placeholder="π.χ. Επίθεση στον Leonidas">
|
||
<label>Στόχος (όνομα)</label>
|
||
<input id="p-tname" placeholder="π.χ. Athens Colony">
|
||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:.5rem">
|
||
<div><label>Στόχος X</label><input id="p-tx" type="number" min="0" max="999"></div>
|
||
<div><label>Στόχος Y</label><input id="p-ty" type="number" min="0" max="999"></div>
|
||
</div>
|
||
<label>Ώρα Άφιξης (τοπική)</label>
|
||
<input id="p-arr" type="datetime-local">
|
||
<div class="warn">⏱ Τουλάχιστον 2 λεπτά στο μέλλον</div>
|
||
<div class="mt"><button class="btn btn-gold" onclick="createPlan()">Δημιουργία →</button></div>
|
||
<div id="msg"></div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<div class="ct">📋 Πλάνα — {{ world_id }}</div>
|
||
<div id="plans-list"><div class="empty">Φόρτωση…</div></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ═══════════════ RIGHT: Plan detail + Add participant ═══════════════ -->
|
||
<div id="right-panel">
|
||
<div class="card" style="border-color:#2a3060">
|
||
<div class="empty" style="color:#444">← Επίλεξε πλάνο για λεπτομέρειες</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<script>
|
||
(function(){
|
||
const PID = window.PLAYER_ID;
|
||
const WID = window.WORLD_ID;
|
||
const KEY = window.__GRC_CLAN_KEY;
|
||
let selPlan = null;
|
||
let allTowns = []; // all clan towns for this world
|
||
|
||
function hdrs(){ return {'Content-Type':'application/json','X-Clan-Key':KEY}; }
|
||
function ts(u){ return u ? new Date(u*1000).toLocaleString('el-GR') : '–'; }
|
||
function badge(s){ return `<span class="badge badge-${s}">${s}</span>`; }
|
||
function showMsg(el,txt,err){el.style.display='block';el.className=err?'err':'ok';el.textContent=txt;}
|
||
|
||
// ─── unit groups ───
|
||
const LAND = ['swordsman','slinger','archer','hoplite','horseman','chariot','catapult'];
|
||
const NAVAL = ['bireme','attack_ship','demolition_ship','transport_ship','colonize_ship'];
|
||
|
||
// ─── Load all clan towns for this world ───
|
||
async function loadClanTowns(){
|
||
try{
|
||
const r = await fetch(`/dashboard/clan-towns?world_id=${WID}`);
|
||
allTowns = await r.json();
|
||
}catch(e){ allTowns=[]; }
|
||
}
|
||
|
||
// ─── Load plans list ───
|
||
async function loadPlans(){
|
||
const r = await fetch(`/api/${WID}/attack_plans`);
|
||
const data = await r.json();
|
||
const el = document.getElementById('plans-list');
|
||
if(!Array.isArray(data)||!data.length){
|
||
el.innerHTML='<div class="empty">Δεν υπάρχουν πλάνα.</div>'; return;
|
||
}
|
||
let h=`<table><thead><tr><th>Πλάνο</th><th>Στόχος</th><th>Άφιξη</th><th>Status</th><th>Συμμ.</th></tr></thead><tbody>`;
|
||
for(const p of data){
|
||
const sel = selPlan===p.id ? ' selected':'';
|
||
h+=`<tr class="plan-row${sel}" onclick="selectPlan(${p.id})">
|
||
<td><strong>${p.plan_name}</strong></td>
|
||
<td style="font-size:.75rem">${p.target_town_name||'–'} ${p.target_x?`(${p.target_x},${p.target_y})`:''}</td>
|
||
<td style="font-size:.72rem">${ts(p.target_arrival_time)}</td>
|
||
<td>${badge(p.status)}</td>
|
||
<td style="text-align:center">${p.participant_count}</td>
|
||
</tr>`;
|
||
}
|
||
h+='</tbody></table>';
|
||
el.innerHTML=h;
|
||
}
|
||
|
||
// ─── Select plan ───
|
||
window.selectPlan = async function(id){
|
||
selPlan = id;
|
||
await loadPlans();
|
||
await renderPlanDetail(id);
|
||
};
|
||
|
||
async function renderPlanDetail(id){
|
||
const r = await fetch(`/api/${WID}/attack_plans/${id}`);
|
||
const plan = await r.json();
|
||
if(plan.error){ return; }
|
||
|
||
const parts = plan.participants||[];
|
||
const isDraft = plan.status==='draft';
|
||
|
||
// Header stats
|
||
let hdr = `
|
||
<div class="plan-stat"><span style="color:var(--muted);font-size:.7rem">ΣΤΟΧΟΣ</span>
|
||
<strong>${plan.target_town_name||'–'}</strong>
|
||
<span style="font-size:.75rem;color:var(--muted)">(${plan.target_x||'?'}, ${plan.target_y||'?'})</span>
|
||
</div>
|
||
<div class="plan-stat"><span style="color:var(--muted);font-size:.7rem">ΑΦΙΞΗ</span>
|
||
<strong style="font-size:.85rem">${ts(plan.target_arrival_time)}</strong>
|
||
</div>
|
||
<div class="plan-stat"><span style="color:var(--muted);font-size:.7rem">STATUS</span>
|
||
<strong>${badge(plan.status)}</strong>
|
||
</div>
|
||
<div class="plan-stat"><span style="color:var(--muted);font-size:.7rem">ΣΥΜΜΕΤΕΧΟΝΤΕΣ</span>
|
||
<strong>${parts.length}</strong>
|
||
</div>`;
|
||
|
||
// Action buttons
|
||
let btns = '';
|
||
if(isDraft){
|
||
btns=`<button class="btn btn-green btn-sm" onclick="armPlan(${id})">▶ ARM</button>`;
|
||
}
|
||
btns+=` <button class="btn btn-red btn-sm" onclick="cancelPlan(${id})">✕ Ακύρωση</button>`;
|
||
|
||
// Participants table
|
||
let ptable = '<div class="empty">Χωρίς συμμετέχοντες ακόμη.</div>';
|
||
if(parts.length){
|
||
let latest = 0;
|
||
let ptrows = '';
|
||
for(const p of parts){
|
||
if(p.return_time>latest) latest=p.return_time;
|
||
const units = p.units ? Object.entries(p.units).filter(([,v])=>v>0).map(([k,v])=>`${k}:${v}`).join(', ') : '–';
|
||
ptrows+=`<tr>
|
||
<td>
|
||
<div style="font-weight:600">${p.origin_town_name||p.origin_town_id}</div>
|
||
<div style="font-size:.72rem;color:var(--muted)">${p.player_name||p.player_id||''}</div>
|
||
</td>
|
||
<td style="font-size:.72rem;color:var(--blue)">${units||'–'}</td>
|
||
<td>${p.transport_needed?`🚢 ${p.transport_count}`:'–'}</td>
|
||
<td style="font-size:.72rem">${ts(p.send_time)}</td>
|
||
<td style="font-size:.72rem">${ts(p.return_time)}</td>
|
||
<td>${badge(p.status)}</td>
|
||
<td>${p.is_feasible?'<span class="fok">✅</span>':`<span class="ferr" title="${p.error_msg||''}">❌</span>`}</td>
|
||
<td><button class="btn btn-red btn-sm" onclick="removeParticipant(${id},'${p.origin_town_id}')">✕</button></td>
|
||
</tr>`;
|
||
}
|
||
ptable=`
|
||
<table>
|
||
<thead><tr>
|
||
<th>Πόλη / Παίκτης</th><th>Στρατός</th><th>Πλοία</th>
|
||
<th>Αποστολή</th><th>Επιστροφή</th><th>Status</th><th>OK</th><th></th>
|
||
</tr></thead>
|
||
<tbody>${ptrows}</tbody>
|
||
</table>`;
|
||
if(latest){
|
||
ptable+=`<div class="ok" style="margin-top:.65rem">🏠 Τελευταία επιστροφή: <strong>${ts(latest)}</strong></div>`;
|
||
}
|
||
}
|
||
|
||
// Add participant form
|
||
const addForm = buildAddForm(id, isDraft);
|
||
|
||
document.getElementById('right-panel').innerHTML=`
|
||
<div class="card">
|
||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.85rem">
|
||
<div class="ct" style="margin:0;border:none;padding:0">📌 ${plan.plan_name}</div>
|
||
<div style="display:flex;gap:.4rem">${btns}</div>
|
||
</div>
|
||
<div class="plan-header">${hdr}</div>
|
||
<div class="sep"></div>
|
||
<div class="section-label">👥 Συμμετέχοντες</div>
|
||
${ptable}
|
||
</div>
|
||
${isDraft ? addForm : ''}
|
||
`;
|
||
|
||
// Populate player dropdown
|
||
if(isDraft) populatePlayerDropdown();
|
||
}
|
||
|
||
// ─── Build "Add Participant" form ───
|
||
function buildAddForm(planId, isDraft){
|
||
if(!isDraft) return '';
|
||
|
||
// Build unit inputs (land + naval groups)
|
||
let landInputs = LAND.map(u=>`
|
||
<div class="ui"><label>${u}</label>
|
||
<input type="number" id="u_${u}" min="0" value="0"></div>`).join('');
|
||
let navalInputs = NAVAL.map(u=>`
|
||
<div class="ui"><label>${u}</label>
|
||
<input type="number" id="u_${u}" min="0" value="0"></div>`).join('');
|
||
|
||
return `
|
||
<div class="card" id="add-form">
|
||
<div class="ct">👤 Προσθήκη Συμμετέχοντα</div>
|
||
|
||
<label>Παίκτης</label>
|
||
<select id="ap-player" onchange="onPlayerChange()">
|
||
<option value="">— Επίλεξε παίκτη —</option>
|
||
</select>
|
||
|
||
<label>Πόλη</label>
|
||
<select id="ap-town" onchange="onTownChange()">
|
||
<option value="">— Επίλεξε πόλη —</option>
|
||
</select>
|
||
<div class="town-meta" id="ap-meta"></div>
|
||
|
||
<div class="sep"></div>
|
||
|
||
<div class="section-label">🗡 Χερσαίος Στρατός</div>
|
||
<div class="unit-grid">${landInputs}</div>
|
||
|
||
<div class="section-label" style="margin-top:.75rem">⚓ Ναυτικός Στρατός</div>
|
||
<div class="unit-grid">${navalInputs}</div>
|
||
|
||
<div class="warn" style="margin-top:.65rem">
|
||
💡 Αφήσε 0 σε μονάδες που δεν συμμετέχουν. Τα πλοία μεταφοράς υπολογίζονται αυτόματα.
|
||
</div>
|
||
|
||
<div class="mt" style="display:flex;gap:.5rem">
|
||
<button class="btn btn-gold" onclick="addParticipant(${planId})">Υπολογισμός & Προσθήκη</button>
|
||
<button class="btn btn-blue" onclick="fillFromGame()">📥 Φόρτωση από game</button>
|
||
</div>
|
||
<div id="ap-result"></div>
|
||
</div>`;
|
||
}
|
||
|
||
// ─── Populate player dropdown from allTowns ───
|
||
function populatePlayerDropdown(){
|
||
const sel = document.getElementById('ap-player');
|
||
if(!sel) return;
|
||
const seen = {};
|
||
allTowns.forEach(t=>{ seen[t.player_id]=t.player; });
|
||
while(sel.options.length>1) sel.remove(1);
|
||
for(const [pid,pname] of Object.entries(seen)){
|
||
const opt=document.createElement('option');
|
||
opt.value=pid; opt.textContent=pname||pid;
|
||
sel.appendChild(opt);
|
||
}
|
||
}
|
||
|
||
// ─── Player change → filter town dropdown ───
|
||
window.onPlayerChange = function(){
|
||
const pid = document.getElementById('ap-player').value;
|
||
const tsel = document.getElementById('ap-town');
|
||
while(tsel.options.length>1) tsel.remove(1);
|
||
document.getElementById('ap-meta').textContent='';
|
||
if(!pid) return;
|
||
allTowns.filter(t=>t.player_id===pid).forEach(t=>{
|
||
const opt=document.createElement('option');
|
||
opt.value=t.town_id;
|
||
opt.textContent=`${t.town_name} (${t.x}, ${t.y})`;
|
||
tsel.appendChild(opt);
|
||
});
|
||
};
|
||
|
||
// ─── Town change → show meta + fill units from game data ───
|
||
window.onTownChange = function(){
|
||
const tid = document.getElementById('ap-town').value;
|
||
const meta = document.getElementById('ap-meta');
|
||
if(!tid){ meta.textContent=''; return; }
|
||
const town = allTowns.find(t=>String(t.town_id)===String(tid));
|
||
if(!town){ return; }
|
||
meta.innerHTML=`🗺 X:<strong>${town.x}</strong> Y:<strong>${town.y}</strong> Sea:<strong>${town.sea}</strong>`;
|
||
};
|
||
|
||
// ─── Fill units from game data (if available) ───
|
||
window.fillFromGame = function(){
|
||
const tid = document.getElementById('ap-town').value;
|
||
if(!tid){ return; }
|
||
const town = allTowns.find(t=>String(t.town_id)===String(tid));
|
||
if(!town||!town.units) return;
|
||
const all=[...LAND,...NAVAL];
|
||
all.forEach(u=>{
|
||
const el=document.getElementById(`u_${u}`);
|
||
if(el && town.units[u]!==undefined) el.value=town.units[u]||0;
|
||
});
|
||
};
|
||
|
||
// ─── Add participant ───
|
||
window.addParticipant = async function(planId){
|
||
const result=document.getElementById('ap-result');
|
||
const tid = document.getElementById('ap-town').value;
|
||
if(!tid){ result.innerHTML='<div class="err">❌ Επίλεξε πόλη</div>'; return; }
|
||
const town = allTowns.find(t=>String(t.town_id)===String(tid));
|
||
if(!town){ result.innerHTML='<div class="err">❌ Πόλη δεν βρέθηκε</div>'; return; }
|
||
|
||
const units={};
|
||
[...LAND,...NAVAL].forEach(u=>{
|
||
const v=parseInt(document.getElementById(`u_${u}`)?.value)||0;
|
||
if(v>0) units[u]=v;
|
||
});
|
||
if(!Object.keys(units).length){ result.innerHTML='<div class="err">❌ Πρόσθεσε τουλάχιστον 1 μονάδα</div>'; return; }
|
||
|
||
const body={
|
||
requester_player_id: PID,
|
||
player_id: town.player_id,
|
||
player_name: town.player,
|
||
origin_town_id: town.town_id,
|
||
origin_town_name: town.town_name,
|
||
origin_x: town.x,
|
||
origin_y: town.y,
|
||
origin_sea: town.sea,
|
||
units
|
||
};
|
||
|
||
const r = await fetch(`/api/${WID}/attack_plans/${planId}/participants`,
|
||
{method:'POST', headers:hdrs(), body:JSON.stringify(body)});
|
||
const data = await r.json();
|
||
|
||
if(data.is_feasible!==undefined){
|
||
if(data.is_feasible){
|
||
result.innerHTML=`<div class="ok">
|
||
✅ Feasible — Χρόνος: ${Math.floor(data.travel_time_secs/60)}λεπτά
|
||
| Πλοία: ${data.transport_count||0}
|
||
| Αποστολή: ${ts(data.send_time)}
|
||
| Επιστροφή: ${ts(data.return_time)}
|
||
</div>`;
|
||
} else {
|
||
result.innerHTML=`<div class="err">❌ ${data.error_msg}</div>`;
|
||
}
|
||
renderPlanDetail(planId);
|
||
} else {
|
||
result.innerHTML=`<div class="err">❌ ${data.error||'Σφάλμα'}</div>`;
|
||
}
|
||
};
|
||
|
||
// ─── Remove participant ───
|
||
window.removeParticipant = async function(planId,townId){
|
||
if(!confirm('Αφαίρεση;')) return;
|
||
await fetch(`/api/${WID}/attack_plans/${planId}/participants/${townId}`,
|
||
{method:'DELETE', headers:hdrs(), body:JSON.stringify({requester_player_id:PID})});
|
||
renderPlanDetail(planId); loadPlans();
|
||
};
|
||
|
||
// ─── Arm plan ───
|
||
window.armPlan = async function(id){
|
||
const r=await fetch(`/api/${WID}/attack_plans/${id}/arm`,
|
||
{method:'POST', headers:hdrs(), body:JSON.stringify({player_id:PID})});
|
||
const d=await r.json();
|
||
alert(d.ok?'✅ Πλάνο ενεργοποιήθηκε!':'❌ '+d.error);
|
||
renderPlanDetail(id); loadPlans();
|
||
};
|
||
|
||
// ─── Cancel plan ───
|
||
window.cancelPlan = async function(id){
|
||
if(!confirm('Ακύρωση πλάνου;')) return;
|
||
await fetch(`/api/${WID}/attack_plans/${id}/cancel`,
|
||
{method:'POST', headers:hdrs(), body:JSON.stringify({player_id:PID})});
|
||
selPlan=null; loadPlans();
|
||
document.getElementById('right-panel').innerHTML=
|
||
'<div class="card" style="border-color:#2a3060"><div class="empty" style="color:#444">← Επίλεξε πλάνο για λεπτομέρειες</div></div>';
|
||
};
|
||
|
||
// ─── Create plan ───
|
||
window.createPlan = async function(){
|
||
const msg=document.getElementById('msg');
|
||
const dt=document.getElementById('p-arr').value;
|
||
if(!dt){ showMsg(msg,'Επίλεξε ώρα άφιξης',true); return; }
|
||
const r=await fetch(`/api/${WID}/attack_plans`,{method:'POST',headers:hdrs(),body:JSON.stringify({
|
||
player_id:PID,
|
||
plan_name:document.getElementById('p-name').value.trim()||'Επίθεση',
|
||
target_town_name:document.getElementById('p-tname').value.trim(),
|
||
target_x:parseFloat(document.getElementById('p-tx').value)||null,
|
||
target_y:parseFloat(document.getElementById('p-ty').value)||null,
|
||
target_arrival_time:Math.floor(new Date(dt).getTime()/1000)
|
||
})});
|
||
const d=await r.json();
|
||
if(d.ok){ showMsg(msg,`✅ Πλάνο δημιουργήθηκε (ID:${d.plan_id})`,false); loadPlans(); }
|
||
else showMsg(msg,'❌ '+d.error,true);
|
||
};
|
||
|
||
// ─── Init ───
|
||
async function init(){
|
||
await loadClanTowns();
|
||
await loadPlans();
|
||
setInterval(loadPlans, 15000);
|
||
setInterval(()=>{ if(selPlan) renderPlanDetail(selPlan); }, 15000);
|
||
}
|
||
init();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|