using UnityEngine; using System.Collections.Generic; /// /// Gère l'état sous tension d'un équipement rackmount. /// /// NE GÉNÈRE PAS les ports alimentation → utiliser EquipementAlim pour ça. /// NE GÉNÈRE PAS de ventilateur → utiliser un child "Ventilateur" existant /// ou laisser vide (pas de ventilo visuel). /// /// Quand alimenté : /// - LEDs d'activité s'allument (emission verte) sur la face avant /// - Ventilateur tourne (si un child "Ventilateur" est assigné) /// - Signale à AmbianceSonore qu'une machine est active /// /// Setup Unity : /// 1. Ajouter EquipementAlim sur l'équipement → générer les ports alim /// 2. Ajouter EtatEquipement sur l'équipement /// 3. Les ports alim sont trouvés automatiquement (GetComponentsInChildren) /// 4. Optionnel : créer un child "Ventilateur" et l'assigner /// 5. Configurer le nombre de LEDs, consommation, etc. /// public class EtatEquipement : MonoBehaviour { [Header("Configuration")] [Tooltip("Mode alimentation redondante : l'équipement reste allumé si au moins 1 port est alimenté")] public bool alimentationRedondante = false; [Tooltip("Consommation électrique en watts")] public float consommationWatts = 350f; [Header("LEDs")] [Tooltip("Nombre de LEDs d'activité sur la face avant")] [Range(0, 8)] public int nombreLEDs = 3; public Color couleurLEDAllumee = new Color(0f, 1f, 0f, 1f); // Vert public Color couleurLEDEteinte = new Color(0.05f, 0.05f, 0.05f, 1f); // Quasi noir public Color couleurLEDStandby = new Color(1f, 0.5f, 0f, 1f); // Orange [Tooltip("Intensité de l'émission des LEDs")] public float intensiteLED = 3f; [Tooltip("Décalage vertical des LEDs depuis le centre")] public float ledsDecalageY = 0f; [Tooltip("Décalage horizontal de départ des LEDs")] public float ledsDecalageX = 0f; [Tooltip("Espacement entre les LEDs")] public float ledsEspacement = 0.015f; [Tooltip("Taille d'une LED")] public Vector3 ledsTaille = new Vector3(0.006f, 0.006f, 0.003f); [Header("Ventilateur")] [Tooltip("Assigner un child 'Ventilateur' existant. Si vide, pas d'animation ventilo.")] public Transform ventilateur; [Tooltip("Vitesse de rotation du ventilateur en degrés/seconde")] public float vitesseVentilateur = 720f; [Tooltip("Axe de rotation du ventilateur (local)")] public Vector3 axeRotation = Vector3.forward; [Header("État (lecture seule)")] public bool estSousTension = false; public bool estEnStandby = false; [Header("Références (auto-remplies au Start)")] public List ledsRenderers = new List(); // Privé private bool _ledsGenerees = false; void Start() { // Génère les LEDs visuelles si configurées if (nombreLEDs > 0 && !_ledsGenerees) { GenererLEDs(); } // Cherche un ventilateur existant si pas assigné if (ventilateur == null) { Transform ventiloChild = transform.Find("Ventilateur"); if (ventiloChild != null) ventilateur = ventiloChild; // PAS de création automatique — on ne pollue plus le mesh } // État initial : éteint MettreAJourVisuel(); } void Update() { // Animation ventilateur if (estSousTension && ventilateur != null) { ventilateur.Rotate(axeRotation, vitesseVentilateur * Time.deltaTime, Space.Self); } } /// /// Génère les LEDs d'activité sur la face avant de l'équipement. /// private void GenererLEDs() { // Supprime les anciennes LEDs for (int i = transform.childCount - 1; i >= 0; i--) { Transform enfant = transform.GetChild(i); if (enfant.name.StartsWith("LED_Activity_")) { if (Application.isPlaying) Destroy(enfant.gameObject); else DestroyImmediate(enfant.gameObject); } } ledsRenderers.Clear(); Bounds bounds = GetBounds(); float startX = ledsDecalageX - ((nombreLEDs - 1) * ledsEspacement) / 2f; for (int i = 0; i < nombreLEDs; i++) { GameObject ledObj = GameObject.CreatePrimitive(PrimitiveType.Sphere); ledObj.name = $"LED_Activity_{i}"; ledObj.transform.SetParent(transform); ledObj.transform.localPosition = new Vector3( startX + i * ledsEspacement, ledsDecalageY, bounds.extents.z + 0.002f // Juste devant la face avant ); ledObj.transform.localScale = ledsTaille; // Désactive le collider (pas interactif) Collider ledCol = ledObj.GetComponent(); if (ledCol != null) { if (Application.isPlaying) Destroy(ledCol); else DestroyImmediate(ledCol); } // Material noir par défaut Renderer rend = ledObj.GetComponent(); if (rend != null) { rend.material = new Material(Shader.Find("Standard")); rend.material.color = couleurLEDEteinte; } ledsRenderers.Add(rend); } _ledsGenerees = true; } /// /// Vérifie l'état d'alimentation en fonction des ports connectés. /// Appelé par PortAlimentation quand l'état change. /// Cherche les ports dynamiquement via GetComponentsInChildren. /// public void VerifierAlimentation() { bool ancienEtat = estSousTension; PortAlimentation[] ports = GetComponentsInChildren(); if (ports.Length == 0) { estSousTension = false; } else if (alimentationRedondante) { // Au moins 1 port alimenté suffit estSousTension = false; foreach (var port in ports) { if (port.estAlimente) { estSousTension = true; break; } } } else { // Tous les ports doivent être alimentés estSousTension = true; foreach (var port in ports) { if (!port.estAlimente) { estSousTension = false; break; } } } if (ancienEtat != estSousTension) { MettreAJourVisuel(); string etat = estSousTension ? "SOUS TENSION" : "HORS TENSION"; Debug.Log($"{gameObject.name}: {etat}"); // Notifie l'ambiance sonore AmbianceSonore ambiance = FindObjectOfType(); if (ambiance != null) ambiance.RecalculerAmbiance(); // v7.1 : propager l'etat sous-tension au EtatEquipementNetworkSync (pour rejoin) if (Mirror.NetworkServer.active) { EtatEquipementNetworkSync sync = GetComponent(); if (sync == null) sync = GetComponentInParent(); if (sync != null) sync.ServerSyncEtat(); } } } /// /// Met à jour l'apparence visuelle selon l'état d'alimentation. /// public void MettreAJourVisuel() { // LEDs foreach (var ledRenderer in ledsRenderers) { if (ledRenderer == null) continue; Color couleur; if (estSousTension) couleur = couleurLEDAllumee; else if (estEnStandby) couleur = couleurLEDStandby; else couleur = couleurLEDEteinte; ledRenderer.material.color = couleur; ledRenderer.material.EnableKeyword("_EMISSION"); if (estSousTension || estEnStandby) ledRenderer.material.SetColor("_EmissionColor", couleur * intensiteLED); else ledRenderer.material.SetColor("_EmissionColor", Color.black); } // Clignotement if (estSousTension) InvokeRepeating(nameof(ClignoterLEDs), 0.5f, 0.3f); else CancelInvoke(nameof(ClignoterLEDs)); } /// /// Fait clignoter aléatoirement les LEDs pour simuler l'activité. /// private void ClignoterLEDs() { if (!estSousTension) return; foreach (var ledRenderer in ledsRenderers) { if (ledRenderer == null) continue; if (Random.value < 0.3f) { bool allumee = Random.value > 0.5f; Color couleur = allumee ? couleurLEDAllumee : couleurLEDEteinte; ledRenderer.material.color = couleur; ledRenderer.material.SetColor("_EmissionColor", allumee ? couleurLEDAllumee * intensiteLED : Color.black); } } } /// /// Retourne la consommation actuelle en watts (0 si éteint). /// public float GetConsommationActuelle() { return estSousTension ? consommationWatts : 0f; } private Bounds GetBounds() { BoxCollider boxCol = GetComponent(); if (boxCol != null) return new Bounds(boxCol.center, boxCol.size); Renderer rend = GetComponent(); if (rend != null) { Bounds worldBounds = rend.bounds; Vector3 localCenter = transform.InverseTransformPoint(worldBounds.center); Vector3 localSize = new Vector3( worldBounds.size.x / transform.lossyScale.x, worldBounds.size.y / transform.lossyScale.y, worldBounds.size.z / transform.lossyScale.z ); return new Bounds(localCenter, localSize); } return new Bounds(Vector3.zero, new Vector3(0.48f, 0.044f, 0.7f)); } }