using UnityEngine; namespace Ashwild.Harvesting { [CreateAssetMenu(fileName = "NewScatterProfile", menuName = "Harvesting/Scatter Profile")] public class ScatterProfile : ScriptableObject { [System.Serializable] public struct Entry { public GameObject prefab; [Min(0f)] public float weight; public Vector2 scaleRange; public bool alignToNormal; } [Header("Prefabs (sélection pondérée)")] [SerializeField] private Entry[] entries; [Header("Densité")] [Tooltip("Nombre d'instances visées pour 100 m² lors d'un Scatter auto.")] [SerializeField] private float density = 5f; [Tooltip("Distance minimale entre deux instances, en mètres.")] [SerializeField] private float minSpacing = 3f; [Header("Contraintes de pente (degrés)")] [SerializeField] private Vector2 slopeRange = new Vector2(0f, 35f); [Header("Enfoncement (sink)")] [Tooltip("Enfoncement aléatoire vers le bas, en mètres (min..max). Ex. arbres qui mordent le sol.")] [SerializeField] private Vector2 sinkRange = Vector2.zero; [Header("Filtre par texture du terrain")] [Tooltip("Terrain layers autorisés (glisse les assets TerrainLayer). Vide = aucune contrainte de texture.")] [SerializeField] private TerrainLayer[] allowedTerrainLayers; [Tooltip("Poids minimal de la texture autorisée sous le point (0-1).")] [SerializeField, Range(0f, 1f)] private float minTextureWeight = 0.5f; [Header("Placement")] [Tooltip("Couche(s) du terrain pour le raycast vers le bas.")] [SerializeField] private LayerMask groundMask = ~0; [Tooltip("Layer forcé sur chaque instance posée (doit être inclus dans le harvestMask du PlayerTools).")] [Layer] [SerializeField] private int placedLayer = 6; public Entry[] Entries => entries; public float Density => density; public float MinSpacing => Mathf.Max(0.01f, minSpacing); public Vector2 SlopeRange => slopeRange; public Vector2 SinkRange => sinkRange; public TerrainLayer[] AllowedTerrainLayers => allowedTerrainLayers; public float MinTextureWeight => minTextureWeight; public LayerMask GroundMask => groundMask; public int PlacedLayer => placedLayer; /// Tire une Entry au hasard selon les poids. Renvoie false si aucune entry valide. public bool TryPickEntry(System.Random rng, out Entry result) { result = default; if (entries == null || entries.Length == 0) return false; float total = 0f; for (int i = 0; i < entries.Length; i++) if (entries[i].prefab != null) total += Mathf.Max(0f, entries[i].weight); if (total <= 0f) return false; float pick = (float)rng.NextDouble() * total; for (int i = 0; i < entries.Length; i++) { if (entries[i].prefab == null) continue; pick -= Mathf.Max(0f, entries[i].weight); if (pick <= 0f) { result = entries[i]; return true; } } // Sécurité (erreurs d'arrondi) : renvoie la dernière entry valide. for (int i = entries.Length - 1; i >= 0; i--) { if (entries[i].prefab != null) { result = entries[i]; return true; } } return false; } } }