new-project.js 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. // JS pour la view projects/new
  2. (function(){
  3. var templates = {
  4. neutral: { title: 'Neutre', description: 'Template neutre', emoji: '🔲' },
  5. rural: { title: 'Paysage rural', description: 'Champs et forêts', emoji: '🌾' },
  6. urban: { title: 'Paysage urbain', description: 'Villes, bâtiments et routes', emoji: '🏙️' }
  7. };
  8. var mapTemplateEl = document.getElementById('mapTemplate');
  9. // Lire les tileDefinitions injectées côté serveur
  10. var tilesDataEl = document.getElementById('templateTilesData');
  11. var tilesData = {};
  12. if (tilesDataEl) {
  13. try {
  14. tilesData = JSON.parse(tilesDataEl.textContent || '{}');
  15. } catch (e) {
  16. tilesData = {};
  17. }
  18. }
  19. if (mapTemplateEl) {
  20. mapTemplateEl.addEventListener('change', function() {
  21. var selectedTemplate = this.value;
  22. var previewDiv = document.getElementById('templatePreview');
  23. var templateImage = document.getElementById('templateImage');
  24. var templateTitle = document.getElementById('templateTitle');
  25. var templateDescription = document.getElementById('templateDescription');
  26. var templateTilesContainer = document.getElementById('templateTiles');
  27. if (!selectedTemplate) {
  28. previewDiv.style.display = 'none';
  29. return;
  30. }
  31. // Render title/description from static map or fallback
  32. var staticMeta = templates[selectedTemplate] || null;
  33. if (staticMeta) {
  34. templateImage.innerHTML = '<span style="font-size: 3rem;">' + (staticMeta.emoji || '🎲') + '</span>';
  35. templateTitle.textContent = staticMeta.title || '';
  36. templateDescription.textContent = staticMeta.description || '';
  37. } else {
  38. templateImage.innerHTML = '';
  39. templateTitle.textContent = '';
  40. templateDescription.textContent = '';
  41. }
  42. // Render tiles from tilesData if present
  43. templateTilesContainer.innerHTML = '';
  44. var defs = tilesData[selectedTemplate] || [];
  45. if (Array.isArray(defs) && defs.length > 0) {
  46. defs.forEach(function(td) {
  47. var tileEl = document.createElement('div');
  48. tileEl.className = 'd-flex align-items-center gap-2';
  49. tileEl.style.margin = '0 .25rem';
  50. // miniature : si un asset est fourni, essayer d'afficher l'SVG via <img>
  51. var swatch = document.createElement('div');
  52. swatch.style.width = '28px';
  53. swatch.style.height = '20px';
  54. swatch.style.border = '1px solid rgba(0,0,0,0.08)';
  55. swatch.style.borderRadius = '3px';
  56. swatch.style.overflow = 'hidden';
  57. if (td.asset) {
  58. var img = document.createElement('img');
  59. img.src = td.asset;
  60. img.alt = td.name || td.id || '';
  61. img.style.width = '100%';
  62. img.style.height = '100%';
  63. img.style.objectFit = 'cover';
  64. img.style.display = 'block';
  65. // Si l'image ne charge pas (chemin non servi), on affiche un fond couleur
  66. img.onerror = function() {
  67. this.remove();
  68. swatch.style.background = td.color || '#ccc';
  69. };
  70. swatch.appendChild(img);
  71. } else {
  72. swatch.style.background = td.color || '#ccc';
  73. }
  74. var label = document.createElement('div');
  75. label.style.fontSize = '0.85rem';
  76. label.style.color = '#333';
  77. label.textContent = td.name || td.id || '';
  78. var wrapper = document.createElement('div');
  79. wrapper.style.display = 'flex';
  80. wrapper.style.flexDirection = 'column';
  81. wrapper.style.alignItems = 'center';
  82. wrapper.style.minWidth = '60px';
  83. wrapper.appendChild(swatch);
  84. wrapper.appendChild(label);
  85. templateTilesContainer.appendChild(wrapper);
  86. });
  87. } else {
  88. // pas de définitions => message court
  89. var msg = document.createElement('div');
  90. msg.className = 'text-muted small';
  91. msg.textContent = 'Aucune définition de tuiles disponible pour ce modèle.';
  92. templateTilesContainer.appendChild(msg);
  93. }
  94. previewDiv.style.display = 'block';
  95. });
  96. }
  97. // Gestion des presets hexagonaux et synchronisation du radius
  98. var hexPresets = document.getElementById('hexPresets');
  99. var hexRadius = document.getElementById('hexRadius');
  100. function hexCellsFromRadius(r) {
  101. // Formule nombre d'hexagones pour un rayon r : 1 + 3r(r+1)
  102. r = parseInt(r, 10) || 0;
  103. return 1 + 3 * r * (r + 1);
  104. }
  105. // Mettre à jour le champ radius caché quand le preset change
  106. if (hexRadius) {
  107. hexRadius.addEventListener('change', function() {
  108. var r = parseInt(this.value, 10) || 0;
  109. var radiusInput = document.getElementById('radiusInput');
  110. if (radiusInput) radiusInput.value = r;
  111. // redraw preview
  112. drawHexPreview(r);
  113. });
  114. }
  115. // Dessiner un aperçu SVG exact en maillage hexagonal (pointy-top)
  116. function drawHexPreview(radius) {
  117. var container = document.getElementById('hexPreview');
  118. if (!container) return;
  119. container.innerHTML = '';
  120. var svgNS = 'http://www.w3.org/2000/svg';
  121. var svg = document.createElementNS(svgNS, 'svg');
  122. svg.setAttribute('width', '100%');
  123. svg.setAttribute('height', '100%');
  124. svg.setAttribute('viewBox', '0 0 100 100');
  125. // Générer coordonnées axiales des hexagones pour un rayon donné
  126. var cells = [];
  127. for (var q = -radius; q <= radius; q++) {
  128. var r1 = Math.max(-radius, -q - radius);
  129. var r2 = Math.min(radius, -q + radius);
  130. for (var r = r1; r <= r2; r++) {
  131. cells.push({q: q, r: r});
  132. }
  133. }
  134. // Paramètres visuels : on va centrer et scaler dans le viewBox 0..100
  135. var N = cells.length;
  136. // Pour positionner les hex, utiliser conversion axial -> pixel pour pointy-top
  137. function hexToPixel(q, r, size) {
  138. var x = size * Math.sqrt(3) * (q + r/2);
  139. var y = size * 3/2 * r;
  140. return { x: x, y: y };
  141. }
  142. // Taille estimée : calculer bounding box non-scalée
  143. var size = 1; // unité
  144. var points = cells.map(function(c) { return hexToPixel(c.q, c.r, size); });
  145. var xs = points.map(p => p.x);
  146. var ys = points.map(p => p.y);
  147. var minX = Math.min.apply(null, xs);
  148. var maxX = Math.max.apply(null, xs);
  149. var minY = Math.min.apply(null, ys);
  150. var maxY = Math.max.apply(null, ys);
  151. var width = maxX - minX || 1;
  152. var height = maxY - minY || 1;
  153. // Définir un scale pour remplir le viewBox (tous en %) avec marges
  154. var padding = 6; // % padding
  155. var avail = 100 - 2*padding;
  156. var scale = Math.min(avail / width, avail / height);
  157. // Size (pixel unit in viewBox coords)
  158. var hexSize = scale * 0.9; // ajustement visuel
  159. // Center offset
  160. var offsetX = (100 - (width * scale)) / 2 - minX * scale;
  161. var offsetY = (100 - (height * scale)) / 2 - minY * scale;
  162. // Fonction pour dessiner un hexagon pointy-top at (cx,cy) with size s
  163. function hexPoints(cx, cy, s) {
  164. var pts = [];
  165. for (var i = 0; i < 6; i++) {
  166. var angle_deg = 60 * i - 30;
  167. var angle_rad = Math.PI / 180 * angle_deg;
  168. var x = cx + s * Math.cos(angle_rad);
  169. var y = cy + s * Math.sin(angle_rad);
  170. pts.push(x + ',' + y);
  171. }
  172. return pts.join(' ');
  173. }
  174. // Draw each hex
  175. cells.forEach(function(c, idx) {
  176. var p = hexToPixel(c.q, c.r, size);
  177. var cx = p.x * scale + offsetX;
  178. var cy = p.y * scale + offsetY;
  179. var s = hexSize / 2; // visual half-size
  180. var poly = document.createElementNS(svgNS, 'polygon');
  181. poly.setAttribute('points', hexPoints(cx, cy, s));
  182. poly.setAttribute('fill', '#e9ecef');
  183. poly.setAttribute('stroke', '#6c757d');
  184. poly.setAttribute('stroke-width', Math.max(0.2, s * 0.05));
  185. poly.setAttribute('opacity', '0.95');
  186. svg.appendChild(poly);
  187. });
  188. container.appendChild(svg);
  189. }
  190. // initial draw (respecter la valeur par défaut du select)
  191. if (hexRadius) drawHexPreview(parseInt(hexRadius.value,10));
  192. var form = document.getElementById('newProjectForm');
  193. if (form) {
  194. form.addEventListener('submit', function(e) {
  195. var name = (document.getElementById('mapName').value || '').trim();
  196. var radius = parseInt((document.getElementById('radiusInput') || {}).value, 10) || 0;
  197. if (!name) {
  198. alert('Veuillez saisir un nom pour votre carte.');
  199. e.preventDefault();
  200. return;
  201. }
  202. // Validation radius (1..15)
  203. if (radius < 1 || radius > 15) {
  204. alert('Le rayon doit être compris entre 1 et 15.');
  205. e.preventDefault();
  206. return;
  207. }
  208. // Log pour debug
  209. console.log('Création du projet (radius):', { name: name, radius: radius, template: document.getElementById('mapTemplate').value });
  210. });
  211. }
  212. })();