38676-vm/index.php
2026-03-09 07:46:42 +00:00

1041 lines
53 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';
require_once 'includes/status_helper.php';
session_start();
$db = db();
$user_role = 'user';
if (isset($_SESSION['user_id'])) {
$stmt = $db->prepare("SELECT u.role, u.display_name, u.username, u.guild_id, l.name as level_raw,
u.selected_title_id, u.selected_badge_id,
t.name as title_name,
b.name as badge_name, b.image_url as badge_image
FROM users u
LEFT JOIN levels l ON u.level_id = l.id
LEFT JOIN titles t ON u.selected_title_id = t.id
LEFT JOIN badges b ON u.selected_badge_id = b.id
WHERE u.id = ?");
$stmt->execute([$_SESSION['user_id']]);
$u_data = $stmt->fetch();
if ($u_data) {
$user_role = $u_data['role'] ?? 'user';
$_SESSION['display_name'] = $u_data['display_name'] ?: $u_data['username'];
$level_num = (int)filter_var($u_data['level_raw'], FILTER_SANITIZE_NUMBER_INT);
$_SESSION['level'] = $level_num;
$_SESSION['guild_id'] = $u_data['guild_id'];
// Save title and badge to session for modal
$_SESSION['selected_title_name'] = $u_data['title_name'];
$_SESSION['selected_badge_name'] = $u_data['badge_name'];
$_SESSION['selected_badge_image'] = $u_data['badge_image'];
$grade_type = ($user_role === 'admin') ? 'admin' : 'utilisateur';
$g_stmt = $db->prepare("SELECT name, image_url FROM grades
WHERE user_type = ?
AND (min_level <= ? OR min_level IS NULL)
AND (max_level >= ? OR max_level IS NULL)
LIMIT 1");
$g_stmt->execute([$grade_type, $level_num, $level_num]);
$grade_data = $g_stmt->fetch();
if ($grade_data) {
$_SESSION['grade_name'] = $grade_data['name'];
$_SESSION['grade_image'] = $grade_data['image_url'];
} else {
$_SESSION['grade_name'] = "Recrue";
$_SESSION['grade_image'] = "assets/images/placeholder_grade.png";
}
}
}
$view = isset($_GET['view']) ? $_GET['view'] : 'sector';
$galaxy_id = isset($_GET['galaxy_id']) ? (int)$_GET['galaxy_id'] : 1;
$sector_id = isset($_GET['sector_id']) ? (int)$_GET['sector_id'] : 1;
// Fetch Dynamic Types, Statuses and Factions
$object_types_db = $db->query("SELECT * FROM celestial_object_types")->fetchAll(PDO::FETCH_ASSOC);
$statuses_db = $db->query("SELECT * FROM celestial_object_statuses")->fetchAll(PDO::FETCH_ASSOC);
$factions_db = $db->query("SELECT * FROM factions")->fetchAll(PDO::FETCH_ASSOC);
$object_types_map = [];
foreach($object_types_db as $ot) {
// Get modifiers for this type
$stmt = $db->prepare("SELECT m.* FROM modifiers m JOIN celestial_object_type_modifiers cotm ON m.id = cotm.modifier_id WHERE cotm.celestial_object_type_id = ?");
$stmt->execute([$ot['id']]);
$ot['modifiers'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$object_types_map[$ot['slug']] = $ot;
}
$statuses_map = []; foreach($statuses_db as $s) { $s['is_blinking'] = (strpos($s['color'], ';blink') !== false); $statuses_map[$s['slug']] = $s; }
$factions_map = []; foreach($factions_db as $f) $factions_map[$f['id']] = $f;
// Grid size: 6x6 = 36 slots per sector
$grid_size = 36;
// Dynamic Resources
$resources = [];
if (isset($_SESSION['user_id'])) {
$stmt = $db->prepare("
SELECT gr.*, COALESCE(ur.amount, 0) as amount
FROM game_resources gr
LEFT JOIN user_resources ur ON gr.id = ur.resource_id AND ur.user_id = ?
WHERE gr.show_in_header = 1
ORDER BY CASE
WHEN gr.name LIKE 'Crédit%' THEN 1
WHEN gr.name LIKE 'Matériau%' THEN 2
WHEN gr.name LIKE 'Energie%' THEN 3
WHEN gr.name LIKE 'Donnée%' THEN 4
ELSE 5
END ASC, gr.name ASC
");
$stmt->execute([$_SESSION['user_id']]);
$header_resources = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($header_resources as $hr) {
$resources[$hr["name"]] = [
"val" => (string)$hr["amount"],
"prod" => "",
"icon" => $hr["icon"] ?: "fa-gem",
"image" => $hr["image_url"]
];
}
} else {
$header_resources = $db->query("SELECT * FROM game_resources WHERE show_in_header = 1 ORDER BY CASE WHEN name LIKE 'Crédit%' THEN 1 WHEN name LIKE 'Matériau%' THEN 2 WHEN name LIKE 'Energie%' THEN 3 WHEN name LIKE 'Donnée%' THEN 4 ELSE 5 END ASC, name ASC")->fetchAll(PDO::FETCH_ASSOC);
foreach($header_resources as $hr) {
$resources[$hr["name"]] = ["val" => "0", "prod" => "", "icon" => $hr["icon"] ?: "fa-gem", "image" => $hr["image_url"]];
}
}
if ($view === 'sector') {
$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'] = [];
$grid[$obj['slot']]['orbital_controls'] = [];
$grid[$obj['slot']]['terrestrial_controls'] = [];
}
if (!empty($planet_ids)) {
$placeholders = implode(',', array_fill(0, count($planet_ids), '?'));
// Fetch Orbital Controls
$stmt = $db->prepare("SELECT * FROM planet_faction_control WHERE planet_id IN ($placeholders)");
$stmt->execute($planet_ids);
$orb_controls_raw = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($orb_controls_raw as $ocr) {
foreach ($grid as &$slot_data) {
if ($slot_data && $slot_data['id'] == $ocr['planet_id']) {
$slot_data['orbital_controls'][$ocr['faction_id']] = $ocr['control_level'];
}
}
}
// Fetch Cities
unset($slot_data);
$stmt = $db->prepare("SELECT c.*, st.name as type_name
FROM cities c
LEFT JOIN settlement_types st ON c.settlement_type_id = st.id
WHERE c.planet_id IN ($placeholders)");
$stmt->execute($planet_ids);
$all_cities = $stmt->fetchAll(PDO::FETCH_ASSOC);
$city_ids = array_column($all_cities, 'id');
$city_controls = [];
if (!empty($city_ids)) {
$c_placeholders = implode(',', array_fill(0, count($city_ids), '?'));
$c_stmt = $db->prepare("SELECT * FROM city_faction_control WHERE city_id IN ($c_placeholders)");
$c_stmt->execute($city_ids);
$controls_raw = $c_stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($controls_raw as $cr) {
$city_controls[$cr['city_id']][$cr['faction_id']] = $cr['control_level'];
}
}
$planet_terrestrial_agg = [];
foreach ($all_cities as $city) {
$pid = $city['planet_id'];
$city['controls'] = $city_controls[$city['id']] ?? [];
foreach ($city['controls'] as $fid => $lvl) {
$planet_terrestrial_agg[$pid][$fid] = ($planet_terrestrial_agg[$pid][$fid] ?? 0) + $lvl;
}
foreach ($grid as &$slot_data) {
if ($slot_data && $slot_data['id'] == $pid) {
$slot_data['cities'][] = $city;
}
}
}
// Calculate average terrestrial control per faction
foreach ($grid as &$slot_data) {
if ($slot_data && !empty($slot_data['cities'])) {
$num_cities = count($slot_data['cities']);
$pid = $slot_data['id'];
if (isset($planet_terrestrial_agg[$pid])) {
foreach ($planet_terrestrial_agg[$pid] as $fid => $total_lvl) {
$slot_data['terrestrial_controls'][$fid] = round($total_lvl / $num_cities);
}
}
}
}
// --- POINT UNIQUE: CALCULATE DYNAMIC STATUS ---
foreach ($grid as &$slot_data) {
if ($slot_data) {
$slot_data['status'] = calculateCelestialStatus($slot_data, $db, $statuses_map);
}
}
}
unset($slot_data);
$stmt = $db->prepare("SELECT name FROM sectors WHERE id = ?");
$stmt->execute([$sector_id]);
$sector_info = $stmt->fetch();
$sector_display_name = $sector_info['name'] ?? "Secteur $sector_id";
$page_title = "$sector_display_name [G$galaxy_id]";
} else {
// Galaxy View: Also need dynamic status
$stmt = $db->prepare("SELECT * FROM planets WHERE galaxy_id = ? ORDER BY sector_id, slot ASC");
$stmt->execute([$galaxy_id]);
$all_planets = $stmt->fetchAll(PDO::FETCH_ASSOC);
$planet_ids = array_column($all_planets, 'id');
$orb_controls = [];
$terr_controls = [];
$city_counts = [];
if (!empty($planet_ids)) {
$placeholders = implode(',', array_fill(0, count($planet_ids), '?'));
// Orbital
$o_stmt = $db->prepare("SELECT * FROM planet_faction_control WHERE planet_id IN ($placeholders)");
$o_stmt->execute($planet_ids);
while($r = $o_stmt->fetch()) $orb_controls[$r['planet_id']][$r['faction_id']] = $r['control_level'];
// Terrestrial (Aggregated per planet)
$t_stmt = $db->prepare("SELECT c.planet_id, cfc.faction_id, SUM(cfc.control_level) as total_lvl
FROM city_faction_control cfc
JOIN cities c ON cfc.city_id = c.id
WHERE c.planet_id IN ($placeholders)
GROUP BY c.planet_id, cfc.faction_id");
$t_stmt->execute($planet_ids);
while($r = $t_stmt->fetch()) {
$terr_controls[$r['planet_id']][$r['faction_id']] = $r['total_lvl'];
}
// City counts to know if it's empty
$c_stmt = $db->prepare("SELECT planet_id, COUNT(*) as cnt FROM cities WHERE planet_id IN ($placeholders) GROUP BY planet_id");
$c_stmt->execute($planet_ids);
while($r = $c_stmt->fetch()) $city_counts[$r['planet_id']] = $r['cnt'];
}
$sector_data = [];
$active_sectors = [];
foreach ($all_planets as $p) {
$p['orbital_controls'] = $orb_controls[$p['id']] ?? [];
$p['cities'] = isset($city_counts[$p['id']]) ? array_fill(0, $city_counts[$p['id']], []) : [];
$p['terrestrial_controls'] = [];
if (!empty($p['cities'])) {
$num_cities = count($p['cities']);
if (isset($terr_controls[$p['id']])) {
foreach ($terr_controls[$p['id']] as $fid => $total_lvl) {
$p['terrestrial_controls'][$fid] = round($total_lvl / $num_cities);
}
}
}
$dynamic_status = calculateCelestialStatus($p, $db, $statuses_map);
$sector_data[$p['sector_id']][$p['slot']] = ['status' => $dynamic_status, 'type' => $p['type']];
if (!in_array($p['sector_id'], $active_sectors)) { $active_sectors[] = (int)$p['sector_id']; }
}
$page_title = "Galaxie $galaxy_id";
}
function getStatusColor($status, $statuses_map) {
$c = $statuses_map[$status]['color'] ?? 'rgba(255,255,255,0.05)'; return str_replace(';blink', '', $c);
}
?>
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Nexus - <?php echo $page_title; ?></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; }
#main-wrapper { display: flex; flex-direction: column; min-height: 100vh; }
/* HEADER STYLES */
#top-bar {
background: #0f172a;
border-bottom: 1px solid #1e293b;
padding: 10px 40px;
display: flex;
flex-direction: column;
gap: 10px;
}
.user-auth-bar {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 20px;
font-size: 11px;
color: #8c92a3;
}
.user-auth-bar a { color: #88c0d0; text-decoration: none; font-weight: bold; }
.user-auth-bar .username { color: #ebcb8b; }
.resource-container {
display: flex;
justify-content: center;
align-items: center;
gap: 30px;
flex-wrap: wrap;
}
.resource-box {
display: flex;
align-items: center;
gap: 12px;
background: rgba(30, 41, 59, 0.4);
padding: 6px 15px;
border-radius: 8px;
border: 1px solid rgba(136, 192, 208, 0.1);
min-width: 140px;
}
.resource-icon {
font-size: 18px;
color: #88c0d0;
width: 24px;
display: flex;
justify-content: center;
}
.resource-icon img {
width: 24px;
height: 24px;
object-fit: contain;
}
.resource-info {
display: flex;
flex-direction: column;
}
.resource-name {
font-size: 9px;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #64748b;
margin-bottom: 2px;
}
.resource-val-prod {
display: flex;
align-items: baseline;
gap: 6px;
}
.resource-value {
font-size: 14px;
font-weight: bold;
color: #f8fafc;
}
.resource-prod {
font-size: 10px;
color: #10b981;
}
#game-container { flex: 1; padding: 30px; display: flex; flex-direction: column; align-items: center; }
.nav-panel { background: rgba(10, 15, 30, 0.95); border: 1px solid #2d3545; padding: 20px; width: 180px; }
.nav-panel h3 { margin: 0 0 15px 0; color: #88c0d0; font-size: 14px; text-transform: uppercase; border-bottom: 1px solid #2d3545; padding-bottom: 10px; }
.nav-panel label { display: block; font-size: 10px; color: #8c92a3; margin-top: 10px; }
.nav-panel input { width: 100%; background: #000; border: 1px solid #3b4252; color: #fff; padding: 5px; margin-top: 3px; font-size: 12px; }
.nav-panel button { width: 100%; margin-top: 15px; background: #88c0d0; border: none; padding: 8px; color: #000; font-weight: bold; cursor: pointer; border-radius: 2px; }
.galaxy-map {
display: grid;
grid-template-columns: repeat(6, 140px);
grid-template-rows: repeat(6, 140px);
gap: 10px;
padding: 15px;
background: rgba(10, 15, 30, 0.5);
border: 1px solid #2d3545;
box-shadow: 0 0 30px rgba(0,0,0,0.5);
}
.slot {
width: 140px;
height: 140px;
background: rgba(46, 52, 64, 0.3);
border: 1px solid #3b4252;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
overflow: hidden;
}
.slot:hover { background: rgba(136, 192, 208, 0.1); border-color: #88c0d0; z-index: 10; }
.slot-id { position: absolute; top: 5px; left: 8px; font-size: 9px; color: #4c566a; font-weight: bold; z-index: 5; }
.slot-icons {
position: absolute;
top: 5px;
right: 5px;
display: flex;
flex-direction: column;
gap: 5px;
align-items: center;
z-index: 6;
}
.faction-icon-sm {
width: 22px;
height: 22px;
filter: drop-shadow(0 0 2px rgba(0,0,0,0.8));
display: flex;
align-items: center;
justify-content: center;
}
.info-icon-sm {
width: 20px;
height: 20px;
font-size: 14px;
color: #ebcb8b;
filter: drop-shadow(0 0 2px rgba(0,0,0,0.8));
display: flex;
align-items: center;
justify-content: center;
}
.object-icon {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90px;
height: 90px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
line-height: 1;
font-size: 90px;
z-index: 2;
}
.object-image { width: 90px; height: 90px; object-fit: contain; margin: 0; }
.slot:hover .object-icon { transform: translate(-50%, -50%) scale(1.1); }
.object-name {
position: absolute;
bottom: 8px;
font-size: 11px;
font-weight: bold;
color: #eceff4;
text-align: center;
width: 95%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
z-index: 3;
text-shadow: 0 0 4px rgba(0,0,0,0.8);
}
.sector-grid {
display: grid;
grid-template-columns: repeat(6, 180px);
grid-template-rows: repeat(6, 180px);
gap: 15px;
}
.sector-card { background: rgba(10, 15, 30, 0.95); border: 1px solid #2d3545; padding: 20px; display: flex; flex-direction: column; align-items: center; justify-content: center; text-decoration: none; color: #fff; transition: all 0.2s; position: relative; width: 180px; height: 180px; box-sizing: border-box; }
.sector-card:hover { border-color: #88c0d0; background: #1a202c; transform: translateY(-3px); }
.sector-card.empty { opacity: 0.6; }
.mini-map { display: grid; grid-template-columns: repeat(6, 12px); gap: 4px; margin-bottom: 15px; background: #000; padding: 6px; border-radius: 2px; }
.mini-dot { width: 12px; height: 12px; border-radius: 1px; }
/* MODAL STYLES */
.modal-overlay {
display: none;
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0, 0, 0, 0.85);
backdrop-filter: blur(5px);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal-container {
background: #0f172a;
border: 1px solid #1e293b;
border-radius: 12px;
width: 600px;
max-height: 90vh;
overflow-y: auto;
position: relative;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}
.modal-header {
padding: 20px;
border-bottom: 1px solid #1e293b;
display: flex;
justify-content: space-between;
align-items: center;
background: rgba(30, 41, 59, 0.5);
}
.modal-header h2 { margin: 0; font-size: 20px; color: #88c0d0; }
.modal-close {
background: none; border: none; color: #8c92a3; font-size: 24px; cursor: pointer;
transition: color 0.2s;
}
.modal-close:hover { color: #fff; }
.modal-body { padding: 25px; }
.planet-hero {
display: flex;
gap: 25px;
margin-bottom: 25px;
align-items: center;
}
.planet-preview-img {
width: 120px;
height: 120px;
object-fit: contain;
filter: drop-shadow(0 0 15px rgba(136, 192, 208, 0.3));
}
.planet-meta { flex: 1; }
.planet-status-badge {
display: inline-block;
padding: 4px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
margin-bottom: 10px;
}
.planet-description {
font-size: 13px;
color: #94a3b8;
line-height: 1.6;
margin-bottom: 15px;
}
.control-section {
margin-bottom: 25px;
padding: 15px;
background: rgba(30, 41, 59, 0.3);
border-radius: 8px;
border: 1px solid rgba(136, 192, 208, 0.1);
}
.control-title {
font-size: 12px;
font-weight: bold;
color: #88c0d0;
text-transform: uppercase;
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.control-title i { font-size: 14px; }
/* Multi-colored Progress Bar */
.multi-control-bar {
height: 14px;
background: #1e293b;
border-radius: 7px;
overflow: hidden;
display: flex;
margin-bottom: 10px;
box-shadow: inset 0 2px 4px rgba(0,0,0,0.3);
}
.control-segment {
height: 100%;
transition: width 0.3s ease;
position: relative;
}
.control-legend {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 10px;
}
.legend-tag {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: #cbd5e1;
}
.legend-color { width: 10px; height: 10px; border-radius: 2px; }
.settlement-card {
background: rgba(15, 23, 42, 0.6);
border: 1px solid #1e293b;
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
}
.settlement-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.settlement-name { font-weight: bold; font-size: 14px; color: #fff; }
.settlement-type { font-size: 10px; color: #8c92a3; text-transform: uppercase; }
.tooltip-box { display: none; position: absolute; top: -10px; left: 105%; width: 240px; background: #1e293b; border: 1px solid #88c0d0; padding: 15px; z-index: 100; pointer-events: none; box-shadow: 10px 10px 20px rgba(0,0,0,0.5); }
.slot:hover .tooltip-box { display: block; }
.tooltip-title { font-size: 14px; color: #88c0d0; font-weight: bold; border-bottom: 1px solid #334155; padding-bottom: 8px; margin-bottom: 8px; }
.tooltip-desc { font-size: 11px; color: #d8dee9; line-height: 1.4; font-style: italic; margin-bottom: 10px; }
.mod-list { display: flex; flex-direction: column; gap: 5px; }
.mod-item { font-size: 10px; padding: 4px 8px; border-radius: 3px; display: flex; align-items: center; gap: 8px; }
.mod-bonus { background: rgba(163, 190, 140, 0.15); color: #a3be8c; border: 1px solid rgba(163, 190, 140, 0.3); }
.mod-malus { background: rgba(191, 97, 106, 0.15); color: #bf616a; border: 1px solid rgba(191, 97, 106, 0.3); }
.mod-item i { font-size: 12px; }
.settlement-title { font-size: 10px; color: #ebcb8b; font-weight: bold; border-top: 1px solid #334155; margin-top: 8px; padding-top: 5px; margin-bottom: 5px; }
.settlement-item-tool { font-size: 9px; color: #fff; margin-bottom: 10px; background: rgba(0,0,0,0.2); padding: 5px; border-radius: 3px; }
.control-bars-mini { margin-top: 5px; display: flex; flex-direction: column; gap: 3px; }
.control-bar-mini { height: 4px; background: #000; border-radius: 2px; overflow: hidden; display: flex; }
.control-fill { height: 100%; }
.control-label-mini { font-size: 7px; color: #8c92a3; display: flex; justify-content: space-between; margin-bottom: 1px; }
.legend { margin-top: 20px; background: rgba(10, 15, 30, 0.95); border: 1px solid #2d3545; padding: 10px 20px; display: flex; gap: 15px; font-size: 10px; flex-wrap: wrap; max-width: 1000px; justify-content: center; }
.legend-item { display: flex; align-items: center; gap: 5px; }
.dot { width: 8px; height: 8px; border-radius: 1px; }
.breadcrumb { margin-bottom: 20px; font-size: 14px; color: #88c0d0; }
.breadcrumb a { color: #fff; text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }
.admin-footer { position: fixed; bottom: 0; left: 0; width: 100%; background: rgba(0,0,0,0.8); padding: 5px 20px; display: flex; justify-content: flex-end; gap: 15px; border-top: 1px solid #2d3545; }
.admin-footer a { color: #fff; text-decoration: none; font-size: 11px; font-weight: bold; padding: 5px 10px; border-radius: 3px; }
.btn-mj { background: #ebcb8b; color: #000 !important; }
.btn-adm { background: #bf616a; }
</style>
</head>
<body>
<div id="main-wrapper">
<header id="top-bar">
<div style="display: flex; align-items: center; gap: 20px;">
<?php if (isset($_SESSION["user_id"])): ?>
<button type="button" onclick="document.getElementById('profileModal').style.display='flex'" style="background:none; border:none; color:#88c0d0; cursor:pointer; font-size:11px; font-weight:bold;">Voir mon profil public</button>
<a href="guilde.php" style="text-decoration:none; color:#88c0d0; font-size:11px; font-weight:bold;"><i class="fa-solid fa-building-shield"></i> <?php echo empty($_SESSION["guild_id"]) ? "Création de guilde" : "Voir ma guilde"; ?></a>
<?php endif; ?>
</div>
<div class="user-auth-bar">
<?php if (isset($_SESSION["user_id"])): ?>
<span>Bienvenue, <span class="username">@<?php echo htmlspecialchars($_SESSION["display_name"] ?? $_SESSION["username"]); ?></span></span>
<a href="project_log.php"><i class="fa-solid fa-clipboard-list"></i> Journal</a> <a href="account.php"><i class="fa-solid fa-user-gear"></i> Mon compte</a>
<a href="auth.php?logout=1" style="color: #bf616a;"><i class="fa-solid fa-right-from-bracket"></i> Déconnexion</a>
<?php else: ?>
<a href="auth.php?page=login"><i class="fa-solid fa-right-to-bracket"></i> Connexion</a>
<a href="project_log.php"><i class="fa-solid fa-clipboard-list"></i> Journal</a> <a href="auth.php?page=register"><i class="fa-solid fa-user-plus"></i> S'inscrire</a>
<?php endif; ?>
</div>
<div class="resource-container">
<?php foreach($resources as $name => $res): ?>
<div class="resource-box">
<div class="resource-icon">
<?php if (!empty($res["image"])): ?>
<img src="<?php echo htmlspecialchars($res["image"]); ?>?v=<?php echo time(); ?>">
<?php else: ?>
<i class="fa-solid <?php echo htmlspecialchars($res["icon"]); ?>"></i>
<?php endif; ?>
</div>
<div class="resource-info">
<div class="resource-name"><?php echo htmlspecialchars($name); ?></div>
<div class="resource-val-prod">
<span class="resource-value"><?php echo htmlspecialchars($res["val"]); ?></span>
<span class="resource-prod"><?php echo htmlspecialchars($res["prod"]); ?></span>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</header>
<main id="game-container">
<div class="breadcrumb">
<a href="?view=galaxy&galaxy_id=<?php echo $galaxy_id; ?>">Galaxie <?php echo $galaxy_id; ?></a>
<?php if($view === 'sector'): ?> &nbsp;>&nbsp; <?php echo htmlspecialchars($sector_display_name); ?> <?php endif; ?>
</div>
<div style="display: flex; gap: 40px; align-items: flex-start; width: 100%; max-width: 1200px; justify-content: center;">
<div class="nav-panel">
<h3>Univers</h3>
<form method="GET"><input type="hidden" name="view" value="<?php echo $view; ?>">
<div><label>Galaxie</label><input type="number" name="galaxy_id" value="<?php echo $galaxy_id; ?>" min="1"></div>
<?php if($view === 'sector'): ?><div><label>Secteur</label><input type="number" name="sector_id" value="<?php echo $sector_id; ?>" min="1"></div><?php endif; ?>
<button type="submit">Localiser</button>
</form>
<?php if($view === 'sector'): ?><button onclick="location.href='?view=galaxy&galaxy_id=<?php echo $galaxy_id; ?>'" style="background: #3b4252; margin-top: 5px;">Vue Galaxie</button><?php endif; ?>
</div>
<?php if($view === 'sector'): ?>
<div class="galaxy-map">
<?php for($i=1; $i<=$grid_size; $i++): ?>
<?php
$obj = $grid[$i] ?? null;
$json_data = $obj ? htmlspecialchars(json_encode($obj)) : 'null';
?>
<div class="slot" onclick="openPlanetModal(<?php echo $json_data; ?>)">
<span class="slot-id"><?php echo $i; ?></span>
<?php if ($obj):
$type_info = $object_types_map[$obj['type']] ?? null;
$fac_info = isset($obj['faction_id']) ? ($factions_map[$obj['faction_id']] ?? null) : null;
?>
<div class="tooltip-box">
<div class="tooltip-title"><?php echo htmlspecialchars($obj['name']); ?></div>
<div style="font-size: 10px; color: #88c0d0; margin-bottom: 5px;"><i class="fa-solid fa-circle-info"></i> <?php echo $statuses_map[$obj['status']]['name'] ?? ucfirst($obj['status']); ?></div>
<?php if ($fac_info && $fac_info['name'] !== 'Aucune'): ?>
<div style="font-size: 10px; color: <?php echo htmlspecialchars($fac_info['color'] ?? '#ebcb8b'); ?>; margin-bottom: 5px;"><i class="fa-solid fa-flag"></i> Faction: <?php echo htmlspecialchars($fac_info['name']); ?></div>
<?php endif; ?>
<div class="tooltip-desc"><?php echo htmlspecialchars($type_info['description'] ?? ''); ?></div>
<!-- Orbital Control Breakdown -->
<?php if (!empty($obj['orbital_controls'])): ?>
<div class="settlement-title" style="color: #88c0d0;"><i class="fa-solid fa-satellite-dish"></i> Contrôle Orbital:</div>
<div class="settlement-item-tool">
<div class="control-bars-mini">
<?php
foreach ($obj['orbital_controls'] as $fid => $lvl):
if ($lvl <= 0) continue;
$fName = $factions_map[$fid]['name'] ?? 'Inconnue';
$fColor = $factions_map[$fid]['color'] ?? '#88c0d0';
?>
<div class="control-label-mini">
<span><?php echo htmlspecialchars($fName); ?></span>
<span><?php echo $lvl; ?>%</span>
</div>
<div class="control-bar-mini">
<div class="control-fill" style="width: <?php echo $lvl; ?>%; background: <?php echo htmlspecialchars($fColor); ?>;"></div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
<?php if (!empty($obj['cities'])): ?>
<div class="settlement-title"><i class="fa-solid fa-city"></i> Établissements:</div>
<?php foreach ($obj['cities'] as $c): ?>
<div class="settlement-item-tool">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px;">
<strong><?php echo htmlspecialchars($c['name']); ?></strong>
<span style="color: #8c92a3; font-size: 7px;"><?php echo htmlspecialchars($c['type_name']); ?></span>
</div>
<?php if (!empty($c['controls'])): ?>
<div class="control-bars-mini">
<?php
foreach ($c['controls'] as $fid => $lvl):
if ($lvl <= 0) continue;
$fName = $factions_map[$fid]['name'] ?? 'Inconnue';
$fColor = $factions_map[$fid]['color'] ?? '#88c0d0';
?>
<div class="control-label-mini">
<span><?php echo htmlspecialchars($fName); ?></span>
<span><?php echo $lvl; ?>%</span>
</div>
<div class="control-bar-mini">
<div class="control-fill" style="width: <?php echo $lvl; ?>%; background: <?php echo htmlspecialchars($fColor); ?>;"></div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
<?php if (!empty($type_info['modifiers'])): ?>
<div class="mod-list">
<?php foreach ($type_info['modifiers'] as $m): ?>
<div class="mod-item <?php echo $m['type'] === 'bonus' ? 'mod-bonus' : 'mod-malus'; ?>">
<i class="fa-solid <?php echo $m['type'] === 'bonus' ? 'fa-circle-up' : 'fa-circle-down'; ?>"></i>
<strong><?php echo htmlspecialchars($m['name']); ?>:</strong> <?php echo htmlspecialchars($m['description']); ?>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
<div class="slot-icons">
<?php if ($fac_info): ?>
<div class="faction-icon-sm">
<?php if (!empty($fac_info['image_url'])): ?>
<img src="<?php echo htmlspecialchars($fac_info['image_url']); ?>?v=<?php echo time(); ?>" style="width: 100%; height: 100%; object-fit: contain;" title="<?php echo htmlspecialchars($fac_info['name']); ?>">
<?php elseif (!empty($fac_info['fa_icon'])): ?>
<i class="fa-solid <?php echo htmlspecialchars($fac_info['fa_icon']); ?>" style="color: <?php echo htmlspecialchars($fac_info['color'] ?? '#fff'); ?>; font-size: 16px;" title="<?php echo htmlspecialchars($fac_info['name']); ?>"></i>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if (!empty($obj['cities'])): ?>
<div class="info-icon-sm" title="Établissements présents">
<i class="fa-solid fa-city"></i>
</div>
<?php endif; ?>
</div>
<div class="object-icon <?php echo ($statuses_map[$obj['status']]['is_blinking'] ?? 0) ? 'blink-effect' : ''; ?>">
<?php
$icon = $type_info['icon'] ?? 'fa-earth-europe';
$color = getStatusColor($obj['status'], $statuses_map);
$imageUrl = $type_info['image_url'] ?? null;
?>
<?php if ($imageUrl): ?>
<img src="<?php echo htmlspecialchars($imageUrl); ?>?v=<?php echo time(); ?>" class="object-image">
<?php else: ?>
<i class="fa-solid <?php echo $icon; ?>" style="color: <?php echo $color; ?>;"></i>
<?php endif; ?>
</div>
<span class="object-name"><?php echo htmlspecialchars($obj['name']); ?></span>
<?php else: ?>
<div style="opacity: 0.05;"><i class="fa-solid fa-circle fa-sm"></i></div>
<?php endif; ?>
</div>
<?php endfor; ?>
</div>
<?php else: ?>
<div class="sector-grid">
<?php for($s=1; $s<=$grid_size; $s++): $isActive = in_array($s, $active_sectors); ?>
<a href="?view=sector&galaxy_id=<?php echo $galaxy_id; ?>&sector_id=<?php echo $s; ?>" class="sector-card <?php echo $isActive ? '' : 'empty'; ?>">
<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'], $statuses_map); }
?>
<div class="mini-dot <?php echo (isset($sector_data[$s][$p]) && ($statuses_map[$sector_data[$s][$p]['status']]['is_blinking'] ?? 0)) ? 'blink-effect' : ''; ?>" style="background-color: <?php echo $dotColor; ?>;"></div>
<?php endfor; ?>
</div>
<div style="font-size: 10px; color: #88c0d0;">SECTEUR</div>
<div style="font-size: 20px; font-weight: bold;"><?php echo $s; ?></div>
<?php if($isActive): ?><div style="font-size: 8px; color: #a3be8c; margin-top: 5px;"><i class="fa-solid fa-check"></i> Actif</div>
<?php else: ?><div style="font-size: 8px; color: #4c566a; margin-top: 5px;">Inexploré</div><?php endif; ?>
</a>
<?php endfor; ?>
</div>
<?php endif; ?>
</div>
<div class="legend">
<?php foreach($statuses_db as $s): ?>
<div class="legend-item"><span class="dot <?php echo $s['is_blinking'] ? 'blink-effect' : ''; ?>" style="background: <?php echo str_replace(';blink', '', str_replace(' ;blink', '', $s['color'])); ?>;"></span> <?php echo $s['name']; ?></div>
<?php endforeach; ?>
</div>
</main>
</div>
<!-- MODAL OVERLAY -->
<div id="planetModal" class="modal-overlay" onclick="if(event.target === this) closePlanetModal()">
<div class="modal-container">
<div class="modal-header">
<div style="display: flex; flex-direction: column; align-items: flex-start;"><h2 id="m-planet-name">Planet Name</h2><div id="m-planet-type" style="font-style: italic; font-size: 13px; color: #88c0d0; opacity: 0.8; margin-top: 2px;"></div></div>
<button class="modal-close" onclick="closePlanetModal()">&times;</button>
</div>
<div class="modal-body">
<div class="planet-hero">
<img id="m-planet-img" src="" class="planet-preview-img">
<div class="planet-meta">
<div id="m-planet-status" class="planet-status-badge">Status</div>
<div id="m-planet-faction" style="font-size: 13px; font-weight: bold; margin-bottom: 8px;">Faction: None</div>
<div id="m-planet-mods" class="mod-list"></div>
</div>
</div>
<div id="m-orbital-section" class="control-section">
<div class="control-title"><i class="fa-solid fa-satellite-dish"></i> Contrôle Orbital</div>
<div id="m-orbital-bar" class="multi-control-bar"></div>
<div id="m-orbital-legend" class="control-legend"></div>
</div>
<div id="m-terrestrial-section" class="control-section">
<div class="control-title"><i class="fa-solid fa-person-military-pointing"></i> Contrôle Terrestre</div>
<div id="m-terrestrial-bar" class="multi-control-bar"></div>
<div id="m-terrestrial-legend" class="control-legend"></div>
</div>
<div id="m-cities-section">
<div class="control-title"><i class="fa-solid fa-city"></i> Lieux et points dintérêts</div>
<div id="m-cities-container"></div>
</div>
</div>
</div>
</div>
<?php if ($user_role === 'admin' || $user_role === 'gm'): ?>
<div class="admin-footer">
<a href="gm_console.php" class="btn-mj"><i class="fa-solid fa-headset"></i> CONSOLE MG</a>
<?php if ($user_role === 'admin'): ?>
<a href="admin.php" class="btn-adm"><i class="fa-solid fa-shield-halved"></i> CONSOLE ADMIN</a>
<?php endif; ?>
</div>
<?php endif; ?>
<script>
const factionsMap = <?php echo json_encode($factions_map); ?>;
const typesMap = <?php echo json_encode($object_types_map); ?>;
const statusesMap = <?php echo json_encode($statuses_map); ?>;
function openPlanetModal(data) {
if (!data) return;
const typeInfo = typesMap[data.type] || {};
const statusInfo = statusesMap[data.status] || {};
const factionInfo = factionsMap[data.faction_id] || { name: 'Aucune', color: '#8c92a3' };
document.getElementById('m-planet-name').innerText = data.name;
document.getElementById('m-planet-type').innerText = typeInfo.name || data.type;
document.getElementById('m-planet-img').src = typeInfo.image_url || '';
document.getElementById('m-planet-status').innerText = statusInfo.name || data.status;
const statusEl = document.getElementById('m-planet-status'); statusEl.style.background = (statusInfo.color || 'rgba(255,255,255,0.1)').replace(' ;blink', '').replace(' ;blink', '').replace(';blink', ''); statusEl.classList.toggle('blink-effect', !!statusInfo.is_blinking);
document.getElementById('m-planet-faction').innerText = 'Faction dominante: ' + factionInfo.name;
document.getElementById('m-planet-faction').style.color = factionInfo.color || '#fff';
// Display modifiers instead of description
const modContainer = document.getElementById('m-planet-mods');
modContainer.innerHTML = '';
if (typeInfo.modifiers && typeInfo.modifiers.length > 0) {
typeInfo.modifiers.forEach(m => {
const modDiv = document.createElement('div');
modDiv.className = 'mod-item ' + (m.type === 'bonus' ? 'mod-bonus' : 'mod-malus');
modDiv.innerHTML = `
<i class="fa-solid ${m.type === 'bonus' ? 'fa-circle-up' : 'fa-circle-down'}"></i>
<strong>${m.name}:</strong> ${m.description}
`;
modContainer.appendChild(modDiv);
});
} else {
modContainer.innerHTML = '<div style="font-size: 11px; color: #64748b; font-style: italic;">Aucun modificateur particulier.</div>';
}
// Orbital Control
const orbitalBar = document.getElementById('m-orbital-bar');
const orbitalLegend = document.getElementById('m-orbital-legend');
orbitalBar.innerHTML = '';
orbitalLegend.innerHTML = '';
if (typeInfo.orbital_control_enabled == 1 && data.orbital_controls && Object.keys(data.orbital_controls).length > 0) {
document.getElementById('m-orbital-section').style.display = 'block';
renderMultiBar(data.orbital_controls, orbitalBar, orbitalLegend);
} else {
document.getElementById('m-orbital-section').style.display = 'none';
}
// Terrestrial Control (Summary)
const terrestrialBar = document.getElementById('m-terrestrial-bar');
const terrestrialLegend = document.getElementById('m-terrestrial-legend');
terrestrialBar.innerHTML = '';
terrestrialLegend.innerHTML = '';
if (typeInfo.terrestrial_control_enabled == 1 && data.terrestrial_controls && Object.keys(data.terrestrial_controls).length > 0) {
document.getElementById('m-terrestrial-section').style.display = 'block';
renderMultiBar(data.terrestrial_controls, terrestrialBar, terrestrialLegend);
} else {
document.getElementById('m-terrestrial-section').style.display = 'none';
}
// Cities
const citiesContainer = document.getElementById('m-cities-container');
citiesContainer.innerHTML = '';
if (typeInfo.terrestrial_control_enabled == 1 && data.cities && data.cities.length > 0) {
document.getElementById('m-cities-section').style.display = 'block';
data.cities.forEach(city => {
const card = document.createElement('div');
card.className = 'settlement-card';
const header = document.createElement('div');
header.className = 'settlement-header';
header.innerHTML = `<span class="settlement-name">${city.name}</span><span class="settlement-type">${city.type_name}</span>`;
card.appendChild(header);
if (city.controls && Object.keys(city.controls).length > 0) {
const bar = document.createElement('div');
bar.className = 'multi-control-bar';
const legend = document.createElement('div');
legend.className = 'control-legend';
renderMultiBar(city.controls, bar, legend);
card.appendChild(bar);
card.appendChild(legend);
}
citiesContainer.appendChild(card);
});
} else {
document.getElementById('m-cities-section').style.display = 'none';
}
document.getElementById('planetModal').style.display = 'flex';
}
function renderMultiBar(controls, barElement, legendElement) {
Object.entries(controls).forEach(([fid, lvl]) => {
const level = parseInt(lvl);
const fac = factionsMap[fid] || { name: 'Inconnue', color: '#88c0d0' };
if (level <= 0) return;
// Segment
const segment = document.createElement('div');
segment.className = 'control-segment';
segment.style.width = level + '%';
segment.style.backgroundColor = fac.color || '#88c0d0';
segment.title = `${fac.name}: ${level}%`;
barElement.appendChild(segment);
// Legend
const tag = document.createElement('div');
tag.className = 'legend-tag';
tag.innerHTML = `<span class="legend-color" style="background:${fac.color}"></span> ${fac.name}: ${level}%`;
legendElement.appendChild(tag);
});
}
function closePlanetModal() {
document.getElementById('planetModal').style.display = 'none';
}
</script>
<!-- PROFILE MODAL -->
<div id="profileModal" class="modal-overlay" onclick="if(event.target === this) this.style.display='none'">
<div class="modal-container">
<div class="modal-header">
<h2>Profil Public</h2>
<button class="modal-close" onclick="document.getElementById('profileModal').style.display='none'">&times;</button>
</div>
<div class="modal-body" style="text-align: center; padding-top: 40px; padding-bottom: 40px;">
<div style="display: flex; align-items: center; justify-content: center; gap: 15px; margin-bottom: 10px;">
<img src="<?php echo htmlspecialchars($_SESSION["grade_image"] ?? "assets/images/placeholder_grade.png"); ?>" style="width: 60px; height: 60px; object-fit: contain;">
<span style="font-size: 28px; font-weight: bold; color: #fff;">
<?php echo htmlspecialchars($_SESSION["grade_name"] ?? "Recrue"); ?>
<?php echo htmlspecialchars($_SESSION["display_name"] ?? $_SESSION["username"]); ?>
</span>
</div>
<?php if (!empty($_SESSION['selected_title_name'])): ?>
<div style="font-size: 14px; font-weight: bold; color: #ebcb8b; text-transform: uppercase; letter-spacing: 0.1em; margin-bottom: 5px;">
« <?php echo htmlspecialchars($_SESSION['selected_title_name']); ?> »
</div>
<?php endif; ?>
<div style="font-size: 16px; color: #88c0d0; opacity: 0.8; margin-bottom: 20px;">
Niveau <?php echo htmlspecialchars($_SESSION["level"] ?? "1"); ?>
</div>
<?php if (!empty($_SESSION['selected_badge_image'])): ?>
<div style="margin-top: 20px; padding: 15px; background: rgba(30, 41, 59, 0.3); border-radius: 12px; display: inline-block;">
<img src="<?php echo htmlspecialchars($_SESSION['selected_badge_image']); ?>?v=<?php echo time(); ?>" style="width: 80px; height: 80px; object-fit: contain; filter: drop-shadow(0 0 10px rgba(136, 192, 208, 0.2));" title="<?php echo htmlspecialchars($_SESSION['selected_badge_name'] ?? ''); ?>">
<div style="font-size: 10px; color: #8c92a3; margin-top: 8px; text-transform: uppercase;">Badge Équipé</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</body>
</html>