// ================================================================ // 04d_execute_culture.js — Execute Αγορά celebration commands // // Handles commands of type 'culture' from the culture_queue. // Completely separate from the commands table — builds/recruits // are never blocked by a stuck culture command. // // Reports results to /api/culture/result/ (dedicated endpoint). // ================================================================ async function reportCultureResult(queueId, status, msg) { try { await apiFetch(`${BASE_URL}/api/culture/result/${queueId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status, message: msg }) }); } catch (e) { log(`[αγορά] ⚠️ Failed to report result for queue #${queueId}: ${e}`); } } async function executeCultureCommand(cmd) { if (!cmd || !cmd.id || !cmd.payload) return { ok: false, msg: 'No payload' }; // town_id lives on the command root; payload has celebration_type const town_id = cmd.town_id || cmd.payload.town_id; const celebration_type = cmd.payload.celebration_type; const source = cmd.payload.source || 'manual'; if (!celebration_type || !town_id) { await reportCultureResult(cmd.id, 'failed', 'Invalid culture payload: missing celebration_type or town_id'); return { ok: false, msg: 'Invalid payload' }; } if (!['party', 'triumph'].includes(celebration_type)) { await reportCultureResult(cmd.id, 'failed', `Unknown celebration_type: ${celebration_type}`); return { ok: false, msg: 'Unknown type' }; } const label = celebration_type === 'party' ? 'Γιορτή πόλης' : 'Παρέλαση θριάμβου'; log(`[αγορά] Εκτέλεση ${label} για πόλη ${town_id} (${source})`); // Validate town exists in game memory const town = uw.ITowns?.towns?.[town_id]; if (!town) { const msg = `Η πόλη ${town_id} δεν βρέθηκε στη μνήμη του παιχνιδιού.`; await reportCultureResult(cmd.id, 'failed', msg); return { ok: false, msg }; } // Double-check: is there already a celebration of this type running? try { const celebModels = uw.MM.getModels()?.Celebration; if (celebModels) { const nowTs = Math.floor(Date.now() / 1000); for (const cel of Object.values(celebModels)) { const a = cel.attributes; if (String(a.town_id) === String(town_id) && a.celebration_type === celebration_type && (a.finished_at ?? 0) > nowTs) { const msg = `${label} ήδη ενεργή στην πόλη ${town_id}.`; await reportCultureResult(cmd.id, 'failed', msg); return { ok: false, msg }; } } } } catch (e) { /* model not loaded — proceed; server already validated */ } // Human-like reaction delay — same as all other executors const reactionMs = randInt(800, 2500); log(`[αγορά] Waiting ${reactionMs}ms (reaction time)...`); await sleep(reactionMs); if (paused) { await reportCultureResult(cmd.id, 'failed', 'Aborted due to pause/captcha'); return { ok: false, msg: 'Paused' }; } // Fire-and-forget — exact same 3-arg pattern used everywhere in this codebase. // AutoFarm confirms: page=building_place, action=start_celebration, // params={ celebration_type, town_id } uw.gpAjax.ajaxPost('building_place', 'start_celebration', { town_id: parseInt(town_id, 10), celebration_type: celebration_type }); await sleep(500); const successMsg = `${label} εστάλη επιτυχώς (${source}).`; log(`[αγορά] ✅ ${successMsg} (πόλη ${town_id})`); await reportCultureResult(cmd.id, 'done', successMsg); return { ok: true, msg: successMsg }; }