38676-vm/gm_console.php
2026-02-22 03:37:20 +00:00

448 lines
25 KiB
PHP
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.

<?php
require_once 'db/config.php';
session_start();
$db = db();
// Auth Check: Must be logged in and be admin or gm
if (!isset($_SESSION['user_id'])) {
header("Location: auth.php?page=login");
exit;
}
$user_id = $_SESSION['user_id'];
$user_stmt = $db->prepare("SELECT role FROM users WHERE id = ?");
$user_stmt->execute([$user_id]);
$current_user = $user_stmt->fetch();
if (!$current_user || ($current_user['role'] !== 'admin' && $current_user['role'] !== 'gm')) {
die("Accès refusé. Vous devez être un Maître du Jeu (MJ) pour accéder à cette console.");
}
$is_admin = ($current_user['role'] === 'admin');
// Fetch Dynamic Types, Statuses, and Settlement Types - Sorted Alphabetically
$object_types_db = $db->query("SELECT * FROM celestial_object_types ORDER BY name ASC")->fetchAll(PDO::FETCH_ASSOC);
$statuses_db = $db->query("SELECT * FROM celestial_object_statuses ORDER BY name ASC")->fetchAll(PDO::FETCH_ASSOC);
$settlement_types_db = $db->query("SELECT * FROM settlement_types ORDER BY name ASC")->fetchAll(PDO::FETCH_ASSOC);
$object_types_map = []; foreach($object_types_db as $ot) $object_types_map[$ot['slug']] = $ot;
$statuses_map = []; foreach($statuses_db as $s) $statuses_map[$s['slug']] = $s;
// Handle Planet/Slot Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update_slot') {
$slot_id = (int)$_POST['slot_id'];
$galaxy_id = (int)$_POST['galaxy_id'];
$sector_id = (int)$_POST['sector_id'];
$slot_num = (int)$_POST['slot_num'];
$name = $_POST['name'];
$type = $_POST['type'];
$status = $_POST['status'];
$orbital = (int)$_POST['orbital_control'];
$terrestrial = (int)$_POST['terrestrial_control'];
if ($type === 'empty') {
if ($slot_id > 0) {
$db->prepare("DELETE FROM cities WHERE planet_id = ?")->execute([$slot_id]);
$db->prepare("DELETE FROM planets WHERE id = ?")->execute([$slot_id]);
}
} else {
if ($slot_id > 0) {
$stmt = $db->prepare("UPDATE planets SET name = ?, type = ?, status = ?, orbital_control = ?, terrestrial_control = ? WHERE id = ?");
$stmt->execute([$name, $type, $status, $orbital, $terrestrial, $slot_id]);
$planet_id = $slot_id;
} else {
$stmt = $db->prepare("INSERT INTO planets (galaxy_id, sector_id, slot, name, type, status, orbital_control, terrestrial_control) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
$stmt->execute([$galaxy_id, $sector_id, $slot_num, $name, $type, $status, $orbital, $terrestrial]);
$planet_id = $db->lastInsertId();
}
// Handle Multiple Settlements
$sent_city_ids = [];
if (isset($_POST['cities']) && is_array($_POST['cities'])) {
foreach ($_POST['cities'] as $city_data) {
if (empty($city_data['name'])) continue;
$c_id = (int)($city_data['id'] ?? 0);
$c_name = $city_data['name'];
$c_type_id = (int)$city_data['type_id'];
if ($c_id > 0) {
$stmt = $db->prepare("UPDATE cities SET name = ?, settlement_type_id = ? WHERE id = ? AND planet_id = ?");
$stmt->execute([$c_name, $c_type_id, $c_id, $planet_id]);
$sent_city_ids[] = $c_id;
} else {
$stmt = $db->prepare("INSERT INTO cities (planet_id, name, settlement_type_id) VALUES (?, ?, ?)");
$stmt->execute([$planet_id, $c_name, $c_type_id]);
$sent_city_ids[] = $db->lastInsertId();
}
}
}
// Delete cities that were removed in the UI
if ($planet_id > 0) {
if (empty($sent_city_ids)) {
$db->prepare("DELETE FROM cities WHERE planet_id = ?")->execute([$planet_id]);
} else {
$placeholders = implode(',', array_fill(0, count($sent_city_ids), '?'));
$stmt = $db->prepare("DELETE FROM cities WHERE planet_id = ? AND id NOT IN ($placeholders)");
$params = array_merge([$planet_id], $sent_city_ids);
$stmt->execute($params);
}
}
}
header("Location: gm_console.php?view=sector&galaxy_id=$galaxy_id&sector_id=$sector_id&success=1");
exit;
}
// Handle Sector Update
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update_sector') {
$sector_id = (int)$_POST['sector_id'];
$galaxy_id = (int)$_POST['galaxy_id'];
$s_name = $_POST['sector_name'];
$s_status = $_POST['sector_status'];
$stmt = $db->prepare("SELECT id FROM sectors WHERE id = ?");
$stmt->execute([$sector_id]);
if ($stmt->fetch()) {
$db->prepare("UPDATE sectors SET name = ?, status = ? WHERE id = ?")->execute([$s_name, $s_status, $sector_id]);
} else {
$db->prepare("INSERT INTO sectors (id, name, status, galaxy_id) VALUES (?, ?, ?, ?)")->execute([$s_name, $s_status, $sector_id, $galaxy_id]);
}
header("Location: gm_console.php?view=sector&galaxy_id=$galaxy_id&sector_id=$sector_id&success=1");
exit;
}
$view = isset($_GET['view']) ? $_GET['view'] : 'galaxy';
$galaxy_id = isset($_GET['galaxy_id']) ? (int)$_GET['galaxy_id'] : 1;
$sector_id = isset($_GET['sector_id']) ? (int)$_GET['sector_id'] : 1;
$grid_size = 36;
if ($view === 'sector') {
// Fetch planets
$stmt = $db->prepare("SELECT * FROM planets WHERE galaxy_id = ? AND sector_id = ? AND slot BETWEEN 1 AND ?");
$stmt->execute([$galaxy_id, $sector_id, $grid_size]);
$objects_raw = $stmt->fetchAll();
$grid = array_fill(1, $grid_size, null);
$planet_ids = [];
foreach ($objects_raw as $obj) {
$grid[$obj['slot']] = $obj;
$planet_ids[] = $obj['id'];
$grid[$obj['slot']]['cities'] = [];
}
// Fetch ALL cities for these planets
if (!empty($planet_ids)) {
$placeholders = implode(',', array_fill(0, count($planet_ids), '?'));
$stmt = $db->prepare("SELECT * FROM cities WHERE planet_id IN ($placeholders)");
$stmt->execute($planet_ids);
$all_cities = $stmt->fetchAll();
foreach ($all_cities as $city) {
foreach ($grid as &$slot_data) {
if ($slot_data && $slot_data['id'] == $city['planet_id']) {
$slot_data['cities'][] = $city;
}
}
}
}
$stmt = $db->prepare("SELECT * FROM sectors WHERE id = ?");
$stmt->execute([$sector_id]);
$sector_info = $stmt->fetch();
} elseif ($view === 'galaxy') {
$stmt = $db->prepare("SELECT sector_id, slot, status, type FROM planets WHERE galaxy_id = ? ORDER BY sector_id, slot ASC");
$stmt->execute([$galaxy_id]);
$all_planets = $stmt->fetchAll();
$sector_data = [];
foreach ($all_planets as $p) {
$sector_data[$p['sector_id']][$p['slot']] = ['status' => $p['status'], 'type' => $p['type']];
}
$stmt = $db->prepare("SELECT id, status FROM sectors WHERE galaxy_id = ?");
$stmt->execute([$galaxy_id]);
$global_sector_statuses = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
}
function getStatusColor($status, $type, $statuses_map, $object_types_map) {
if (isset($statuses_map[$status])) return $statuses_map[$status]['color'];
return 'rgba(255,255,255,0.05)';
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Console MJ - Nexus</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<link href="assets/css/custom.css?v=<?php echo time(); ?>" rel="stylesheet">
<style>
body { background: #000; color: #fff; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 0; }
header { background: #1a202c; padding: 10px 20px; border-bottom: 2px solid #2d3545; display: flex; justify-content: space-between; align-items: center; }
.nav-links a { color: #88c0d0; text-decoration: none; margin-right: 20px; font-weight: bold; font-size: 14px; }
.nav-links a:hover { color: #fff; }
.container { padding: 20px; display: flex; flex-direction: column; align-items: center; }
.galaxy-map { background: rgba(0,0,0,0.9); border: 2px solid #2d3545; display: grid; grid-template-columns: repeat(6, 120px); grid-template-rows: repeat(6, 100px); gap: 1px; }
.slot { background: #050505; border: 1px solid #1a1a1a; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; cursor: pointer; transition: all 0.2s; }
.slot:hover { background: #1a2a4a; border-color: #88c0d0; transform: scale(1.05); z-index: 10; }
.slot-id { position: absolute; top: 2px; left: 5px; font-size: 9px; color: #444; }
.object-icon { font-size: 24px; margin-bottom: 5px; }
.object-name { font-size: 10px; font-weight: bold; color: #3b82f6; text-align: center; max-width: 90%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sector-grid { display: grid; grid-template-columns: repeat(6, 160px); grid-template-rows: repeat(6, 130px); gap: 10px; }
.sector-card { background: rgba(10,15,30,0.9); border: 1px solid #3b4252; display: flex; flex-direction: column; align-items: center; justify-content: center; cursor: pointer; text-decoration: none; color: #fff; padding: 10px; position: relative; transition: all 0.2s; }
.sector-card:hover { border-color: #88c0d0; background: #1a2a4a; }
.mini-map { display: grid; grid-template-columns: repeat(6, 8px); gap: 2px; background: #000; padding: 5px; border: 1px solid #4c566a; }
.mini-dot { width: 8px; height: 8px; background: rgba(255,255,255,0.05); }
.sector-status-label { position: absolute; top: 5px; right: 5px; font-size: 8px; padding: 2px 5px; border-radius: 3px; background: rgba(0,0,0,0.5); }
#editModal, #sectorModal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 1000; justify-content: center; align-items: center; }
.modal-content { background: #1e293b; padding: 25px; border: 2px solid #88c0d0; width: 550px; box-shadow: 0 0 50px rgba(136, 192, 208, 0.2); max-height: 90vh; overflow-y: auto; border-radius: 8px; }
.modal-content h3 { margin-top: 0; color: #88c0d0; font-size: 18px; border-bottom: 1px solid #334155; padding-bottom: 10px; margin-bottom: 20px; }
.form-group { margin-bottom: 15px; }
.form-group label { display: block; font-size: 11px; color: #8c92a3; margin-bottom: 5px; text-transform: uppercase; letter-spacing: 0.5px; }
.form-group input, .form-group select { width: 100%; background: #0f172a; border: 1px solid #334155; color: #fff; padding: 8px 12px; box-sizing: border-box; border-radius: 4px; font-size: 13px; }
.form-group input:focus, .form-group select:focus { border-color: #88c0d0; outline: none; }
.btn-save { background: #a3be8c; color: #000; border: none; padding: 12px 20px; font-weight: bold; cursor: pointer; width: 100%; font-size: 14px; margin-top: 10px; border-radius: 4px; text-transform: uppercase; }
.btn-cancel { background: #4c566a; color: #fff; border: none; padding: 10px 20px; font-weight: bold; cursor: pointer; width: 100%; margin-top: 10px; border-radius: 4px; font-size: 12px; }
.settlement-item { background: rgba(15, 23, 42, 0.5); padding: 12px; border: 1px solid #334155; margin-bottom: 10px; position: relative; border-radius: 6px; }
.btn-remove-settlement { position: absolute; top: -8px; right: -8px; background: #bf616a; color: #fff; border: none; width: 20px; height: 20px; border-radius: 50%; cursor: pointer; font-size: 10px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 4px rgba(0,0,0,0.3); }
.btn-add-settlement { background: transparent; color: #88c0d0; border: 1px dashed #88c0d0; padding: 8px 15px; font-weight: bold; cursor: pointer; font-size: 11px; margin-bottom: 20px; width: 100%; border-radius: 4px; transition: all 0.2s; }
.btn-add-settlement:hover { background: rgba(136, 192, 208, 0.1); }
.compact-row { display: flex; gap: 12px; align-items: flex-end; }
.compact-row .form-group { margin-bottom: 0; flex: 1; }
</style>
</head>
<body>
<header>
<div style="display: flex; align-items: center; gap: 20px;">
<h2 style="margin: 0; color: #ebcb8b;"><i class="fa-solid fa-headset"></i> CONSOLE MJ</h2>
<nav class="nav-links">
<a href="?view=galaxy"><i class="fa-solid fa-braille"></i> Galaxie</a>
<?php if ($is_admin): ?>
<a href="admin.php" target="_blank"><i class="fa-solid fa-shield-halved"></i> Console Admin</a>
<?php endif; ?>
<a href="index.php"><i class="fa-solid fa-eye"></i> Vue Joueur</a>
</nav>
</div>
<div>
<span style="color: #8c92a3; font-size: 12px;">Maître du Jeu: </span>
<span style="font-weight: bold; color: #88c0d0;"><?php echo htmlspecialchars($_SESSION['username']); ?></span>
</div>
</header>
<div class="container">
<?php if ($view === 'galaxy'): ?>
<h3 style="color: #88c0d0;">Sélecteur de Secteur</h3>
<div class="sector-grid">
<?php for($s=1; $s<=$grid_size; $s++):
$sStatus = $global_sector_statuses[$s] ?? 'unexplored';
?>
<a href="?view=sector&galaxy_id=<?php echo $galaxy_id; ?>&sector_id=<?php echo $s; ?>" class="sector-card">
<span class="sector-status-label" style="background: <?php echo ($sStatus == 'stable' ? '#a3be8c' : ($sStatus == 'hostile' ? '#bf616a' : ($sStatus == 'war' ? '#ef4444' : '#4c566a'))); ?>; color: #000; font-weight: bold;"><?php echo strtoupper($sStatus); ?></span>
<div class="mini-map">
<?php for($p=1; $p<=$grid_size; $p++):
$dotColor = 'rgba(255,255,255,0.05)';
if (isset($sector_data[$s][$p])) { $dotColor = getStatusColor($sector_data[$s][$p]['status'], $sector_data[$s][$p]['type'], $statuses_map, $object_types_map); }
?>
<div class="mini-dot" style="background-color: <?php echo $dotColor; ?>;"></div>
<?php endfor; ?>
</div>
<div style="font-size: 14px; font-weight: bold; margin-top: 5px;">SECTEUR <?php echo $s; ?></div>
</a>
<?php endfor; ?>
</div>
<?php elseif ($view === 'sector'): ?>
<div style="display: flex; justify-content: space-between; width: 100%; max-width: 720px; align-items: center; margin-bottom: 20px;">
<div style="display: flex; align-items: center; gap: 15px;">
<a href="?view=galaxy" style="color: #88c0d0; text-decoration: none;"><i class="fa-solid fa-arrow-left"></i> Retour</a>
<h3 style="color: #88c0d0; margin: 0;">Secteur <?php echo $sector_id; ?>: <?php echo htmlspecialchars($sector_info['name'] ?? "Secteur $sector_id"); ?></h3>
</div>
<button onclick="editSector()" style="background: #5e81ac; border: none; color: #fff; padding: 8px 15px; cursor: pointer; font-size: 12px; font-weight: bold; border-radius: 4px;"><i class="fa-solid fa-pen-to-square"></i> MODIFIER SECTEUR</button>
</div>
<div class="galaxy-map">
<?php for($i=1; $i<=$grid_size; $i++): ?>
<div class="slot" onclick='editSlot(<?php echo $i; ?>, <?php echo json_encode($grid[$i] ?? null); ?>)'>
<span class="slot-id"><?php echo $i; ?></span>
<?php if (isset($grid[$i])): $obj = $grid[$i]; ?>
<div class="object-icon">
<?php
$icon = $object_types_map[$obj['type']]['icon'] ?? 'fa-circle';
$color = getStatusColor($obj['status'], $obj['type'], $statuses_map, $object_types_map);
?>
<i class="fa-solid <?php echo $icon; ?>" style="color: <?php echo $color; ?>;"></i>
</div>
<span class="object-name"><?php echo htmlspecialchars($obj['name']); ?></span>
<?php else: ?>
<div style="opacity: 0.1;"><i class="fa-solid fa-plus"></i></div>
<?php endif; ?>
</div>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
<!-- Edit Slot Modal -->
<div id="editModal">
<div class="modal-content">
<h3 id="modalTitle">Éditer Case</h3>
<form method="POST">
<input type="hidden" name="action" value="update_slot">
<input type="hidden" name="slot_id" id="form_slot_id">
<input type="hidden" name="slot_num" id="form_slot_num">
<input type="hidden" name="galaxy_id" value="<?php echo $galaxy_id; ?>">
<input type="hidden" name="sector_id" value="<?php echo $sector_id; ?>">
<div style="display: flex; gap: 15px;">
<div class="form-group" style="flex: 2;">
<label>Nom de l'objet / Planète</label>
<input type="text" name="name" id="form_name" placeholder="Ex: Terra Nova..." required>
</div>
<div class="form-group" style="flex: 1;">
<label>Type d'objet</label>
<select name="type" id="form_type">
<option value="empty">VIDE (Suppr)</option>
<?php foreach($object_types_db as $ot): ?>
<option value="<?php echo $ot['slug']; ?>"><?php echo $ot['name']; ?></option>
<?php endforeach; ?>
</select>
</div>
</div>
<div class="form-group">
<label>Statut / État</label>
<select name="status" id="form_status">
<?php foreach($statuses_db as $st): ?>
<option value="<?php echo $st['slug']; ?>"><?php echo $st['name']; ?></option>
<?php endforeach; ?>
</select>
</div>
<div style="background: rgba(0,0,0,0.1); padding: 15px; border-radius: 6px; border: 1px solid #334155; margin-bottom: 20px;">
<label style="font-size: 11px; color: #88c0d0; font-weight: bold; display: block; margin-bottom: 15px; text-align: center; border-bottom: 1px solid #334155; padding-bottom: 8px;">ÉTABLISSEMENTS</label>
<div id="settlementsContainer">
<!-- Settlements will be injected here -->
</div>
<button type="button" class="btn-add-settlement" onclick="addSettlementRow()"><i class="fa-solid fa-plus"></i> AJOUTER UN ÉTABLISSEMENT</button>
</div>
<div style="display: flex; gap: 15px;">
<div class="form-group" style="flex: 1;">
<label>Contrôle Orbital (%)</label>
<input type="number" name="orbital_control" id="form_orbital" min="0" max="100" value="0">
</div>
<div class="form-group" style="flex: 1;">
<label>Contrôle Sol (%)</label>
<input type="number" name="terrestrial_control" id="form_terrestrial" min="0" max="100" value="0">
</div>
</div>
<button type="submit" class="btn-save">ENREGISTRER</button>
<button type="button" class="btn-cancel" onclick="closeModal()">ANNULER</button>
</form>
</div>
</div>
<!-- Edit Sector Modal -->
<div id="sectorModal">
<div class="modal-content">
<h3>Paramètres du Secteur</h3>
<form method="POST">
<input type="hidden" name="action" value="update_sector">
<input type="hidden" name="sector_id" value="<?php echo $sector_id; ?>">
<input type="hidden" name="galaxy_id" value="<?php echo $galaxy_id; ?>">
<div class="form-group">
<label>Nom du Secteur</label>
<input type="text" name="sector_name" value="<?php echo htmlspecialchars($sector_info['name'] ?? "Secteur $sector_id"); ?>">
</div>
<div class="form-group">
<label>Statut Global du Secteur</label>
<select name="sector_status">
<option value="unexplored" <?php echo ($sector_info['status'] ?? '') == 'unexplored' ? 'selected' : ''; ?>>Inexploré</option>
<option value="stable" <?php echo ($sector_info['status'] ?? '') == 'stable' ? 'selected' : ''; ?>>Stable / Pacifique</option>
<option value="hostile" <?php echo ($sector_info['status'] ?? '') == 'hostile' ? 'selected' : ''; ?>>Hostile / Contesté</option>
<option value="war" <?php echo ($sector_info['status'] ?? '') == 'war' ? 'selected' : ''; ?>>Zone de Guerre Mondiale</option>
</select>
</div>
<button type="submit" class="btn-save">METTRE À JOUR LE SECTEUR</button>
<button type="button" class="btn-cancel" onclick="closeSectorModal()">ANNULER</button>
</form>
</div>
</div>
<script>
let settlementIndex = 0;
const settlementTypes = <?php echo json_encode($settlement_types_db); ?>;
function addSettlementRow(data = null) {
const container = document.getElementById('settlementsContainer');
const index = settlementIndex++;
const div = document.createElement('div');
div.className = 'settlement-item';
div.id = 'settlement_row_' + index;
let html = `<button type="button" class="btn-remove-settlement" onclick="document.getElementById('settlement_row_${index}').remove()" title="Supprimer">×</button>`;
html += `<input type="hidden" name="cities[${index}][id]" value="${data ? data.id : 0}">`;
html += `<div class="compact-row">`;
html += `<div class="form-group" style="flex: 2;"><label>Nom</label>`;
html += `<input type="text" name="cities[${index}][name]" value="${data ? data.name : ''}" placeholder="Ex: New Hope" required></div>`;
html += `<div class="form-group" style="flex: 1;"><label>Type</label><select name="cities[${index}][type_id]">`;
settlementTypes.forEach(t => {
const sel = (data && data.settlement_type_id == t.id) ? 'selected' : '';
html += `<option value="${t.id}" ${sel}>${t.name}</option>`;
});
html += `</select></div>`;
html += `</div>`;
div.innerHTML = html;
container.appendChild(div);
}
function editSlot(num, data) {
document.getElementById('modalTitle').innerText = 'Modifier la case n°' + num;
document.getElementById('form_slot_num').value = num;
document.getElementById('settlementsContainer').innerHTML = '';
settlementIndex = 0;
if (data) {
document.getElementById('form_slot_id').value = data.id;
document.getElementById('form_name').value = data.name;
document.getElementById('form_type').value = data.type;
document.getElementById('form_status').value = data.status;
document.getElementById('form_orbital').value = data.orbital_control;
document.getElementById('form_terrestrial').value = data.terrestrial_control;
if (data.cities && data.cities.length > 0) {
data.cities.forEach(c => addSettlementRow(c));
}
} else {
document.getElementById('form_slot_id').value = 0;
document.getElementById('form_name').value = 'Objet ' + num;
document.getElementById('form_type').value = 'planet';
document.getElementById('form_status').value = 'empty';
document.getElementById('form_orbital').value = 0;
document.getElementById('form_terrestrial').value = 0;
}
document.getElementById('editModal').style.display = 'flex';
}
function closeModal() { document.getElementById('editModal').style.display = 'none'; }
function editSector() { document.getElementById('sectorModal').style.display = 'flex'; }
function closeSectorModal() { document.getElementById('sectorModal').style.display = 'none'; }
window.onclick = function(event) {
if (event.target == document.getElementById('editModal')) closeModal();
if (event.target == document.getElementById('sectorModal')) closeSectorModal();
}
</script>
</body>
</html>