1388 lines
59 KiB
C#
1388 lines
59 KiB
C#
// AgrandissementSalle.cs - v7.0 Multi-compatible
|
|
//
|
|
// v7.0 : Support multijoueur via SynchronisationAgrandissement
|
|
// - Nouvelle methode DemanderAgrandir() : appelee par PlayerInteraction, route
|
|
// vers le singleton SynchronisationAgrandissement qui orchestre la sync
|
|
// - Nouvelle methode publique AgrandirLocal(bool instantane) : execute l'agrandissement
|
|
// sur l'instance locale (serveur OU client). Parametre instantane=true pour le rejeu
|
|
// des clients qui rejoignent en cours de partie (pas d'animation, pas d'explosion)
|
|
// - Methode Agrandir() originale conservee pour compat editor/test
|
|
// - Cote dedicated server : skip debris/anim visuelle (pas de gestion de shader/material)
|
|
// mais execute bien la construction logique pour que les emplacements existent
|
|
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
public class AgrandissementSalle : MonoBehaviour
|
|
{
|
|
public enum DirectionAgrandissement { Nord, Sud, Est, Ouest }
|
|
|
|
[Header("Agrandissement")]
|
|
public DirectionAgrandissement direction = DirectionAgrandissement.Nord;
|
|
public float nouvelleLargeur = 10f;
|
|
public float nouvelleProfondeur = 10f;
|
|
|
|
[Header("Prix")]
|
|
public float prix = 25000f;
|
|
|
|
[Header("Animation")]
|
|
public int nombreDebris = 25;
|
|
public float forceExplosion = 5f;
|
|
public float dureeDebris = 3f;
|
|
public float dureeConstruction = 4f;
|
|
|
|
[Header("Panneau")]
|
|
public float largeurPanneau = 0.6f;
|
|
public float hauteurPanneau = 0.4f;
|
|
public Vector3 offsetPanneau = new Vector3(0f, 0f, 0.1f);
|
|
public Color couleurPanneau = new Color(0.1f, 0.3f, 0.6f, 1f);
|
|
|
|
[Header("=== Ajustements manuels ===")]
|
|
[Tooltip("Décalage manuel de la grille de dalles en X/Z (local à l'extension)")]
|
|
public Vector2 offsetGrille = Vector2.zero;
|
|
|
|
[Tooltip("Recul des murs latéraux côté passage (évite chevauchement avec la salle existante)")]
|
|
public float margeReculMurs = 0.0f;
|
|
|
|
[Tooltip("Largeur de l'ouverture entre salle et extension (0 = toute la largeur du mur cassé)")]
|
|
public float largeurOuverture = 0f;
|
|
|
|
[Header("=== Debug Gizmos ===")]
|
|
[Tooltip("Afficher les gizmos de debug dans la Scene view")]
|
|
public bool afficherGizmos = true;
|
|
public Color couleurGizmoZone = new Color(0f, 1f, 0.5f, 0.3f);
|
|
public Color couleurGizmoGrille = new Color(1f, 1f, 0f, 0.5f);
|
|
|
|
[Header("État")]
|
|
public bool estAgrandi = false;
|
|
public bool enAnimation = false;
|
|
|
|
private GameObject _panneau;
|
|
private SalleDatacenter _salleRef;
|
|
|
|
// ==================== PANNEAU ====================
|
|
|
|
public void CreerPanneau()
|
|
{
|
|
if (_panneau != null)
|
|
{
|
|
#if UNITY_EDITOR
|
|
if (!Application.isPlaying) DestroyImmediate(_panneau);
|
|
else Destroy(_panneau);
|
|
#else
|
|
Destroy(_panneau);
|
|
#endif
|
|
}
|
|
|
|
_panneau = new GameObject("Panneau_Agrandissement");
|
|
|
|
// On parente au mur (transform) pour que l'Interactable fonctionne,
|
|
// mais on compense le scale non-uniforme du mur
|
|
_panneau.transform.SetParent(transform);
|
|
_panneau.transform.localPosition = Vector3.zero;
|
|
_panneau.transform.localRotation = Quaternion.identity;
|
|
_panneau.transform.localScale = Vector3.one;
|
|
|
|
// Calculer la position WORLD du panneau (centre du mur, décalé vers l'intérieur)
|
|
SalleDatacenter salle = FindObjectOfType<SalleDatacenter>();
|
|
float sL = salle != null ? salle.longueur : 15f;
|
|
float sW = salle != null ? salle.largeur : 10f;
|
|
float hMur = salle != null ? salle.hauteurMurs / 2f : 1.6f;
|
|
Vector3 salleOrigin = salle != null ? salle.transform.position : Vector3.zero;
|
|
|
|
Vector3 panneauWorldPos = Vector3.zero;
|
|
Quaternion panneauWorldRot = Quaternion.identity;
|
|
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Nord:
|
|
panneauWorldPos = salleOrigin + new Vector3(sL / 2f, hMur, sW - 0.1f);
|
|
panneauWorldRot = Quaternion.Euler(0, 180, 0);
|
|
break;
|
|
case DirectionAgrandissement.Sud:
|
|
panneauWorldPos = salleOrigin + new Vector3(sL / 2f, hMur, 0.1f);
|
|
panneauWorldRot = Quaternion.Euler(0, 0, 0);
|
|
break;
|
|
case DirectionAgrandissement.Est:
|
|
panneauWorldPos = salleOrigin + new Vector3(sL - 0.1f, hMur, sW / 2f);
|
|
panneauWorldRot = Quaternion.Euler(0, -90, 0);
|
|
break;
|
|
case DirectionAgrandissement.Ouest:
|
|
panneauWorldPos = salleOrigin + new Vector3(0.1f, hMur, sW / 2f);
|
|
panneauWorldRot = Quaternion.Euler(0, 90, 0);
|
|
break;
|
|
}
|
|
|
|
// Détacher temporairement du mur pour positionner en world-space sans déformation
|
|
_panneau.transform.SetParent(null);
|
|
_panneau.transform.position = panneauWorldPos;
|
|
_panneau.transform.rotation = panneauWorldRot;
|
|
_panneau.transform.localScale = Vector3.one;
|
|
|
|
// Reparenter au mur (nécessaire pour l'Interactable et Agrandir)
|
|
// worldPositionStays = true pour garder la position/rotation world
|
|
_panneau.transform.SetParent(transform, true);
|
|
|
|
GameObject fond = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
fond.name = "Panneau_Fond";
|
|
fond.transform.SetParent(_panneau.transform);
|
|
fond.transform.localPosition = Vector3.zero;
|
|
fond.transform.localScale = new Vector3(largeurPanneau, hauteurPanneau, 0.02f);
|
|
if (!DedicatedServerMode.IsDedicatedServer)
|
|
{
|
|
Material matFond = new Material(Shader.Find("Standard"));
|
|
matFond.color = couleurPanneau;
|
|
matFond.EnableKeyword("_EMISSION");
|
|
matFond.SetColor("_EmissionColor", couleurPanneau * 0.3f);
|
|
fond.GetComponent<Renderer>().sharedMaterial = matFond;
|
|
}
|
|
|
|
GameObject bordure = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
bordure.name = "Panneau_Bordure";
|
|
bordure.transform.SetParent(_panneau.transform);
|
|
bordure.transform.localPosition = new Vector3(0, 0, -0.005f);
|
|
bordure.transform.localScale = new Vector3(largeurPanneau + 0.03f, hauteurPanneau + 0.03f, 0.01f);
|
|
if (!DedicatedServerMode.IsDedicatedServer)
|
|
{
|
|
Material matBord = new Material(Shader.Find("Standard"));
|
|
matBord.color = new Color(0.15f, 0.5f, 0.8f);
|
|
matBord.EnableKeyword("_EMISSION");
|
|
matBord.SetColor("_EmissionColor", new Color(0.1f, 0.4f, 0.7f) * 1.5f);
|
|
bordure.GetComponent<Renderer>().sharedMaterial = matBord;
|
|
}
|
|
Collider bc = bordure.GetComponent<Collider>();
|
|
if (bc != null) DestroyImmediate(bc);
|
|
|
|
// Textes sur la face AVANT du panneau (côté joueur = +Z local)
|
|
if (!DedicatedServerMode.IsDedicatedServer)
|
|
{
|
|
CreerTexte(_panneau.transform, "AGRANDIR", new Vector3(0, 0.1f, 0.015f), 200, 0.002f, new Color(0.8f, 0.9f, 1f));
|
|
CreerTexte(_panneau.transform, nouvelleLargeur + "m x " + nouvelleProfondeur + "m", new Vector3(0, 0.02f, 0.015f), 160, 0.0015f, new Color(0.6f, 0.8f, 0.9f));
|
|
CreerTexte(_panneau.transform, prix.ToString("N0") + " EUR", new Vector3(0, -0.08f, 0.015f), 180, 0.0018f, new Color(1f, 0.85f, 0.2f));
|
|
}
|
|
|
|
Interactable inter = fond.AddComponent<Interactable>();
|
|
inter.nomEquipement = "Agrandissement " + nouvelleLargeur + "x" + nouvelleProfondeur + "m";
|
|
inter.tailleU = 0;
|
|
}
|
|
|
|
// ==================== AGRANDIR ====================
|
|
|
|
/// <summary>
|
|
/// v7.0 : Point d'entree multijoueur. Appele par PlayerInteraction quand le joueur
|
|
/// clique [E] sur un panneau d'agrandissement. Route vers le singleton qui orchestre
|
|
/// la sync via Cmd/Rpc. En solo, le singleton appelle directement AgrandirLocal().
|
|
/// </summary>
|
|
public void DemanderAgrandir()
|
|
{
|
|
if (estAgrandi || enAnimation) return;
|
|
|
|
// Validation economique locale (pre-check, affinable cote serveur plus tard)
|
|
// En sandbox, TenterAchat retourne toujours true.
|
|
if (GameEconomy.Instance != null && !GameEconomy.Instance.modeSandbox)
|
|
{
|
|
if (!GameEconomy.Instance.TenterAchat(prix))
|
|
{
|
|
Debug.Log("[AgrandissementSalle] Fonds insuffisants pour agrandir");
|
|
return;
|
|
}
|
|
}
|
|
else if (GameEconomy.Instance != null)
|
|
{
|
|
GameEconomy.Instance.TenterAchat(prix); // log + compteur sandbox
|
|
}
|
|
|
|
// Recuperer le salleId. Si pas encore enregistree, on utilise 1 par defaut
|
|
// (cas mono-salle au demarrage).
|
|
int salleId = 1;
|
|
SalleDatacenter salleParente = FindObjectOfType<SalleDatacenter>();
|
|
if (salleParente != null && salleParente.salleId > 0)
|
|
salleId = salleParente.salleId;
|
|
|
|
if (SynchronisationAgrandissement.Instance != null)
|
|
{
|
|
SynchronisationAgrandissement.Instance.DemanderAgrandissement(salleId, direction.ToString());
|
|
}
|
|
else
|
|
{
|
|
// Fallback : si le singleton n'existe pas (scene sans multi), on execute en local direct
|
|
Debug.LogWarning("[AgrandissementSalle] SynchronisationAgrandissement.Instance introuvable, fallback solo");
|
|
AgrandirLocal(false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Version originale conservee pour compat (editor, boutons de test).
|
|
/// Equivalent a AgrandirLocal(false).
|
|
/// </summary>
|
|
public void Agrandir()
|
|
{
|
|
AgrandirLocal(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// v7.0 : Execute l'agrandissement sur l'instance LOCALE (pas de reseau).
|
|
/// Appele par SynchronisationAgrandissement.Rpc ou par Agrandir() en mode solo.
|
|
///
|
|
/// instantane=true : pas d'animation, pas d'explosion, construction immediate.
|
|
/// Utilise pour le rejeu cote client qui rejoint une partie en cours.
|
|
/// </summary>
|
|
public void AgrandirLocal(bool instantane)
|
|
{
|
|
if (estAgrandi || enAnimation) return;
|
|
|
|
_salleRef = FindObjectOfType<SalleDatacenter>();
|
|
if (_salleRef == null) { Debug.LogError("SalleDatacenter introuvable !"); return; }
|
|
|
|
// Créer un runner persistant pour toute l'animation
|
|
// (le gameObject du mur peut être détruit/désactivé par d'autres scripts)
|
|
GameObject runnerObj = new GameObject("AgrandissementRunner_" + direction);
|
|
CoroutineRunner runner = runnerObj.AddComponent<CoroutineRunner>();
|
|
runner.StartCoroutine(AnimationAgrandissement(runner, instantane));
|
|
}
|
|
|
|
IEnumerator AnimationAgrandissement(CoroutineRunner runner, bool instantane)
|
|
{
|
|
enAnimation = true;
|
|
|
|
Renderer murRend = GetComponent<Renderer>();
|
|
Collider murCol = GetComponent<Collider>();
|
|
if (murRend != null) murRend.enabled = false;
|
|
if (murCol != null) murCol.enabled = false;
|
|
if (_panneau != null) _panneau.SetActive(false);
|
|
|
|
// === PHASE 1 : EXPLOSION ===
|
|
// Skip cote serveur (pas de shader) et en mode instantane
|
|
bool skipVisuel = instantane || DedicatedServerMode.IsDedicatedServer;
|
|
|
|
if (!skipVisuel)
|
|
{
|
|
Material matDebris = new Material(Shader.Find("Standard"));
|
|
matDebris.color = _salleRef.couleurMur;
|
|
Vector3 dirVec = GetDirectionVector();
|
|
List<GameObject> debris = new List<GameObject>();
|
|
|
|
for (int i = 0; i < nombreDebris; i++)
|
|
{
|
|
GameObject d = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
d.name = "Debris_" + i;
|
|
float rx = Random.Range(-transform.localScale.x / 2f, transform.localScale.x / 2f);
|
|
float ry = Random.Range(0f, _salleRef.hauteurMurs);
|
|
d.transform.position = transform.position + transform.right * rx + Vector3.up * ry + Random.insideUnitSphere * 0.1f;
|
|
float sz = Random.Range(0.08f, 0.25f);
|
|
d.transform.localScale = new Vector3(sz, sz * Random.Range(0.5f, 1.5f), sz * Random.Range(0.3f, 0.8f));
|
|
d.transform.rotation = Random.rotation;
|
|
d.GetComponent<Renderer>().material = matDebris;
|
|
Rigidbody rb = d.AddComponent<Rigidbody>();
|
|
rb.mass = Random.Range(0.5f, 2f);
|
|
rb.AddForce((dirVec + Vector3.up * Random.Range(0.2f, 0.6f) + Random.insideUnitSphere * 0.3f) * forceExplosion, ForceMode.Impulse);
|
|
rb.AddTorque(Random.insideUnitSphere * 3f, ForceMode.Impulse);
|
|
debris.Add(d);
|
|
}
|
|
|
|
yield return new WaitForSeconds(2f);
|
|
foreach (var d in debris) if (d != null) Destroy(d);
|
|
}
|
|
|
|
// === PHASE 1.5 : NETTOYER LA SALLE EXISTANTE ===
|
|
NettoyerSalleExistante();
|
|
|
|
// === PHASE 2 : CALCUL POSITION ===
|
|
Vector3 salleOrigineMonde = _salleRef.transform.position;
|
|
Vector3 origineNouvelle = CalculerOrigineNouvelleZone(salleOrigineMonde);
|
|
|
|
float zoneL, zoneW;
|
|
if (direction == DirectionAgrandissement.Nord || direction == DirectionAgrandissement.Sud)
|
|
{ zoneL = nouvelleLargeur; zoneW = nouvelleProfondeur; }
|
|
else
|
|
{ zoneL = nouvelleProfondeur; zoneW = nouvelleLargeur; }
|
|
|
|
GameObject ext = new GameObject("Salle_Extension_" + direction);
|
|
ext.transform.position = origineNouvelle;
|
|
|
|
// === PHASE 3 : SOL ===
|
|
Debug.Log("AgrandissementSalle: PHASE 3 - Début construction sol");
|
|
yield return runner.StartCoroutine(ConstruireSol(ext.transform, zoneL, zoneW, origineNouvelle, salleOrigineMonde, skipVisuel));
|
|
Debug.Log("AgrandissementSalle: PHASE 3 - Sol terminé");
|
|
if (!skipVisuel) yield return new WaitForSeconds(0.2f);
|
|
|
|
// === PHASE 4 : MURS ===
|
|
Debug.Log("AgrandissementSalle: PHASE 4 - Début construction murs");
|
|
yield return runner.StartCoroutine(ConstruireMurs(ext.transform, zoneL, zoneW, skipVisuel));
|
|
Debug.Log("AgrandissementSalle: PHASE 4 - Murs terminés");
|
|
if (!skipVisuel) yield return new WaitForSeconds(0.2f);
|
|
|
|
// === PHASE 5 : PLAFOND ===
|
|
Debug.Log("AgrandissementSalle: PHASE 5 - Début construction plafond");
|
|
yield return runner.StartCoroutine(ConstruirePlafond(ext.transform, zoneL, zoneW, origineNouvelle, salleOrigineMonde, skipVisuel));
|
|
Debug.Log("AgrandissementSalle: PHASE 5 - Plafond terminé");
|
|
if (!skipVisuel) yield return new WaitForSeconds(0.1f);
|
|
|
|
// === PHASE 6 : ÉCLAIRAGE ===
|
|
// Skip cote serveur (lights inutiles)
|
|
if (!DedicatedServerMode.IsDedicatedServer)
|
|
{
|
|
Debug.Log("AgrandissementSalle: PHASE 6 - Début éclairage");
|
|
yield return runner.StartCoroutine(ConstruireEclairage(ext.transform, zoneL, zoneW, origineNouvelle, salleOrigineMonde, skipVisuel));
|
|
Debug.Log("AgrandissementSalle: PHASE 6 - Éclairage terminé");
|
|
}
|
|
|
|
// === PHASE 7 : EMPLACEMENTS DE BAIES ===
|
|
Debug.Log("AgrandissementSalle: PHASE 7 - Génération emplacements baies");
|
|
GenererEmplacementsExtension(ext.transform, zoneL, zoneW);
|
|
Debug.Log("AgrandissementSalle: PHASE 7 - Construction complète !");
|
|
|
|
// Nettoyage
|
|
try
|
|
{
|
|
if (this != null && gameObject != null)
|
|
{
|
|
estAgrandi = true;
|
|
enAnimation = false;
|
|
Destroy(gameObject);
|
|
}
|
|
}
|
|
catch (System.Exception) { /* mur déjà détruit, pas grave */ }
|
|
|
|
Destroy(runner.gameObject);
|
|
}
|
|
|
|
// ==================== POSITION ====================
|
|
|
|
Vector3 CalculerOrigineNouvelleZone(Vector3 salleOrigine)
|
|
{
|
|
float sL = _salleRef.longueur;
|
|
float sW = _salleRef.largeur;
|
|
return CalculerOrigineNouvelleZoneEditor(salleOrigine, sL, sW);
|
|
}
|
|
|
|
public Vector3 CalculerOrigineNouvelleZoneEditor(Vector3 salleOrigine, float sL, float sW)
|
|
{
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Ouest:
|
|
return new Vector3(salleOrigine.x - nouvelleProfondeur, salleOrigine.y,
|
|
salleOrigine.z + sW / 2f - nouvelleLargeur / 2f);
|
|
case DirectionAgrandissement.Est:
|
|
return new Vector3(salleOrigine.x + sL, salleOrigine.y,
|
|
salleOrigine.z + sW / 2f - nouvelleLargeur / 2f);
|
|
case DirectionAgrandissement.Sud:
|
|
return new Vector3(salleOrigine.x + sL / 2f - nouvelleLargeur / 2f, salleOrigine.y,
|
|
salleOrigine.z - nouvelleProfondeur);
|
|
case DirectionAgrandissement.Nord:
|
|
return new Vector3(salleOrigine.x + sL / 2f - nouvelleLargeur / 2f, salleOrigine.y,
|
|
salleOrigine.z + sW);
|
|
default: return salleOrigine;
|
|
}
|
|
}
|
|
|
|
// ==================== GRILLE CONTINUE ====================
|
|
|
|
struct GrilleInfo
|
|
{
|
|
public float startLocalX, startLocalZ;
|
|
public int gridOffsetX, gridOffsetZ;
|
|
public int nbX, nbZ;
|
|
}
|
|
|
|
GrilleInfo CalculerGrilleContinue(Vector3 origineExt, Vector3 origineSalle,
|
|
float pas, float tailleDalle, float zoneL, float zoneW)
|
|
{
|
|
GrilleInfo g = new GrilleInfo();
|
|
float deltaX = origineExt.x - origineSalle.x - offsetGrille.x;
|
|
float deltaZ = origineExt.z - origineSalle.z - offsetGrille.y;
|
|
float demiDalle = tailleDalle / 2f;
|
|
|
|
// Premier indice global dont le BORD DROIT (centre + demiDalle) est > 0
|
|
// => la dalle est au moins partiellement visible dans la zone
|
|
g.gridOffsetX = Mathf.CeilToInt((deltaX - demiDalle) / pas);
|
|
g.gridOffsetZ = Mathf.CeilToInt((deltaZ - demiDalle) / pas);
|
|
|
|
g.startLocalX = g.gridOffsetX * pas + demiDalle - deltaX;
|
|
g.startLocalZ = g.gridOffsetZ * pas + demiDalle - deltaZ;
|
|
|
|
// Compter les dalles dont le BORD GAUCHE (centre - demiDalle) est < zoneL
|
|
// => la dalle est au moins partiellement dans la zone
|
|
g.nbX = 0;
|
|
float px = g.startLocalX;
|
|
while ((px - demiDalle) < zoneL - 0.001f) { g.nbX++; px += pas; }
|
|
|
|
g.nbZ = 0;
|
|
float pz = g.startLocalZ;
|
|
while ((pz - demiDalle) < zoneW - 0.001f) { g.nbZ++; pz += pas; }
|
|
|
|
return g;
|
|
}
|
|
|
|
// ==================== NETTOYAGE SALLE EXISTANTE ====================
|
|
|
|
void NettoyerSalleExistante()
|
|
{
|
|
if (_salleRef == null) return;
|
|
Transform salleT = _salleRef.transform;
|
|
|
|
// 1) Supprimer le mur correspondant à la direction
|
|
string nomMur = "";
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Nord: nomMur = "DC_MurNord"; break;
|
|
case DirectionAgrandissement.Sud: nomMur = "DC_MurSud"; break;
|
|
case DirectionAgrandissement.Est: nomMur = "DC_MurEst"; break;
|
|
case DirectionAgrandissement.Ouest: nomMur = "DC_MurOuest"; break;
|
|
}
|
|
|
|
// Chercher tous les objets à modifier/supprimer
|
|
Transform murT = null;
|
|
Transform plafondSolideT = null;
|
|
Transform solColliderT = null;
|
|
Transform sousSolT = null;
|
|
|
|
foreach (Transform child in salleT.GetComponentsInChildren<Transform>())
|
|
{
|
|
if (child.name == nomMur) murT = child;
|
|
else if (child.name == "DC_PlafondSolide") plafondSolideT = child;
|
|
else if (child.name == "DC_SolCollider") solColliderT = child;
|
|
else if (child.name == "DC_SousSol") sousSolT = child;
|
|
}
|
|
|
|
// Supprimer le mur
|
|
if (murT != null)
|
|
{
|
|
Debug.Log("AgrandissementSalle: Suppression " + nomMur);
|
|
Destroy(murT.gameObject);
|
|
}
|
|
|
|
// 2) Redimensionner plafond solide, sol collider et sous-sol
|
|
// pour qu'ils ne dépassent pas côté extension
|
|
// (on les réduit à la taille intérieure de la salle, sans débordement)
|
|
float sL = _salleRef.longueur;
|
|
float sW = _salleRef.largeur;
|
|
|
|
if (plafondSolideT != null)
|
|
{
|
|
// Réduire à longueur x largeur (sans + epaisseurMur * 2)
|
|
Vector3 sc = plafondSolideT.localScale;
|
|
plafondSolideT.localScale = new Vector3(sL, sc.y, sW);
|
|
plafondSolideT.localPosition = new Vector3(sL / 2f, plafondSolideT.localPosition.y, sW / 2f);
|
|
Debug.Log("AgrandissementSalle: PlafondSolide redimensionné à " + sL + "x" + sW);
|
|
}
|
|
|
|
if (solColliderT != null)
|
|
{
|
|
BoxCollider bc = solColliderT.GetComponent<BoxCollider>();
|
|
if (bc != null)
|
|
{
|
|
float solSommetN = _salleRef.hauteurSol - 0.01f;
|
|
float solBaseN = -0.1f;
|
|
float solEpN = solSommetN - solBaseN;
|
|
bc.size = new Vector3(sL, solEpN, sW);
|
|
}
|
|
float sCentreN = (_salleRef.hauteurSol - 0.01f + (-0.1f)) / 2f;
|
|
solColliderT.localPosition = new Vector3(sL / 2f, sCentreN, sW / 2f);
|
|
Debug.Log("AgrandissementSalle: SolCollider redimensionné");
|
|
}
|
|
|
|
if (sousSolT != null)
|
|
{
|
|
sousSolT.localScale = new Vector3(sL, sousSolT.localScale.y, sW);
|
|
sousSolT.localPosition = new Vector3(sL / 2f, sousSolT.localPosition.y, sW / 2f);
|
|
}
|
|
}
|
|
|
|
// ==================== SOL ====================
|
|
|
|
IEnumerator ConstruireSol(Transform parent, float longueur, float largeur,
|
|
Vector3 origineExt, Vector3 origineSalle, bool skipVisuel)
|
|
{
|
|
float hauteurSol = _salleRef.hauteurSol;
|
|
float tailleDalle = _salleRef.tailleDalle;
|
|
float epaisseurDalle = _salleRef.epaisseurDalle;
|
|
float espacement = _salleRef.espacementDalles;
|
|
float pas = tailleDalle + espacement;
|
|
|
|
Material matDalle = CreerMat(_salleRef.couleurDalleSol, 0.1f, 0.3f);
|
|
Material matVent = CreerMat(_salleRef.couleurDalleVentilee, 0.2f, 0.5f);
|
|
Material matDalleAlt = CreerMat(
|
|
new Color(_salleRef.couleurDalleSol.r - 0.03f, _salleRef.couleurDalleSol.g - 0.03f,
|
|
_salleRef.couleurDalleSol.b - 0.02f, 1f), 0.1f, 0.3f);
|
|
Material matPied = CreerMat(_salleRef.couleurPiedestal, 0.3f, 0.4f);
|
|
Material matSousSol = CreerMat(_salleRef.couleurSousSol, 0f, 0.1f);
|
|
|
|
// Sous-sol visuel
|
|
GameObject ss = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
ss.name = "Ext_SousSol";
|
|
ss.transform.SetParent(parent);
|
|
ss.transform.localPosition = new Vector3(longueur / 2f, -0.05f, largeur / 2f);
|
|
ss.transform.localScale = new Vector3(longueur, 0.1f, largeur);
|
|
if (ss.GetComponent<Renderer>() != null && matSousSol != null)
|
|
ss.GetComponent<Renderer>().sharedMaterial = matSousSol;
|
|
ss.isStatic = true;
|
|
// Supprimer le collider du sous-sol visuel (le sol collider gère tout)
|
|
Collider ssCol = ss.GetComponent<Collider>();
|
|
if (ssCol != null) Destroy(ssCol);
|
|
|
|
// Collider sol - TOUJOURS cree (meme cote serveur pour le CharacterController)
|
|
GameObject solC = new GameObject("Ext_SolCollider");
|
|
solC.transform.SetParent(parent);
|
|
float solSommet = hauteurSol - 0.01f;
|
|
float solBase = -0.1f;
|
|
float solEp = solSommet - solBase;
|
|
float solCentre = (solSommet + solBase) / 2f;
|
|
solC.transform.localPosition = new Vector3(longueur / 2f, solCentre, largeur / 2f);
|
|
BoxCollider col = solC.AddComponent<BoxCollider>();
|
|
col.size = new Vector3(longueur, solEp, largeur);
|
|
int layerSol = LayerMask.NameToLayer("Sol");
|
|
if (layerSol >= 0) solC.layer = layerSol;
|
|
|
|
// Dalle de fond continue
|
|
GameObject fondSol = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
fondSol.name = "Ext_FondSol";
|
|
fondSol.transform.SetParent(parent);
|
|
fondSol.transform.localPosition = new Vector3(longueur / 2f, hauteurSol - 0.001f, largeur / 2f);
|
|
fondSol.transform.localScale = new Vector3(longueur, epaisseurDalle, largeur);
|
|
if (fondSol.GetComponent<Renderer>() != null && matDalle != null)
|
|
fondSol.GetComponent<Renderer>().sharedMaterial = matDalle;
|
|
fondSol.isStatic = true;
|
|
Collider fc = fondSol.GetComponent<Collider>();
|
|
if (fc != null) Destroy(fc);
|
|
|
|
// === Dalles alignées grille globale ===
|
|
GrilleInfo g = CalculerGrilleContinue(origineExt, origineSalle, pas, tailleDalle, longueur, largeur);
|
|
Debug.Log($"Extension Sol: {g.nbX}x{g.nbZ} dalles, start=({g.startLocalX:F3},{g.startLocalZ:F3})");
|
|
|
|
// Calculer les allées froides de l'extension
|
|
List<Vector2> alleesFroides = CalculerZonesAlleesFroidesExtension(longueur, largeur);
|
|
|
|
int count = 0;
|
|
int total = g.nbX * g.nbZ;
|
|
int dallesParFrame = Mathf.Max(1, total / Mathf.Max(1, (int)(dureeConstruction * 20)));
|
|
|
|
for (int ix = 0; ix < g.nbX; ix++)
|
|
{
|
|
for (int iz = 0; iz < g.nbZ; iz++)
|
|
{
|
|
float px = g.startLocalX + ix * pas;
|
|
float pz = g.startLocalZ + iz * pas;
|
|
|
|
int globalX = g.gridOffsetX + ix;
|
|
int globalZ = g.gridOffsetZ + iz;
|
|
|
|
// Vérifier si dans une allée froide
|
|
bool dansAlleeFroide = false;
|
|
foreach (Vector2 zone in alleesFroides)
|
|
{
|
|
if (pz >= zone.x && pz <= zone.y)
|
|
{
|
|
dansAlleeFroide = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
Material matChoisi;
|
|
if (dansAlleeFroide)
|
|
matChoisi = matVent;
|
|
else
|
|
matChoisi = ((Mathf.Abs(globalX + globalZ)) % 2 == 0) ? matDalle : matDalleAlt;
|
|
|
|
GameObject dalle = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
dalle.name = $"Ext_Dalle_{ix}_{iz}";
|
|
dalle.transform.SetParent(parent);
|
|
dalle.transform.localPosition = new Vector3(px, hauteurSol, pz);
|
|
dalle.transform.localScale = new Vector3(tailleDalle, epaisseurDalle, tailleDalle);
|
|
if (dalle.GetComponent<Renderer>() != null && matChoisi != null)
|
|
dalle.GetComponent<Renderer>().sharedMaterial = matChoisi;
|
|
dalle.isStatic = true;
|
|
Collider dc = dalle.GetComponent<Collider>();
|
|
if (dc != null) Destroy(dc);
|
|
|
|
if (Mathf.Abs(globalX) % 2 == 0 && Mathf.Abs(globalZ) % 2 == 0)
|
|
{
|
|
float piedH = hauteurSol - epaisseurDalle / 2f;
|
|
GameObject pied = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
pied.name = $"Ext_Pied_{ix}_{iz}";
|
|
pied.transform.SetParent(parent);
|
|
pied.transform.localPosition = new Vector3(px, piedH / 2f, pz);
|
|
pied.transform.localScale = new Vector3(0.04f, piedH, 0.04f);
|
|
if (pied.GetComponent<Renderer>() != null && matPied != null)
|
|
pied.GetComponent<Renderer>().sharedMaterial = matPied;
|
|
pied.isStatic = true;
|
|
Collider pc = pied.GetComponent<Collider>();
|
|
if (pc != null) Destroy(pc);
|
|
}
|
|
|
|
count++;
|
|
// Mode instantane : pas de yield, tout dans la meme frame
|
|
if (!skipVisuel && count % dallesParFrame == 0) yield return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================== MURS ====================
|
|
|
|
IEnumerator ConstruireMurs(Transform parent, float longueur, float largeur, bool skipVisuel)
|
|
{
|
|
Material matMur = CreerMat(_salleRef.couleurMur, 0f, 0.2f);
|
|
float hauteur = _salleRef.hauteurMurs;
|
|
float ep = _salleRef.epaisseurMur;
|
|
float mY = hauteur / 2f;
|
|
|
|
// Recul automatique : les murs latéraux de l'extension doivent s'arrêter
|
|
// avant les murs existants de la salle (épaisseur du mur existant)
|
|
float recul = margeReculMurs + ep;
|
|
|
|
List<Vector3> positions = new List<Vector3>();
|
|
List<Vector3> tailles = new List<Vector3>();
|
|
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Ouest:
|
|
{
|
|
positions.Add(new Vector3(-ep / 2f, mY, largeur / 2f));
|
|
tailles.Add(new Vector3(ep, hauteur, largeur + ep * 2));
|
|
float murLen = longueur - recul;
|
|
positions.Add(new Vector3(murLen / 2f, mY, largeur + ep / 2f));
|
|
tailles.Add(new Vector3(murLen, hauteur, ep));
|
|
positions.Add(new Vector3(murLen / 2f, mY, -ep / 2f));
|
|
tailles.Add(new Vector3(murLen, hauteur, ep));
|
|
break;
|
|
}
|
|
case DirectionAgrandissement.Est:
|
|
{
|
|
positions.Add(new Vector3(longueur + ep / 2f, mY, largeur / 2f));
|
|
tailles.Add(new Vector3(ep, hauteur, largeur + ep * 2));
|
|
float murLen = longueur - recul;
|
|
float offX = recul + murLen / 2f;
|
|
positions.Add(new Vector3(offX, mY, largeur + ep / 2f));
|
|
tailles.Add(new Vector3(murLen, hauteur, ep));
|
|
positions.Add(new Vector3(offX, mY, -ep / 2f));
|
|
tailles.Add(new Vector3(murLen, hauteur, ep));
|
|
break;
|
|
}
|
|
case DirectionAgrandissement.Nord:
|
|
{
|
|
positions.Add(new Vector3(longueur / 2f, mY, largeur + ep / 2f));
|
|
tailles.Add(new Vector3(longueur + ep * 2, hauteur, ep));
|
|
float murLen = largeur - recul;
|
|
float offZ = recul + murLen / 2f;
|
|
positions.Add(new Vector3(longueur + ep / 2f, mY, offZ));
|
|
tailles.Add(new Vector3(ep, hauteur, murLen));
|
|
positions.Add(new Vector3(-ep / 2f, mY, offZ));
|
|
tailles.Add(new Vector3(ep, hauteur, murLen));
|
|
break;
|
|
}
|
|
case DirectionAgrandissement.Sud:
|
|
{
|
|
positions.Add(new Vector3(longueur / 2f, mY, -ep / 2f));
|
|
tailles.Add(new Vector3(longueur + ep * 2, hauteur, ep));
|
|
float murLen = largeur - recul;
|
|
positions.Add(new Vector3(longueur + ep / 2f, mY, murLen / 2f));
|
|
tailles.Add(new Vector3(ep, hauteur, murLen));
|
|
positions.Add(new Vector3(-ep / 2f, mY, murLen / 2f));
|
|
tailles.Add(new Vector3(ep, hauteur, murLen));
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < positions.Count; i++)
|
|
{
|
|
GameObject mur = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
mur.name = "Ext_Mur_" + i;
|
|
mur.transform.SetParent(parent);
|
|
|
|
if (skipVisuel)
|
|
{
|
|
// Construction immediate (rejeu ou serveur) : taille finale direct
|
|
mur.transform.localScale = tailles[i];
|
|
mur.transform.localPosition = positions[i];
|
|
}
|
|
else
|
|
{
|
|
// Animation : le mur monte progressivement
|
|
mur.transform.localScale = new Vector3(tailles[i].x, 0.01f, tailles[i].z);
|
|
mur.transform.localPosition = new Vector3(positions[i].x, 0.005f, positions[i].z);
|
|
}
|
|
|
|
if (mur.GetComponent<Renderer>() != null && matMur != null)
|
|
mur.GetComponent<Renderer>().sharedMaterial = matMur;
|
|
mur.isStatic = true;
|
|
mur.AddComponent<BoxCollider>();
|
|
|
|
if (!skipVisuel)
|
|
{
|
|
float elapsed = 0f;
|
|
while (elapsed < 0.6f)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = 1f - Mathf.Pow(1f - elapsed / 0.6f, 3f);
|
|
float h = Mathf.Lerp(0.01f, tailles[i].y, t);
|
|
mur.transform.localScale = new Vector3(tailles[i].x, h, tailles[i].z);
|
|
mur.transform.localPosition = new Vector3(positions[i].x, h / 2f, positions[i].z);
|
|
yield return null;
|
|
}
|
|
mur.transform.localScale = tailles[i];
|
|
mur.transform.localPosition = positions[i];
|
|
yield return new WaitForSeconds(0.1f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================== PLAFOND ====================
|
|
|
|
IEnumerator ConstruirePlafond(Transform parent, float longueur, float largeur,
|
|
Vector3 origineExt, Vector3 origineSalle, bool skipVisuel)
|
|
{
|
|
float hauteurPlafond = _salleRef.hauteurPlafond;
|
|
float hauteurMurs = _salleRef.hauteurMurs;
|
|
float tailleDalle = _salleRef.tailleDallePlafond;
|
|
float epaisseurPlafond = _salleRef.epaisseurPlafond;
|
|
float espacement = _salleRef.espacementDalles;
|
|
float pas = tailleDalle + espacement;
|
|
|
|
Material matDalle = CreerMat(_salleRef.couleurPlafondDalle, 0f, 0.2f);
|
|
Material matSolide = CreerMat(_salleRef.couleurSousSol, 0f, 0.1f);
|
|
Material matRail = CreerMat(_salleRef.couleurPiedestal, 0.3f, 0.4f);
|
|
|
|
// Plafond solide — animation descente (ou direct en mode skip)
|
|
GameObject ps = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
ps.name = "Ext_PlafondSolide";
|
|
ps.transform.SetParent(parent);
|
|
Vector3 posFinale = new Vector3(longueur / 2f, hauteurMurs, largeur / 2f);
|
|
|
|
if (skipVisuel)
|
|
{
|
|
ps.transform.localPosition = posFinale;
|
|
}
|
|
else
|
|
{
|
|
ps.transform.localPosition = new Vector3(longueur / 2f, hauteurMurs + 1f, largeur / 2f);
|
|
}
|
|
ps.transform.localScale = new Vector3(longueur, 0.1f, largeur);
|
|
if (ps.GetComponent<Renderer>() != null && matSolide != null)
|
|
ps.GetComponent<Renderer>().sharedMaterial = matSolide;
|
|
ps.isStatic = true;
|
|
|
|
if (!skipVisuel)
|
|
{
|
|
float elapsed = 0f;
|
|
while (elapsed < 0.8f)
|
|
{
|
|
elapsed += Time.deltaTime;
|
|
float t = elapsed / 0.8f; t = t * t * (3f - 2f * t);
|
|
ps.transform.localPosition = Vector3.Lerp(new Vector3(longueur / 2f, hauteurMurs + 1f, largeur / 2f), posFinale, t);
|
|
yield return null;
|
|
}
|
|
ps.transform.localPosition = posFinale;
|
|
}
|
|
|
|
// Dalle de fond plafond
|
|
GameObject fondPlaf = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
fondPlaf.name = "Ext_FondPlafond";
|
|
fondPlaf.transform.SetParent(parent);
|
|
fondPlaf.transform.localPosition = new Vector3(longueur / 2f, hauteurPlafond + 0.001f, largeur / 2f);
|
|
fondPlaf.transform.localScale = new Vector3(longueur, epaisseurPlafond, largeur);
|
|
if (fondPlaf.GetComponent<Renderer>() != null && matDalle != null)
|
|
fondPlaf.GetComponent<Renderer>().sharedMaterial = matDalle;
|
|
fondPlaf.isStatic = true;
|
|
Collider fpc = fondPlaf.GetComponent<Collider>();
|
|
if (fpc != null) Destroy(fpc);
|
|
|
|
// === Dalles plafond alignées grille globale ===
|
|
GrilleInfo g = CalculerGrilleContinue(origineExt, origineSalle, pas, tailleDalle, longueur, largeur);
|
|
Debug.Log($"Extension Plafond: {g.nbX}x{g.nbZ} dalles");
|
|
|
|
for (int ix = 0; ix < g.nbX; ix++)
|
|
{
|
|
for (int iz = 0; iz < g.nbZ; iz++)
|
|
{
|
|
float px = g.startLocalX + ix * pas;
|
|
float pz = g.startLocalZ + iz * pas;
|
|
|
|
GameObject d = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
d.name = $"Ext_PlafDalle_{ix}_{iz}";
|
|
d.transform.SetParent(parent);
|
|
d.transform.localPosition = new Vector3(px, hauteurPlafond, pz);
|
|
d.transform.localScale = new Vector3(tailleDalle - 0.02f, epaisseurPlafond, tailleDalle - 0.02f);
|
|
if (d.GetComponent<Renderer>() != null && matDalle != null)
|
|
d.GetComponent<Renderer>().sharedMaterial = matDalle;
|
|
d.isStatic = true;
|
|
Collider c = d.GetComponent<Collider>();
|
|
if (c != null) Destroy(c);
|
|
}
|
|
}
|
|
|
|
// Rails
|
|
float railStartX = g.startLocalX - tailleDalle / 2f;
|
|
float railStartZ = g.startLocalZ - tailleDalle / 2f;
|
|
|
|
for (int ix = 0; ix <= g.nbX; ix++)
|
|
{
|
|
float px = railStartX + ix * pas;
|
|
GameObject r = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
r.name = "Ext_RailX_" + ix;
|
|
r.transform.SetParent(parent);
|
|
r.transform.localPosition = new Vector3(px, hauteurPlafond + epaisseurPlafond / 2f, largeur / 2f);
|
|
r.transform.localScale = new Vector3(0.02f, 0.03f, largeur);
|
|
if (r.GetComponent<Renderer>() != null && matRail != null)
|
|
r.GetComponent<Renderer>().sharedMaterial = matRail;
|
|
r.isStatic = true;
|
|
Collider c = r.GetComponent<Collider>();
|
|
if (c != null) Destroy(c);
|
|
}
|
|
for (int iz = 0; iz <= g.nbZ; iz++)
|
|
{
|
|
float pz = railStartZ + iz * pas;
|
|
GameObject r = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
r.name = "Ext_RailZ_" + iz;
|
|
r.transform.SetParent(parent);
|
|
r.transform.localPosition = new Vector3(longueur / 2f, hauteurPlafond + epaisseurPlafond / 2f, pz);
|
|
r.transform.localScale = new Vector3(longueur, 0.03f, 0.02f);
|
|
if (r.GetComponent<Renderer>() != null && matRail != null)
|
|
r.GetComponent<Renderer>().sharedMaterial = matRail;
|
|
r.isStatic = true;
|
|
Collider c = r.GetComponent<Collider>();
|
|
if (c != null) Destroy(c);
|
|
}
|
|
}
|
|
|
|
// ==================== ÉCLAIRAGE ====================
|
|
|
|
IEnumerator ConstruireEclairage(Transform parent, float longueur, float largeur,
|
|
Vector3 origineExt, Vector3 origineSalle, bool skipVisuel)
|
|
{
|
|
float hauteurPlafond = _salleRef.hauteurPlafond;
|
|
float tailleDalle = _salleRef.tailleDallePlafond;
|
|
float espacement = _salleRef.espacementDalles;
|
|
int freqNeon = _salleRef.frequenceNeon;
|
|
float intensite = _salleRef.intensiteLumiere;
|
|
Color couleurLum = _salleRef.couleurLumiere;
|
|
Color couleurNeon = _salleRef.couleurNeon;
|
|
float pas = tailleDalle + espacement;
|
|
|
|
GrilleInfo g = CalculerGrilleContinue(origineExt, origineSalle, pas, tailleDalle, longueur, largeur);
|
|
|
|
// Calculer nbPlafondZ de la salle pour reproduire le même compteur linéaire
|
|
float salleL = _salleRef.longueur;
|
|
float salleW = _salleRef.largeur;
|
|
int nbPlafondZSalle = 0;
|
|
while ((nbPlafondZSalle * pas + tailleDalle / 2f) <= salleW + 0.001f) nbPlafondZSalle++;
|
|
|
|
List<Material> mats = new List<Material>();
|
|
List<Light> lts = new List<Light>();
|
|
|
|
for (int ix = 0; ix < g.nbX; ix++)
|
|
{
|
|
for (int iz = 0; iz < g.nbZ; iz++)
|
|
{
|
|
int globalX = g.gridOffsetX + ix;
|
|
int globalZ = g.gridOffsetZ + iz;
|
|
|
|
// Reproduire le compteur linéaire de la salle : globalX * nbZ + globalZ
|
|
// Pour les indices négatifs, on utilise Abs pour garder la cohérence
|
|
int globalCount = Mathf.Abs(globalX) * nbPlafondZSalle + Mathf.Abs(globalZ);
|
|
bool estNeon = (globalCount % freqNeon == 0);
|
|
|
|
if (estNeon)
|
|
{
|
|
float px = g.startLocalX + ix * pas;
|
|
float pz = g.startLocalZ + iz * pas;
|
|
|
|
GameObject neon = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
neon.name = $"Ext_Neon_{ix}_{iz}";
|
|
neon.transform.SetParent(parent);
|
|
neon.transform.localPosition = new Vector3(px, hauteurPlafond, pz);
|
|
neon.transform.localScale = new Vector3(tailleDalle - 0.05f, 0.03f, tailleDalle - 0.05f);
|
|
|
|
Material m = new Material(Shader.Find("Standard"));
|
|
m.color = couleurNeon;
|
|
m.EnableKeyword("_EMISSION");
|
|
if (skipVisuel)
|
|
m.SetColor("_EmissionColor", couleurNeon * 2f); // tout de suite allume
|
|
else
|
|
m.SetColor("_EmissionColor", Color.black);
|
|
m.SetFloat("_Glossiness", 0.8f);
|
|
if (neon.GetComponent<Renderer>() != null)
|
|
neon.GetComponent<Renderer>().material = m;
|
|
neon.isStatic = true;
|
|
Collider c = neon.GetComponent<Collider>();
|
|
if (c != null) Destroy(c);
|
|
|
|
GameObject lo = new GameObject($"Ext_Light_{ix}_{iz}");
|
|
lo.transform.SetParent(parent);
|
|
lo.transform.localPosition = new Vector3(px, hauteurPlafond - 0.1f, pz);
|
|
Light l = lo.AddComponent<Light>();
|
|
l.type = LightType.Point;
|
|
l.color = couleurLum;
|
|
l.range = 4f;
|
|
l.shadows = LightShadows.Hard;
|
|
l.intensity = skipVisuel ? intensite : 0f;
|
|
lo.isStatic = true;
|
|
|
|
mats.Add(m);
|
|
lts.Add(l);
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug.Log($"Extension Eclairage: {mats.Count} néons créés");
|
|
|
|
// Animation d'allumage progressif uniquement en mode non-skip
|
|
if (!skipVisuel)
|
|
{
|
|
Color emMax = couleurNeon * 2f;
|
|
for (int i = 0; i < mats.Count; i++)
|
|
{
|
|
float el = 0f;
|
|
while (el < 0.3f)
|
|
{
|
|
el += Time.deltaTime;
|
|
float t = el / 0.3f;
|
|
if (mats[i] != null) mats[i].SetColor("_EmissionColor", emMax * t);
|
|
if (lts[i] != null) lts[i].intensity = Mathf.Lerp(0f, intensite, t);
|
|
yield return null;
|
|
}
|
|
if (mats[i] != null) mats[i].SetColor("_EmissionColor", emMax);
|
|
if (lts[i] != null) lts[i].intensity = intensite;
|
|
yield return new WaitForSeconds(0.06f);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================== GIZMOS ====================
|
|
|
|
#if UNITY_EDITOR
|
|
void OnDrawGizmosSelected()
|
|
{
|
|
if (!afficherGizmos) return;
|
|
|
|
SalleDatacenter salle = FindObjectOfType<SalleDatacenter>();
|
|
if (salle == null) return;
|
|
|
|
Vector3 salleOrigine = salle.transform.position;
|
|
float sL = salle.longueur;
|
|
float sW = salle.largeur;
|
|
Vector3 origineExt = CalculerOrigineNouvelleZoneEditor(salleOrigine, sL, sW);
|
|
|
|
float zoneL, zoneW;
|
|
if (direction == DirectionAgrandissement.Nord || direction == DirectionAgrandissement.Sud)
|
|
{ zoneL = nouvelleLargeur; zoneW = nouvelleProfondeur; }
|
|
else
|
|
{ zoneL = nouvelleProfondeur; zoneW = nouvelleLargeur; }
|
|
|
|
float hauteurSol = salle.hauteurSol;
|
|
float hMur = salle.hauteurMurs;
|
|
float ep = salle.epaisseurMur;
|
|
|
|
// Zone d'extension
|
|
Gizmos.color = couleurGizmoZone;
|
|
Vector3 centre = origineExt + new Vector3(zoneL / 2f, hauteurSol + 0.05f, zoneW / 2f);
|
|
Vector3 size = new Vector3(zoneL, 0.02f, zoneW);
|
|
Gizmos.DrawCube(centre, size);
|
|
Gizmos.color = new Color(couleurGizmoZone.r, couleurGizmoZone.g, couleurGizmoZone.b, 1f);
|
|
Gizmos.DrawWireCube(centre, size);
|
|
|
|
// Grille extension (damier)
|
|
float tailleDalle = salle.tailleDalle;
|
|
float espacement = salle.espacementDalles;
|
|
float pas = tailleDalle + espacement;
|
|
GrilleInfo g = CalculerGrilleContinue(origineExt, salleOrigine, pas, tailleDalle, zoneL, zoneW);
|
|
|
|
for (int ix = 0; ix < g.nbX; ix++)
|
|
{
|
|
for (int iz = 0; iz < g.nbZ; iz++)
|
|
{
|
|
float px = g.startLocalX + ix * pas;
|
|
float pz = g.startLocalZ + iz * pas;
|
|
Vector3 pos = origineExt + new Vector3(px, hauteurSol + 0.06f, pz);
|
|
int globalX = g.gridOffsetX + ix;
|
|
int globalZ = g.gridOffsetZ + iz;
|
|
bool foncee = (globalX + globalZ) % 2 == 1;
|
|
Gizmos.color = foncee
|
|
? new Color(0.3f, 0.3f, 0.3f, 0.6f)
|
|
: new Color(1f, 1f, 0.5f, 0.6f);
|
|
Gizmos.DrawCube(pos, new Vector3(tailleDalle * 0.9f, 0.01f, tailleDalle * 0.9f));
|
|
}
|
|
}
|
|
|
|
// Grille salle existante (wireframe)
|
|
int nbXSalle = Mathf.CeilToInt(sL / pas);
|
|
int nbZSalle = Mathf.CeilToInt(sW / pas);
|
|
Gizmos.color = new Color(0.2f, 0.6f, 1f, 0.15f);
|
|
for (int x = 0; x < nbXSalle; x++)
|
|
{
|
|
for (int z = 0; z < nbZSalle; z++)
|
|
{
|
|
float px = x * pas + tailleDalle / 2f;
|
|
float pz = z * pas + tailleDalle / 2f;
|
|
Vector3 pos = salleOrigine + new Vector3(px, hauteurSol + 0.07f, pz);
|
|
Gizmos.DrawWireCube(pos, new Vector3(tailleDalle * 0.85f, 0.005f, tailleDalle * 0.85f));
|
|
}
|
|
}
|
|
|
|
// Murs latéraux (preview)
|
|
Gizmos.color = new Color(1f, 0.4f, 0.2f, 0.25f);
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Ouest:
|
|
case DirectionAgrandissement.Est:
|
|
{
|
|
float murLen = zoneL - margeReculMurs;
|
|
float startX = (direction == DirectionAgrandissement.Ouest) ? 0 : margeReculMurs;
|
|
Gizmos.DrawCube(origineExt + new Vector3(startX + murLen / 2f, hMur / 2f, zoneW + ep / 2f), new Vector3(murLen, hMur, ep));
|
|
Gizmos.DrawCube(origineExt + new Vector3(startX + murLen / 2f, hMur / 2f, -ep / 2f), new Vector3(murLen, hMur, ep));
|
|
break;
|
|
}
|
|
case DirectionAgrandissement.Nord:
|
|
case DirectionAgrandissement.Sud:
|
|
{
|
|
float murLen = zoneW - margeReculMurs;
|
|
float startZ = (direction == DirectionAgrandissement.Sud) ? 0 : margeReculMurs;
|
|
Gizmos.DrawCube(origineExt + new Vector3(zoneL + ep / 2f, hMur / 2f, startZ + murLen / 2f), new Vector3(ep, hMur, murLen));
|
|
Gizmos.DrawCube(origineExt + new Vector3(-ep / 2f, hMur / 2f, startZ + murLen / 2f), new Vector3(ep, hMur, murLen));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Label
|
|
Vector3 labelPos = origineExt + new Vector3(zoneL / 2f, hMur + 0.5f, zoneW / 2f);
|
|
Handles.Label(labelPos,
|
|
$"Extension {direction}\n{zoneL}m x {zoneW}m\n" +
|
|
$"Grille: {g.nbX}x{g.nbZ} dalles\n" +
|
|
$"Start: ({g.startLocalX:F3}, {g.startLocalZ:F3})\n" +
|
|
$"Offset: ({offsetGrille.x:F3}, {offsetGrille.y:F3})\n" +
|
|
$"Recul: {margeReculMurs}m");
|
|
}
|
|
#endif
|
|
|
|
// ==================== EMPLACEMENTS EXTENSION ====================
|
|
|
|
/// <summary>
|
|
/// Calcule les zones d'allées froides pour l'extension (en local de l'extension).
|
|
/// Pour Est/Ouest : réutilise les mêmes positions Z que la salle d'origine.
|
|
/// Pour Nord/Sud : recalcule pour les nouvelles dimensions.
|
|
/// </summary>
|
|
List<Vector2> CalculerZonesAlleesFroidesExtension(float extLongueur, float extLargeur)
|
|
{
|
|
if (_salleRef == null) return new List<Vector2>();
|
|
|
|
// Pour Est/Ouest : si même largeur que la salle, réutiliser DIRECTEMENT
|
|
// les zones d'allées froides de la salle (même positions Z)
|
|
if (direction == DirectionAgrandissement.Est || direction == DirectionAgrandissement.Ouest)
|
|
{
|
|
if (Mathf.Abs(extLargeur - _salleRef.largeur) < 0.1f)
|
|
return _salleRef.CalculerZonesAlleesFroides();
|
|
}
|
|
|
|
// Sinon : recalculer pour les dimensions de l'extension
|
|
float profondeur = _salleRef.profondeurEmplacement;
|
|
float alleeFroide = _salleRef.espaceAlleeFroide;
|
|
float alleeChaude = _salleRef.espaceAlleeChaude;
|
|
float marge = _salleRef.margeDebutRangee;
|
|
float pasDalle = _salleRef.tailleDalle + _salleRef.espacementDalles;
|
|
|
|
float largeurPaire = profondeur * 2 + alleeFroide;
|
|
int nbPairesMax = Mathf.FloorToInt((extLargeur - marge * 2 + alleeChaude) / (largeurPaire + alleeChaude));
|
|
if (nbPairesMax < 1) nbPairesMax = 1;
|
|
int nbRangeesExt = nbPairesMax * 2;
|
|
|
|
float totalZ = nbPairesMax * largeurPaire + (nbPairesMax - 1) * alleeChaude;
|
|
float startZ = (extLargeur - totalZ) / 2f;
|
|
if (startZ < marge) startZ = marge;
|
|
startZ = Mathf.Round(startZ / pasDalle) * pasDalle;
|
|
|
|
List<Vector2> zones = new List<Vector2>();
|
|
float currentZ = startZ;
|
|
int rangeeNum = 1;
|
|
|
|
for (int paire = 0; paire < nbPairesMax; paire++)
|
|
{
|
|
if (rangeeNum <= nbRangeesExt)
|
|
{
|
|
rangeeNum++;
|
|
currentZ += profondeur;
|
|
}
|
|
|
|
zones.Add(new Vector2(currentZ, currentZ + alleeFroide));
|
|
currentZ += alleeFroide;
|
|
|
|
if (rangeeNum <= nbRangeesExt)
|
|
{
|
|
rangeeNum++;
|
|
currentZ += profondeur;
|
|
}
|
|
|
|
if (paire < nbPairesMax - 1)
|
|
currentZ += alleeChaude;
|
|
}
|
|
|
|
return zones;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Génère les emplacements de baies dans l'extension.
|
|
/// Reprend la même logique que SalleDatacenter.GenererEmplacementsBaies()
|
|
/// mais adaptée aux dimensions de l'extension.
|
|
/// </summary>
|
|
void GenererEmplacementsExtension(Transform parent, float extLongueur, float extLargeur)
|
|
{
|
|
if (_salleRef == null) return;
|
|
|
|
float profondeur = _salleRef.profondeurEmplacement;
|
|
float alleeFroide = _salleRef.espaceAlleeFroide;
|
|
float alleeChaude = _salleRef.espaceAlleeChaude;
|
|
float largeurEmpl = _salleRef.largeurEmplacement;
|
|
float marge = _salleRef.margeDebutRangee;
|
|
float espBaies = _salleRef.espaceBaiesDansRangee;
|
|
float hauteurSol = _salleRef.hauteurSol;
|
|
float epaisseurDalle = _salleRef.epaisseurDalle;
|
|
float pasDalle = _salleRef.tailleDalle + _salleRef.espacementDalles;
|
|
|
|
GameObject parentEmpl = new GameObject("Ext_Emplacements");
|
|
parentEmpl.transform.SetParent(parent);
|
|
parentEmpl.transform.localPosition = Vector3.zero;
|
|
|
|
float largeurPaire = profondeur * 2 + alleeFroide;
|
|
|
|
// Pour Est/Ouest avec même largeur : réutiliser EXACTEMENT les mêmes paramètres
|
|
int nbPairesMax;
|
|
int nbRangeesExt;
|
|
float startZ;
|
|
|
|
bool memeAxeQueSalle = (direction == DirectionAgrandissement.Est || direction == DirectionAgrandissement.Ouest)
|
|
&& Mathf.Abs(extLargeur - _salleRef.largeur) < 0.1f;
|
|
|
|
if (memeAxeQueSalle)
|
|
{
|
|
// Même nombre de paires et même startZ que la salle
|
|
nbPairesMax = Mathf.CeilToInt(_salleRef.nbRangees / 2f);
|
|
nbRangeesExt = _salleRef.nbRangees;
|
|
|
|
float totalZSalle = nbPairesMax * largeurPaire + (nbPairesMax - 1) * alleeChaude;
|
|
bool rangeeImpaire = (_salleRef.nbRangees % 2 != 0);
|
|
if (rangeeImpaire) totalZSalle -= profondeur + alleeFroide;
|
|
startZ = (_salleRef.largeur - totalZSalle) / 2f;
|
|
if (startZ < marge) startZ = marge;
|
|
startZ = Mathf.Round(startZ / pasDalle) * pasDalle;
|
|
}
|
|
else
|
|
{
|
|
nbPairesMax = Mathf.FloorToInt((extLargeur - marge * 2 + alleeChaude) / (largeurPaire + alleeChaude));
|
|
if (nbPairesMax < 1) nbPairesMax = 1;
|
|
nbRangeesExt = nbPairesMax * 2;
|
|
|
|
float totalZ = nbPairesMax * largeurPaire + (nbPairesMax - 1) * alleeChaude;
|
|
startZ = (extLargeur - totalZ) / 2f;
|
|
if (startZ < marge) startZ = marge;
|
|
startZ = Mathf.Round(startZ / pasDalle) * pasDalle;
|
|
}
|
|
|
|
// Combien d'emplacements en X
|
|
int emplParRangee = Mathf.FloorToInt((extLongueur - marge * 2 + espBaies) / (largeurEmpl + espBaies));
|
|
if (emplParRangee < 1) emplParRangee = 1;
|
|
|
|
float totalXExt = emplParRangee * (largeurEmpl + espBaies) - espBaies;
|
|
float startX = (extLongueur - totalXExt) / 2f;
|
|
startX = Mathf.Round(startX / pasDalle) * pasDalle;
|
|
|
|
float currentZ = startZ;
|
|
int emplacementNum = 1;
|
|
int rangeeNum = 1;
|
|
float yEmplacement = hauteurSol + epaisseurDalle / 2f + 0.01f;
|
|
|
|
for (int paire = 0; paire < nbPairesMax; paire++)
|
|
{
|
|
if (rangeeNum <= nbRangeesExt)
|
|
{
|
|
GenererRangeeExtension(parentEmpl, startX, currentZ, 0f,
|
|
rangeeNum, emplParRangee, largeurEmpl, profondeur, espBaies,
|
|
yEmplacement, ref emplacementNum);
|
|
rangeeNum++;
|
|
currentZ += profondeur;
|
|
}
|
|
|
|
currentZ += alleeFroide;
|
|
|
|
if (rangeeNum <= nbRangeesExt)
|
|
{
|
|
GenererRangeeExtension(parentEmpl, startX, currentZ, 180f,
|
|
rangeeNum, emplParRangee, largeurEmpl, profondeur, espBaies,
|
|
yEmplacement, ref emplacementNum);
|
|
rangeeNum++;
|
|
currentZ += profondeur;
|
|
}
|
|
|
|
if (paire < nbPairesMax - 1)
|
|
currentZ += alleeChaude;
|
|
}
|
|
|
|
Debug.Log($"Extension: {emplacementNum - 1} emplacements en {rangeeNum - 1} rangées ({nbPairesMax} paires)");
|
|
}
|
|
|
|
void GenererRangeeExtension(GameObject parent, float startX, float posZ, float orientation,
|
|
int rangeeNum, int emplParRangee, float largeurEmpl, float profondeur,
|
|
float espBaies, float yEmplacement, ref int emplacementNum)
|
|
{
|
|
for (int i = 0; i < emplParRangee; i++)
|
|
{
|
|
float posX = startX + i * (largeurEmpl + espBaies) + largeurEmpl / 2f;
|
|
|
|
GameObject emplacement = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
emplacement.name = "Ext_Empl_R" + rangeeNum + "_" + (i + 1);
|
|
emplacement.transform.SetParent(parent.transform);
|
|
emplacement.transform.localPosition = new Vector3(posX, yEmplacement, posZ + profondeur / 2f);
|
|
emplacement.transform.localScale = new Vector3(largeurEmpl, 0.01f, profondeur);
|
|
emplacement.transform.localRotation = Quaternion.identity;
|
|
|
|
// Layer Emplacement (comme dans SalleDatacenter)
|
|
int layerEmplacement = LayerMask.NameToLayer("Emplacement");
|
|
if (layerEmplacement >= 0) emplacement.layer = layerEmplacement;
|
|
|
|
EmplacementBaie empl = emplacement.AddComponent<EmplacementBaie>();
|
|
BoxCollider emplCol = emplacement.GetComponent<BoxCollider>();
|
|
if (emplCol != null) emplCol.isTrigger = true;
|
|
empl.numeroEmplacement = i + 1;
|
|
empl.rangee = rangeeNum;
|
|
empl.orientationFaceAvant = orientation;
|
|
empl.largeurEmplacement = largeurEmpl;
|
|
empl.profondeurEmplacement = profondeur;
|
|
|
|
// Label visuel : skip cote dedicated server (pas besoin du TextMesh)
|
|
if (!DedicatedServerMode.IsDedicatedServer)
|
|
{
|
|
GameObject numLabel = new GameObject("Ext_Num_" + emplacementNum);
|
|
numLabel.transform.SetParent(emplacement.transform);
|
|
numLabel.transform.localPosition = new Vector3(0, 0.01f, 0);
|
|
numLabel.transform.localRotation = Quaternion.Euler(90, 0, 0);
|
|
TextMesh tm = numLabel.AddComponent<TextMesh>();
|
|
tm.text = "E-R" + rangeeNum + "-" + (i + 1);
|
|
tm.fontSize = 24;
|
|
tm.characterSize = 0.04f;
|
|
tm.anchor = TextAnchor.MiddleCenter;
|
|
tm.alignment = TextAlignment.Center;
|
|
tm.color = new Color(0.3f, 0.6f, 0.9f, 0.5f);
|
|
}
|
|
|
|
emplacementNum++;
|
|
}
|
|
}
|
|
|
|
// ==================== HELPERS ====================
|
|
|
|
Vector3 GetDirectionVector()
|
|
{
|
|
switch (direction)
|
|
{
|
|
case DirectionAgrandissement.Nord: return Vector3.forward;
|
|
case DirectionAgrandissement.Sud: return Vector3.back;
|
|
case DirectionAgrandissement.Est: return Vector3.right;
|
|
case DirectionAgrandissement.Ouest: return Vector3.left;
|
|
default: return Vector3.forward;
|
|
}
|
|
}
|
|
|
|
Material CreerMat(Color c, float metallic = 0f, float gloss = 0.2f)
|
|
{
|
|
if (DedicatedServerMode.IsDedicatedServer) return null;
|
|
Material m = new Material(Shader.Find("Standard"));
|
|
m.color = c;
|
|
m.SetFloat("_Metallic", metallic);
|
|
m.SetFloat("_Glossiness", gloss);
|
|
return m;
|
|
}
|
|
|
|
void CreerTexte(Transform parent, string texte, Vector3 pos, int fontSize, float charSize, Color couleur)
|
|
{
|
|
if (DedicatedServerMode.IsDedicatedServer) return;
|
|
|
|
GameObject o = new GameObject("Txt_" + texte);
|
|
o.transform.SetParent(parent);
|
|
o.transform.localPosition = pos;
|
|
o.transform.localRotation = Quaternion.identity;
|
|
TextMesh tm = o.AddComponent<TextMesh>();
|
|
tm.text = texte;
|
|
tm.fontSize = fontSize;
|
|
tm.characterSize = charSize;
|
|
tm.anchor = TextAnchor.MiddleCenter;
|
|
tm.alignment = TextAlignment.Center;
|
|
tm.color = couleur;
|
|
tm.fontStyle = FontStyle.Bold;
|
|
|
|
// Rendre le texte visible des deux côtés
|
|
MeshRenderer mr = o.GetComponent<MeshRenderer>();
|
|
if (mr != null && mr.sharedMaterial != null)
|
|
{
|
|
mr.sharedMaterial.shader = Shader.Find("GUI/Text Shader");
|
|
}
|
|
}
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
[CustomEditor(typeof(AgrandissementSalle))]
|
|
public class AgrandissementSalleEditor : Editor
|
|
{
|
|
public override void OnInspectorGUI()
|
|
{
|
|
DrawDefaultInspector();
|
|
AgrandissementSalle agr = (AgrandissementSalle)target;
|
|
|
|
GUILayout.Space(10);
|
|
|
|
SalleDatacenter salle = FindObjectOfType<SalleDatacenter>();
|
|
if (salle != null)
|
|
{
|
|
float tailleDalle = salle.tailleDalle;
|
|
float pas = tailleDalle + salle.espacementDalles;
|
|
Vector3 salleOrigine = salle.transform.position;
|
|
float sL = salle.longueur;
|
|
float sW = salle.largeur;
|
|
|
|
float zoneL, zoneW;
|
|
if (agr.direction == AgrandissementSalle.DirectionAgrandissement.Nord ||
|
|
agr.direction == AgrandissementSalle.DirectionAgrandissement.Sud)
|
|
{ zoneL = agr.nouvelleLargeur; zoneW = agr.nouvelleProfondeur; }
|
|
else
|
|
{ zoneL = agr.nouvelleProfondeur; zoneW = agr.nouvelleLargeur; }
|
|
|
|
Vector3 origineExt = agr.CalculerOrigineNouvelleZoneEditor(salleOrigine, sL, sW);
|
|
float deltaX = origineExt.x - salleOrigine.x;
|
|
float deltaZ = origineExt.z - salleOrigine.z;
|
|
|
|
EditorGUILayout.HelpBox(
|
|
$"Salle: {sL}x{sW}m @ ({salleOrigine.x:F2}, {salleOrigine.z:F2})\n" +
|
|
$"Extension: {zoneL}x{zoneW}m @ ({origineExt.x:F2}, {origineExt.z:F2})\n" +
|
|
$"Delta: ({deltaX:F3}, {deltaZ:F3})\n" +
|
|
$"Pas sol: {pas:F4}m | Pas plafond: {(salle.tailleDallePlafond + salle.espacementDalles):F4}m",
|
|
MessageType.Info);
|
|
}
|
|
|
|
GUILayout.Space(5);
|
|
|
|
GUI.backgroundColor = new Color(0.8f, 0.4f, 0.1f);
|
|
if (GUILayout.Button("Créer le panneau sur ce mur", GUILayout.Height(35)))
|
|
{
|
|
agr.CreerPanneau();
|
|
EditorUtility.SetDirty(agr);
|
|
}
|
|
|
|
GUI.backgroundColor = new Color(0.2f, 0.7f, 0.3f);
|
|
if (GUILayout.Button("Reset offset grille", GUILayout.Height(25)))
|
|
{
|
|
Undo.RecordObject(agr, "Reset offset grille");
|
|
agr.offsetGrille = Vector2.zero;
|
|
EditorUtility.SetDirty(agr);
|
|
}
|
|
|
|
GUI.backgroundColor = Color.white;
|
|
|
|
if (agr.estAgrandi)
|
|
EditorGUILayout.HelpBox("Déjà agrandie.", MessageType.Info);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Helper simple pour exécuter des coroutines sur un GameObject stable
|
|
public class CoroutineRunner : MonoBehaviour { }
|