diff --git a/admin.php b/admin.php index 03f9c45..e7ea284 100644 --- a/admin.php +++ b/admin.php @@ -41,6 +41,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $slug = $_POST['slug']; $icon = $_POST['icon']; $description = $_POST['description']; + $modifier_ids = isset($_POST['modifiers']) ? $_POST['modifiers'] : []; if ($id > 0) { $stmt = $db->prepare("UPDATE celestial_object_types SET name = ?, slug = ?, icon = ?, description = ? WHERE id = ?"); @@ -48,7 +49,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' } else { $stmt = $db->prepare("INSERT INTO celestial_object_types (name, slug, icon, description) VALUES (?, ?, ?, ?)"); $stmt->execute([$name, $slug, $icon, $description]); + $id = $db->lastInsertId(); } + + // Sync modifiers + $db->prepare("DELETE FROM celestial_object_type_modifiers WHERE celestial_object_type_id = ?")->execute([$id]); + if (!empty($modifier_ids)) { + $ins = $db->prepare("INSERT INTO celestial_object_type_modifiers (celestial_object_type_id, modifier_id) VALUES (?, ?)"); + foreach ($modifier_ids as $mid) { + $ins->execute([$id, (int)$mid]); + } + } + header("Location: admin.php?tab=objects&success=1"); exit; } @@ -87,7 +99,7 @@ if (isset($_GET['delete_status'])) { } // Handle Settlement Type CRUD -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upsert_settlement') { +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upsert_settlement_type') { $id = (int)$_POST['id']; $name = $_POST['name']; $slug = $_POST['slug']; @@ -100,14 +112,39 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $stmt = $db->prepare("INSERT INTO settlement_types (name, slug, description) VALUES (?, ?, ?)"); $stmt->execute([$name, $slug, $description]); } - header("Location: admin.php?tab=settlements&success=1"); + header("Location: admin.php?tab=settlement_types&success=1"); exit; } -if (isset($_GET['delete_settlement'])) { - $id = (int)$_GET['delete_settlement']; +if (isset($_GET['delete_settlement_type'])) { + $id = (int)$_GET['delete_settlement_type']; $db->prepare("DELETE FROM settlement_types WHERE id = ?")->execute([$id]); - header("Location: admin.php?tab=settlements&success=1"); + header("Location: admin.php?tab=settlement_types&success=1"); + exit; +} + +// Handle Modifiers CRUD +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'upsert_modifier') { + $id = (int)$_POST['id']; + $name = $_POST['name']; + $type = $_POST['type']; + $description = $_POST['description']; + + if ($id > 0) { + $stmt = $db->prepare("UPDATE modifiers SET name = ?, type = ?, description = ? WHERE id = ?"); + $stmt->execute([$name, $type, $description, $id]); + } else { + $stmt = $db->prepare("INSERT INTO modifiers (name, type, description) VALUES (?, ?, ?)"); + $stmt->execute([$name, $type, $description]); + } + header("Location: admin.php?tab=modifiers&success=1"); + exit; +} + +if (isset($_GET['delete_modifier'])) { + $id = (int)$_GET['delete_modifier']; + $db->prepare("DELETE FROM modifiers WHERE id = ?")->execute([$id]); + header("Location: admin.php?tab=modifiers&success=1"); exit; } @@ -115,16 +152,26 @@ if (isset($_GET['delete_settlement'])) { $users_list = []; $objects_list = []; $statuses_list = []; -$settlements_list = []; +$settlement_types_list = []; +$modifiers_list = []; if ($tab === 'users') { $users_list = $db->query("SELECT id, username, email, role FROM users ORDER BY username ASC")->fetchAll(); } elseif ($tab === 'objects') { $objects_list = $db->query("SELECT * FROM celestial_object_types ORDER BY name ASC")->fetchAll(); + // For each object, get its modifiers + foreach ($objects_list as &$obj) { + $stmt = $db->prepare("SELECT modifier_id FROM celestial_object_type_modifiers WHERE celestial_object_type_id = ?"); + $stmt->execute([$obj['id']]); + $obj['modifier_ids'] = $stmt->fetchAll(PDO::FETCH_COLUMN); + } + $modifiers_list = $db->query("SELECT * FROM modifiers ORDER BY type, name ASC")->fetchAll(); } elseif ($tab === 'statuses') { $statuses_list = $db->query("SELECT * FROM celestial_object_statuses ORDER BY name ASC")->fetchAll(); -} elseif ($tab === 'settlements') { - $settlements_list = $db->query("SELECT * FROM settlement_types ORDER BY name ASC")->fetchAll(); +} elseif ($tab === 'settlement_types') { + $settlement_types_list = $db->query("SELECT * FROM settlement_types ORDER BY name ASC")->fetchAll(); +} elseif ($tab === 'modifiers') { + $modifiers_list = $db->query("SELECT * FROM modifiers ORDER BY type, name ASC")->fetchAll(); } ?> @@ -142,7 +189,7 @@ if ($tab === 'users') { .nav-links a:hover { color: #fff; } .container { padding: 40px; max-width: 1200px; margin: 0 auto; } - .tabs { display: flex; gap: 5px; margin-bottom: 20px; border-bottom: 2px solid #2d3545; } + .tabs { display: flex; gap: 5px; margin-bottom: 20px; border-bottom: 2px solid #2d3545; flex-wrap: wrap; } .tab-link { padding: 10px 20px; text-decoration: none; color: #8c92a3; background: #0a0f1d; border: 1px solid #2d3545; border-bottom: none; font-weight: bold; font-size: 14px; } .tab-link.active { background: #1a202c; color: #88c0d0; border-bottom: 2px solid #88c0d0; } @@ -162,6 +209,10 @@ if ($tab === 'users') { .btn-ok { background: #88c0d0; color: #000; } .success-msg { background: #a3be8c; color: #000; padding: 10px; margin-bottom: 20px; border-radius: 4px; font-weight: bold; } + + .modifier-tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 10px; font-weight: bold; margin-right: 5px; margin-bottom: 5px; } + .tag-bonus { background: #a3be8c; color: #000; } + .tag-malus { background: #bf616a; color: #fff; } @@ -186,8 +237,9 @@ if ($tab === 'users') {
Utilisateurs Objets Célestes + Bonus & Malus Statuts / États - Villes / Avant-postes + Types d'Établissements
@@ -244,7 +296,24 @@ if ($tab === 'users') { -
+ +
+
+ +
+ + + +
+
+
+ +
@@ -254,13 +323,24 @@ if ($tab === 'users') {
- + +
IcôneNomSlugActions
IcôneNomSlugBonus/MalusActions
+ prepare("SELECT m.name, m.type FROM modifiers m JOIN celestial_object_type_modifiers cotm ON m.id = cotm.modifier_id WHERE cotm.celestial_object_type_id = ?"); + $stmt->execute([$o['id']]); + $m_list = $stmt->fetchAll(); + foreach ($m_list as $ml): ?> + + + + + Suppr @@ -270,6 +350,56 @@ if ($tab === 'users') {
+ +

Gestion des Bonus & Malus

+
+

Ajouter / Modifier un Modificateur

+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ + +
+
+ + + + + + + + + + + + + +
TypeNomDescriptionActions
+ + + + + + Suppr +
+

Statuts / États

@@ -317,43 +447,43 @@ if ($tab === 'users') { - -

Villes / Avant-postes

+ +

Types d'Établissements

Ajouter / Modifier un Type d'Établissement

-
- - + + +
- +
- +
- +
- +
- + @@ -369,9 +499,23 @@ if ($tab === 'users') { document.getElementById('obj_slug').value = data.slug; document.getElementById('obj_icon').value = data.icon; document.getElementById('obj_desc').value = data.description; + + // Reset checkboxes + document.querySelectorAll('.modifier-checkbox').forEach(cb => cb.checked = false); + // Check assigned modifiers + if (data.modifier_ids) { + data.modifier_ids.forEach(mid => { + const cb = document.querySelector(`.modifier-checkbox[value="${mid}"]`); + if (cb) cb.checked = true; + }); + } window.scrollTo(0,0); } - function resetObjectForm() { document.getElementById('objectForm').reset(); document.getElementById('obj_id').value = 0; } + function resetObjectForm() { + document.getElementById('objectForm').reset(); + document.getElementById('obj_id').value = 0; + document.querySelectorAll('.modifier-checkbox').forEach(cb => cb.checked = false); + } function editStatus(data) { document.getElementById('st_id').value = data.id; @@ -383,14 +527,23 @@ if ($tab === 'users') { } function resetStatusForm() { document.getElementById('statusForm').reset(); document.getElementById('st_id').value = 0; } - function editSettlement(data) { - document.getElementById('set_id').value = data.id; - document.getElementById('set_name').value = data.name; - document.getElementById('set_slug').value = data.slug; - document.getElementById('set_desc').value = data.description; + function editSettlementType(data) { + document.getElementById('set_t_id').value = data.id; + document.getElementById('set_t_name').value = data.name; + document.getElementById('set_t_slug').value = data.slug; + document.getElementById('set_t_desc').value = data.description; window.scrollTo(0,0); } - function resetSettlementForm() { document.getElementById('settlementForm').reset(); document.getElementById('set_id').value = 0; } + function resetSettlementTypeForm() { document.getElementById('settlementTypeForm').reset(); document.getElementById('set_t_id').value = 0; } + + function editModifier(data) { + document.getElementById('mod_id').value = data.id; + document.getElementById('mod_name').value = data.name; + document.getElementById('mod_type').value = data.type; + document.getElementById('mod_desc').value = data.description; + window.scrollTo(0,0); + } + function resetModifierForm() { document.getElementById('modifierForm').reset(); document.getElementById('mod_id').value = 0; } - \ No newline at end of file + diff --git a/db/add_modifiers.php b/db/add_modifiers.php new file mode 100644 index 0000000..fa33698 --- /dev/null +++ b/db/add_modifiers.php @@ -0,0 +1,41 @@ +exec("CREATE TABLE IF NOT EXISTS modifiers ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + type ENUM('bonus', 'malus') NOT NULL, + description TEXT, + icon VARCHAR(50) DEFAULT 'info-circle' +)"); + +// Create junction table for celestial_object_types and modifiers +$pdo->exec("CREATE TABLE IF NOT EXISTS celestial_object_type_modifiers ( + celestial_object_type_id INT NOT NULL, + modifier_id INT NOT NULL, + PRIMARY KEY (celestial_object_type_id, modifier_id), + FOREIGN KEY (celestial_object_type_id) REFERENCES celestial_object_types(id) ON DELETE CASCADE, + FOREIGN KEY (modifier_id) REFERENCES modifiers(id) ON DELETE CASCADE +)"); + +// Insert sample modifiers +$modifiers = [ + ['Chaleur', 'malus', 'Progression des troupes divisée par deux due à la chaleur extrême.'], + ['Chaleur Extrême', 'malus', 'Effets de chaleur doublés, risque d\'incendie élevé.'], + ['Gravité Faible', 'bonus', 'Vitesse de construction et mouvement des troupes augmentés.'], + ['Atmosphère Toxique', 'malus', 'Besoin de respirateurs permanents, réduction de l\'espérance de vie.'], + ['Ressources Abondantes', 'bonus', 'Production de ressources doublée.'], + ['Froid Polaire', 'malus', 'Consommation d\'énergie accrue pour le chauffage.'], + ['Champ de Ruines', 'bonus', 'Possibilité de récupérer des débris technologiques anciens.'] +]; + +$stmt = $pdo->prepare("INSERT IGNORE INTO modifiers (name, type, description) VALUES (?, ?, ?)"); +foreach ($modifiers as $m) { + $stmt->execute($m); +} + +echo "Migration for modifiers completed successfully.\n"; + diff --git a/db/migrate_dynamic_types.php b/db/migrate_dynamic_types.php index e0a6204..4c76de6 100644 --- a/db/migrate_dynamic_types.php +++ b/db/migrate_dynamic_types.php @@ -1,88 +1,34 @@ exec("CREATE TABLE IF NOT EXISTS celestial_object_types ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) NOT NULL, - slug VARCHAR(50) NOT NULL UNIQUE, - icon VARCHAR(50) NOT NULL, - description TEXT - )"); - - // 2. Create celestial_object_statuses table - $db->exec("CREATE TABLE IF NOT EXISTS celestial_object_statuses ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) NOT NULL, - slug VARCHAR(50) NOT NULL UNIQUE, - color VARCHAR(20) NOT NULL, - description TEXT - )"); - - // 3. Create settlement_types table - $db->exec("CREATE TABLE IF NOT EXISTS settlement_types ( - id INT AUTO_INCREMENT PRIMARY KEY, - name VARCHAR(100) NOT NULL, - slug VARCHAR(50) NOT NULL UNIQUE, - description TEXT - )"); - - // 4. Seed initial data - $object_types = [ - ['Planète', 'planet', 'fa-earth-europe', 'Une planète habitable ou non.'], - ['Champ d\'astéroïdes', 'asteroid', 'fa-mountain', 'Riche en minerais.'], - ['Trou noir', 'black_hole', 'fa-circle-dot', 'Phénomène gravitationnel extrême.'], - ['Pulsar', 'pulsar', 'fa-bolt', 'Étoile à neutrons hautement magnétisée.'], - ['Étoile', 'star', 'fa-sun', 'Étoile centrale d\'un système.'], - ['Comète', 'comet', 'fa-comet', 'Petit corps céleste de glace et de poussière.'], - ['Vaisseau abandonné', 'wreckage', 'fa-ship', 'Épave dérivant dans l\'espace.'], - ['Champ de bataille', 'battlefield', 'fa-burst', 'Vestiges d\'un affrontement spatial.'], - ['Vortex', 'vortex', 'fa-whirlpool', 'Anomalie spatio-temporelle.'], - ]; - - $stmt = $db->prepare("INSERT IGNORE INTO celestial_object_types (name, slug, icon, description) VALUES (?, ?, ?, ?)"); - foreach ($object_types as $ot) { - $stmt->execute($ot); + $db = db(); + + // Check current column types for 'type' and 'status' in 'planets' table + $stmt = $db->query("DESCRIBE planets"); + $columns = $stmt->fetchAll(PDO::FETCH_ASSOC); + + echo "Current schema for 'planets' table:\n"; + foreach ($columns as $column) { + if ($column['Field'] === 'type' || $column['Field'] === 'status') { + echo "- {$column['Field']}: {$column['Type']}\n"; + } } - $statuses = [ - ['Inhabité / Vide', 'empty', 'rgba(255,255,255,0.05)', 'Aucune présence détectée.'], - ['Hostile', 'hostile', '#ef4444', 'Zone contrôlée par des ennemis.'], - ['Stable / Allié', 'stable', '#a3be8c', 'Zone sûre et amicale.'], - ['Contrôlé', 'controlled', '#5e81ac', 'Zone sous contrôle total.'], - ['Contesté', 'contested', '#ebcb8b', 'Zone de conflit actif.'], - ['Ressources', 'resource', '#f59e0b', 'Zone riche en ressources exploitables.'], - ['PNA', 'pna', '#d08770', 'Pacte de Non-Agression en vigueur.'], - ['Vortex', 'vortex', '#8b5cf6', 'Zone instable.'], - ['Inhabité (Neutre)', 'inhabited', '#eceff4', 'Présence neutre ou civile.'], - ]; + echo "\nConverting 'type' and 'status' columns to VARCHAR(50) to allow dynamic values...\n"; - $stmt = $db->prepare("INSERT IGNORE INTO celestial_object_statuses (name, slug, color, description) VALUES (?, ?, ?, ?)"); - foreach ($statuses as $s) { - $stmt->execute($s); - } + // Convert 'type' to VARCHAR(50) + $db->exec("ALTER TABLE planets MODIFY COLUMN type VARCHAR(50) NOT NULL DEFAULT 'planet'"); + echo "Column 'type' converted successfully.\n"; - $settlement_types = [ - ['Avant-poste', 'avant-poste', 'Petite base avancée.'], - ['Petite ville', 'petite', 'Établissement mineur.'], - ['Moyenne ville', 'moyenne', 'Établissement standard.'], - ['Grande ville', 'grande', 'Métropole importante.'], - ['Mégacité', 'mégacité', 'Centre urbain colossal.'], - ]; + // Convert 'status' to VARCHAR(50) + $db->exec("ALTER TABLE planets MODIFY COLUMN status VARCHAR(50) NOT NULL DEFAULT 'stable'"); + echo "Column 'status' converted successfully.\n"; - $stmt = $db->prepare("INSERT IGNORE INTO settlement_types (name, slug, description) VALUES (?, ?, ?)"); - foreach ($settlement_types as $st) { - $stmt->execute($st); - } + echo "\nMigration completed successfully. New types and statuses will now be accepted.\n"; - // 5. Update planets and cities tables to use IDs (optional but better for integrity) - // For now, let's just make sure the tables exist and can be managed. - // We will update the code to use these tables for the select options. - - echo "Migration completed successfully."; } catch (PDOException $e) { - echo "Error: " . $e->getMessage(); + echo "Error during migration: " . $e->getMessage() . "\n"; + exit(1); } - +?> \ No newline at end of file diff --git a/db/migrate_settlements_v2.php b/db/migrate_settlements_v2.php new file mode 100644 index 0000000..e76108e --- /dev/null +++ b/db/migrate_settlements_v2.php @@ -0,0 +1,55 @@ +exec("CREATE TABLE IF NOT EXISTS settlement_sizes ( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(100) NOT NULL, + slug VARCHAR(50) NOT NULL, + description TEXT + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"); + + // 2. Insert default sizes if empty + $count = $db->query("SELECT COUNT(*) FROM settlement_sizes")->fetchColumn(); + if ($count == 0) { + $db->exec("INSERT INTO settlement_sizes (name, slug, description) VALUES + ('Minuscule', 'minuscule', 'Très petit établissement.'), + ('Petit', 'petit', 'Établissement mineur.'), + ('Moyen', 'moyen', 'Établissement standard.'), + ('Grand', 'grand', 'Métropole importante.'), + ('Gigantesque', 'gigantesque', 'Centre urbain colossal.')"); + } + + // 3. Ensure settlement_types has better data (just types) + // We'll keep existing but maybe add more + $count = $db->query("SELECT COUNT(*) FROM settlement_types")->fetchColumn(); + if ($count <= 5) { // If it only has the old mixed ones + $db->exec("INSERT INTO settlement_types (name, slug, description) VALUES + ('Base Militaire', 'base_militaire', 'Installation de défense.'), + ('Station de Recherche', 'station_recherche', 'Laboratoire scientifique.')"); + } + + // 4. Update cities table + // First, add new columns + $db->exec("ALTER TABLE cities ADD COLUMN settlement_type_id INT NULL AFTER planet_id;"); + $db->exec("ALTER TABLE cities ADD COLUMN settlement_size_id INT NULL AFTER settlement_type_id;"); + + // Try to migrate old data if any + // Map old enum strings to IDs (rough estimation) + $db->exec("UPDATE cities SET settlement_type_id = 1 WHERE type = 'avant-poste'"); + $db->exec("UPDATE cities SET settlement_type_id = 2 WHERE type != 'avant-poste'"); + $db->exec("UPDATE cities SET settlement_size_id = 2 WHERE type = 'petite'"); + $db->exec("UPDATE cities SET settlement_size_id = 3 WHERE type = 'moyenne'"); + $db->exec("UPDATE cities SET settlement_size_id = 4 WHERE type = 'grande'"); + $db->exec("UPDATE cities SET settlement_size_id = 5 WHERE type = 'mégacité'"); + + // Drop old type column + $db->exec("ALTER TABLE cities DROP COLUMN type;"); + + echo "Migration successful!\n"; +} catch (Exception $e) { + echo "Error during migration: " . $e->getMessage() . "\n"; +} + diff --git a/db/remove_settlement_sizes.php b/db/remove_settlement_sizes.php new file mode 100644 index 0000000..0dea6de --- /dev/null +++ b/db/remove_settlement_sizes.php @@ -0,0 +1,19 @@ +exec("ALTER TABLE cities DROP COLUMN settlement_size_id;"); + echo "Removed settlement_size_id from cities table.\n"; + + // 2. Drop settlement_sizes table + $db->exec("DROP TABLE IF EXISTS settlement_sizes;"); + echo "Dropped settlement_sizes table.\n"; + + echo "Migration completed successfully.\n"; +} catch (PDOException $e) { + echo "Error: " . $e->getMessage() . "\n"; +} + diff --git a/gm_console.php b/gm_console.php index 5f87b06..001c298 100644 --- a/gm_console.php +++ b/gm_console.php @@ -20,10 +20,10 @@ if (!$current_user || ($current_user['role'] !== 'admin' && $current_user['role' $is_admin = ($current_user['role'] === 'admin'); -// Fetch Dynamic Types and Statuses -$object_types_db = $db->query("SELECT * FROM celestial_object_types ORDER BY id ASC")->fetchAll(PDO::FETCH_ASSOC); -$statuses_db = $db->query("SELECT * FROM celestial_object_statuses ORDER BY id ASC")->fetchAll(PDO::FETCH_ASSOC); -$settlement_types_db = $db->query("SELECT * FROM settlement_types ORDER BY id ASC")->fetchAll(PDO::FETCH_ASSOC); +// 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; @@ -42,6 +42,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' 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 { @@ -55,18 +56,36 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' $planet_id = $db->lastInsertId(); } - // Handle Settlement (City) - if (!empty($_POST['city_name'])) { - $c_name = $_POST['city_name']; - $c_type = $_POST['city_type']; - // Simple check if city exists for this planet - $stmt = $db->prepare("SELECT id FROM cities WHERE planet_id = ?"); - $stmt->execute([$planet_id]); - $city = $stmt->fetch(); - if ($city) { - $db->prepare("UPDATE cities SET name = ?, type = ? WHERE id = ?")->execute([$c_name, $c_type, $city['id']]); + // 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 { - $db->prepare("INSERT INTO cities (planet_id, name, type) VALUES (?, ?, ?)")->execute([$planet_id, $c_name, $c_type]); + $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); } } } @@ -86,7 +105,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST[' 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([$sector_id, $s_name, $s_status, $galaxy_id]); + $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§or_id=$sector_id&success=1"); exit; @@ -98,14 +117,34 @@ $sector_id = isset($_GET['sector_id']) ? (int)$_GET['sector_id'] : 1; $grid_size = 36; if ($view === 'sector') { - // Fetch planets AND their cities - $stmt = $db->prepare("SELECT p.*, c.name as city_name, c.type as city_type FROM planets p LEFT JOIN cities c ON p.id = c.planet_id WHERE p.galaxy_id = ? AND p.sector_id = ? AND p.slot BETWEEN 1 AND ?"); + // 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(); @@ -156,13 +195,23 @@ function getStatusColor($status, $type, $statuses_map, $object_types_map) { .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: 30px; border: 2px solid #88c0d0; width: 450px; box-shadow: 0 0 50px rgba(136, 192, 208, 0.2); max-height: 90vh; overflow-y: auto; } - .modal-content h3 { margin-top: 0; color: #88c0d0; } + .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: 12px; color: #8c92a3; margin-bottom: 5px; } - .form-group input, .form-group select { width: 100%; background: #0f172a; border: 1px solid #334155; color: #fff; padding: 10px; box-sizing: border-box; } - .btn-save { background: #a3be8c; color: #000; border: none; padding: 10px 20px; font-weight: bold; cursor: pointer; width: 100%; font-size: 16px; margin-top: 10px; } - .btn-cancel { background: #bf616a; color: #fff; border: none; padding: 10px 20px; font-weight: bold; cursor: pointer; width: 100%; margin-top: 10px; } + .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; } @@ -247,19 +296,20 @@ function getStatusColor($status, $type, $statuses_map, $object_types_map) { -
- - -
- -
- - +
+
+ + +
+
+ + +
@@ -271,23 +321,15 @@ function getStatusColor($status, $type, $statuses_map, $object_types_map) {
-
- -
- - -
-
- - +
+ +
+
+
-
+
@@ -298,7 +340,7 @@ function getStatusColor($status, $type, $statuses_map, $object_types_map) {
- +
@@ -335,9 +377,42 @@ function getStatusColor($status, $type, $statuses_map, $object_types_map) {
- \ No newline at end of file + diff --git a/index.php b/index.php index 2bd18cc..1b9cd9b 100644 --- a/index.php +++ b/index.php @@ -19,7 +19,15 @@ $sector_id = isset($_GET['sector_id']) ? (int)$_GET['sector_id'] : 1; $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); -$object_types_map = []; foreach($object_types_db as $ot) $object_types_map[$ot['slug']] = $ot; +$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) $statuses_map[$s['slug']] = $s; // Grid size: 6x6 = 36 slots per sector @@ -35,11 +43,34 @@ $resources = [ ]; if ($view === 'sector') { - $stmt = $db->prepare("SELECT p.*, c.name as city_name, c.type as city_type FROM planets p LEFT JOIN cities c ON p.id = c.planet_id WHERE p.galaxy_id = ? AND p.sector_id = ? AND p.slot BETWEEN 1 AND ?"); + $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); - foreach ($objects_raw as $obj) { $grid[$obj['slot']] = $obj; } + $planet_ids = []; + foreach ($objects_raw as $obj) { + $grid[$obj['slot']] = $obj; + $planet_ids[] = $obj['id']; + $grid[$obj['slot']]['cities'] = []; + } + + if (!empty($planet_ids)) { + $placeholders = implode(',', array_fill(0, count($planet_ids), '?')); + $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(); + 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 name FROM sectors WHERE id = ?"); $stmt->execute([$sector_id]); @@ -101,12 +132,26 @@ function getStatusColor($status, $statuses_map) { .mini-map { display: grid; grid-template-columns: repeat(6, 8px); gap: 2px; margin-bottom: 12px; background: rgba(0,0,0,0.7); padding: 5px; border: 1px solid #4c566a; } .mini-dot { width: 8px; height: 8px; background: rgba(255,255,255,0.05); } .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: #111; border-color: #3b82f6; z-index: 10; } + .slot:hover { background: #111; border-color: #3b82f6; z-index: 100; } .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; } .object-status { font-size: 9px; color: #8c92a3; } - .city-label { font-size: 8px; color: #ebcb8b; margin-top: 2px; } + .city-label { font-size: 8px; color: #ebcb8b; margin-top: 2px; display: block; width: 90%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + + /* Tooltip Styles */ + .tooltip-box { display: none; position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); background: #1e293b; border: 1px solid #3b82f6; padding: 10px; width: 180px; z-index: 1000; box-shadow: 0 10px 25px rgba(0,0,0,0.8); pointer-events: none; } + .slot:hover .tooltip-box { display: block; } + .tooltip-title { font-size: 12px; font-weight: bold; color: #88c0d0; border-bottom: 1px solid #334155; margin-bottom: 5px; padding-bottom: 3px; } + .tooltip-desc { font-size: 10px; color: #8c92a3; margin-bottom: 8px; line-height: 1.3; } + .mod-list { display: flex; flex-direction: column; gap: 3px; margin-top: 8px; } + .mod-item { font-size: 9px; padding: 2px 5px; border-radius: 2px; display: flex; align-items: center; gap: 4px; } + .mod-bonus { background: rgba(163, 190, 140, 0.2); color: #a3be8c; border: 1px solid rgba(163, 190, 140, 0.4); } + .mod-malus { background: rgba(191, 97, 106, 0.2); color: #bf616a; border: 1px solid rgba(191, 97, 106, 0.4); } + + .settlement-title { font-size: 10px; color: #ebcb8b; font-weight: bold; border-top: 1px solid #334155; margin-top: 8px; padding-top: 5px; } + .settlement-item-tool { font-size: 9px; color: #fff; margin-bottom: 3px; } + .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; } @@ -166,18 +211,50 @@ function getStatusColor($status, $statuses_map) {
- + +
+
+
+ + +
Établissements:
+ +
+ + () +
+ + + + +
+ +
+ + : +
+ +
+ +
- - + + + +
@@ -224,4 +301,4 @@ function getStatusColor($status, $statuses_map) {
- \ No newline at end of file +
NomSlugDescriptionActions
- - Suppr + + Suppr