1424 lines
69 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using Mirror;
/// <summary>
/// PlayerInteraction.cs - v6.13 Multijoueur
///
/// v6.13 : Fix des syncs manquantes en multijoueur
/// BUG 1 : La pile de ZoneLivraison ne se reorganise pas cote autres clients
/// quand un joueur ramasse un article (le client qui ramasse reorganise
/// en local, les autres ne voient rien bouger).
/// Fix : CmdReorganiserZoneLivraison envoie la reorganisation au serveur,
/// qui modifie les positions des articles -> Mirror sync via NetworkTransform.
///
/// BUG 2 : Les equipements poses sur le chariot "volent" quand l'autre
/// joueur pilote. Mirror ne sync pas le SetParent, donc seul le client qui
/// a pose voit les equipements parentes au chariot. Les autres les voient
/// a leur position monde figee au moment de la pose.
/// Fix : CmdParenterAuChariot + RpcParenterAuChariot pour broadcaster le
/// SetParent a tous les clients (chacun fait SetParent local). Idem pour
/// le detachement au ramassage (CmdDetacherDuChariot + RpcDetacherDuChariot).
///
/// v6.12 : Transfert d'autorite pour le pilotage du chariot en multijoueur
/// v6.11 : Pose/retrait des torons sur les axes lateraux du chariot
/// v6.10 : Fix critique - objets qui flottent apres avoir ete poses sur le chariot
/// v6.9 : Integration du systeme de pose plateau / etagere basse du Chariot v7.x
/// - Detection des zones de pose via RaycastAll + tri par distance
/// - Distinction automatique zonePosePlateau vs zonePoseEtagere
/// - Affichage du fantome a la bonne position via AfficherFantomePose(tailleU, surEtagereBasse)
/// - Cachage des fantomes quand on quitte le chariot du regard ou qu'on pose
/// - Pose sur etagere basse via PoserEquipementSurEtagere
/// - Ramassage depuis etagere basse (via equipementsSurEtagere.Contains)
///
/// v6.8 : Systeme d'assise sur les chaises
/// v6.7 : Placement local client pour supports muraux
/// v6.5 : Remise de PDU sur SupportPDU
/// v6.4 : Portage toron "dans les mains"
/// v6.3 : Placement precis toron sur axe cible
/// v6.2 : Remise de toron sur SupportTorons
/// </summary>
public class PlayerInteraction : NetworkBehaviour
{
[Header("Interaction")]
public float porteeInteraction = 4f;
public KeyCode toucheInteraction = KeyCode.E;
public KeyCode toucheConfiguration = KeyCode.F;
[Header("Performance")]
[Range(1, 5)]
public int detectInterval = 2;
public bool detecterTriggers = false;
[Header("Transport")]
public float distanceTransport = 1.5f;
public float hauteurTransport = 0.6f;
public float decalageHorizontal = 0f;
public float vitesseSuivi = 15f;
[Header("Transport — Toron (v6.4)")]
public float distanceTransportToron = 0.85f;
public float hauteurTransportToron = 0.15f;
public float decalageHorizontalToron = 0.35f;
public Vector3 rotationEulerToron = new Vector3(0f, 90f, 0f);
[Header("Remise sur supports muraux (v6.2/6.5)")]
public float distanceDetectionSupport = 3.5f;
public float angleDetectionSupport = 35f;
private Camera _camera;
private Interactable _objetSurvole;
private Interactable _objetEnMain;
private Rigidbody _rbEnMain;
private Vector3 _cibleTransport;
private Quaternion _rotationOffsetTransport = Quaternion.identity;
private RackSlot _slotSurvole;
private Chariot _chariotPilote;
private Chariot _chariotVise;
private bool _visePoignee = false;
// v6.9 : distinction zone plateau/etagere du chariot vise
private bool _chariotZoneEtagereVisee = false;
// v6.11 : index de l'axe toron cible sur le chariot (0=Gauche, 1=Droit, -1=aucun)
private int _chariotAxeToronCibleIndex = -1;
private EmplacementBaie _emplacementVise;
private EmplacementPDU _emplacementPDUVise;
private PorteBaie _porteVisee;
private PriseC13 _priseSurvolee;
private BoutonPower _boutonPowerVise;
// v6.8 : système d'assise
private ChaiseSiege _chaiseSiegeVisee;
private ChaiseSiege _chaiseSiegeActuelle;
private List<Collider> _collisionsIgnorees = new List<Collider>();
private EmplacementBaie[] _tousEmplacements;
private bool _emplacementsVisibles = false;
private bool _emplacementsPDUVisibles = false;
private int _detectFrame = 0;
private MenuPause _cachedPause;
private MenuPrincipal _cachedMenu;
private bool _cacheInitialized = false;
// v6.2 : axe cible courant sur SupportTorons
private int _axeSupportCibleLigne = -1;
private int _axeSupportCibleColonne = -1;
// v6.5 : emplacement cible courant sur SupportPDU
private int _emplacementSupportPDUCibleLigne = -1;
private int _emplacementSupportPDUCibleColonne = -1;
[SyncVar(hook = nameof(OnObjetPorteChange))]
private uint _objetPorteNetId = 0;
private GameObject _objetPorteDistant;
public Interactable ObjetEnMain => _objetEnMain;
void Start()
{
_camera = GetComponentInChildren<Camera>();
if (_camera == null) _camera = Camera.main;
}
private bool _popupEtaitOuvert = false;
void Update()
{
if (!isLocalPlayer) return;
// v6.8 : mode assis — seul [E] pour se lever est actif
if (_chaiseSiegeActuelle != null)
{
if (Input.GetKeyDown(toucheInteraction))
{
_chaiseSiegeActuelle.SeLever();
_chaiseSiegeActuelle = null;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("");
}
return;
}
if (_camera == null) _camera = Camera.main;
if (UIConfigurationEquipement.Instance != null && UIConfigurationEquipement.Instance.EstOuvert())
{
_popupEtaitOuvert = true;
return;
}
if (_popupEtaitOuvert)
{
_popupEtaitOuvert = false;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
var playerInput = GetComponent<UnityEngine.InputSystem.PlayerInput>();
if (playerInput != null) playerInput.enabled = true;
var fpc = GetComponent<StarterAssets.FirstPersonController>();
if (fpc != null) fpc.enabled = true;
}
if (Cursor.visible && _objetEnMain == null)
{
if (!_cacheInitialized || _cachedPause == null) { _cachedPause = FindObjectOfType<MenuPause>(); _cachedMenu = FindObjectOfType<MenuPrincipal>(); if (_cachedPause != null) _cacheInitialized = true; }
bool pauseOuverte = (_cachedPause != null && _cachedPause.estEnPause);
bool menuOuvert = (_cachedMenu != null && _cachedMenu.EstAffiche());
MonitoringDatacenter monitoring = FindObjectOfType<MonitoringDatacenter>();
bool monitoringOuvert = (monitoring != null && monitoring.EstAffiche());
PlanSalle plan = FindObjectOfType<PlanSalle>();
bool planOuvert = (plan != null && plan.EstOuvert());
UIBoutique boutique = FindObjectOfType<UIBoutique>();
bool boutiqueOuverte = (boutique != null && boutique.panneauPrincipal != null && boutique.panneauPrincipal.activeSelf);
UIConfigurationEquipement configPopup = UIConfigurationEquipement.Instance;
bool configOuverte = (configPopup != null && configPopup.EstOuvert());
UITickets tickets = UITickets.Instance;
bool ticketsOuverts = (tickets != null && tickets.EstAffiche());
if (!pauseOuverte && !menuOuvert && !monitoringOuvert && !planOuvert && !boutiqueOuverte && !configOuverte && !ticketsOuverts)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
var fpc2 = GetComponent<StarterAssets.FirstPersonController>();
if (fpc2 != null) fpc2.enabled = true;
var pi2 = GetComponent<UnityEngine.InputSystem.PlayerInput>();
if (pi2 != null) pi2.enabled = true;
}
}
if (_objetEnMain == null)
{
if (Time.frameCount % detectInterval == 0)
DetecterObjet();
if (_emplacementsVisibles) { SetEmplacementsVisibles(false); _emplacementsVisibles = false; }
if (_emplacementsPDUVisibles) { SetEmplacementsPDUVisibles(false); _emplacementsPDUVisibles = false; }
if (_axeSupportCibleLigne >= 0) QuitterSurvolSupport();
if (_emplacementSupportPDUCibleLigne >= 0) QuitterSurvolSupportPDU();
// v6.9 : cacher les fantomes du chariot quand on n'a plus rien en main
CacherFantomesChariotTous();
}
else
{
DetecterCiblePose();
bool portePDU = _objetEnMain.GetComponent<PDU>() != null;
if (portePDU && !_emplacementsPDUVisibles) { SetEmplacementsPDUVisibles(true); _emplacementsPDUVisibles = true; }
if (!portePDU && _emplacementsPDUVisibles) { SetEmplacementsPDUVisibles(false); _emplacementsPDUVisibles = false; }
if (_emplacementsVisibles) { SetEmplacementsVisibles(false); _emplacementsVisibles = false; }
}
if (Input.GetKeyDown(toucheInteraction))
{
// v6.8 : s'asseoir si on vise une chaise et pas d'objet en main
if (_chaiseSiegeVisee != null && _objetEnMain == null)
{
if (_chaiseSiegeVisee.Asseoir(gameObject))
{
_chaiseSiegeActuelle = _chaiseSiegeVisee;
_chaiseSiegeVisee = null;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("[E] Se lever");
}
return;
}
if (_boutonPowerVise != null && _objetEnMain == null) { CmdAppuyerBoutonPower(_boutonPowerVise.GetComponentInParent<NetworkIdentity>().netId); return; }
if (_priseSurvolee != null && _priseSurvolee.estConnectee && _objetEnMain == null) { CmdBasculerInterrupteur(_priseSurvolee.GetComponentInParent<NetworkIdentity>().netId); return; }
if (_porteVisee != null && _objetEnMain == null)
{
NetworkIdentity porteNetId = _porteVisee.GetComponentInParent<NetworkIdentity>();
if (porteNetId != null) CmdTogglePorte(porteNetId.netId, _porteVisee.gameObject.name);
return;
}
if (_chariotPilote != null && _objetEnMain == null && _visePoignee)
{
LacherChariot();
QuitterSurvol();
return;
}
if (_objetEnMain == null && _objetSurvole != null) Ramasser();
else if (_objetEnMain != null) Poser();
}
if (Input.GetKeyDown(toucheConfiguration) && _objetEnMain == null && _objetSurvole != null)
{
RackSlot slotConfig = TrouverSlotDeEquipement(_objetSurvole);
if (slotConfig != null)
{
ConfigurationEquipement config = _objetSurvole.GetComponent<ConfigurationEquipement>();
if (config == null) config = _objetSurvole.gameObject.AddComponent<ConfigurationEquipement>();
if (UIConfigurationEquipement.Instance != null)
{ UIConfigurationEquipement.Instance.Ouvrir(config, premiereOuverture: false); QuitterSurvol(); }
}
}
if (Input.GetKeyDown(KeyCode.F5) && isServer && SaveManager.Instance != null)
{
if (SaveManager.Instance.SauvegardeRapide())
Debug.Log("[QuickSave] Sauvegarde rapide effectuée !");
}
}
void FixedUpdate()
{
if (!isLocalPlayer) return;
if (_objetEnMain != null && _rbEnMain != null)
{
Vector3 dir = new Vector3(_camera.transform.forward.x, 0, _camera.transform.forward.z).normalized;
bool estToron = _objetEnMain.GetComponent<ToronProcedural>() != null;
float dist = estToron ? distanceTransportToron : distanceTransport;
float hauteur = estToron ? hauteurTransportToron : hauteurTransport;
float decalage = estToron ? decalageHorizontalToron : decalageHorizontal;
_cibleTransport = transform.position + dir * dist
+ Vector3.up * hauteur + _camera.transform.right * decalage;
Vector3 direction = _cibleTransport - _rbEnMain.position;
Vector3 desiredVelocity = direction * vitesseSuivi;
if (direction.magnitude > 0.05f)
{
RaycastHit hit;
int excludeMask = ~LayerMask.GetMask("Joueur", "Equipement", "Sol", "Baie", "Default", "Emplacement");
if (Physics.SphereCast(_rbEnMain.position, 0.15f, direction.normalized, out hit, direction.magnitude, excludeMask))
if (!EstColliderIgnore(hit.collider) && hit.distance < 0.15f)
desiredVelocity = Vector3.ProjectOnPlane(desiredVelocity, hit.normal);
}
_rbEnMain.velocity = desiredVelocity;
if (estToron)
_rbEnMain.rotation = Quaternion.LookRotation(dir) * Quaternion.Euler(rotationEulerToron);
else
_rbEnMain.rotation = Quaternion.LookRotation(dir) * _rotationOffsetTransport;
CmdUpdateTransportPosition(_rbEnMain.position, _rbEnMain.rotation);
}
}
// ══════════════════════════════════════════════════════════
// COMMANDS
// ══════════════════════════════════════════════════════════
[Command]
void CmdRamasser(uint objetNetId)
{
if (!NetworkServer.spawned.ContainsKey(objetNetId)) return;
_objetPorteNetId = objetNetId;
var obj = NetworkServer.spawned[objetNetId];
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null) { rb.isKinematic = false; rb.useGravity = false; rb.freezeRotation = true; }
RpcOnRamasser(objetNetId);
}
[Command]
void CmdPoser(uint objetNetId, Vector3 position, Quaternion rotation, bool avecGravite, bool estRacke)
{
if (!NetworkServer.spawned.ContainsKey(objetNetId)) return;
_objetPorteNetId = 0;
var obj = NetworkServer.spawned[objetNetId];
obj.transform.position = position;
obj.transform.rotation = rotation;
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null)
{
if (estRacke) { if (!rb.isKinematic) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } rb.isKinematic = true; }
else { rb.isKinematic = false; rb.useGravity = avecGravite; rb.velocity = Vector3.zero; rb.freezeRotation = false; rb.interpolation = RigidbodyInterpolation.None; }
}
RpcOnPoser(objetNetId, position, rotation, avecGravite, estRacke);
}
[Command]
void CmdPoserToronSurSupport(uint toronNetId, int ligne, int colonne)
{
if (!NetworkServer.spawned.ContainsKey(toronNetId)) return;
_objetPorteNetId = 0;
var obj = NetworkServer.spawned[toronNetId];
if (obj == null) return;
bool place = false;
if (SupportTorons.Instance != null)
{
if (ligne >= 0 && colonne >= 0) place = SupportTorons.Instance.PoserToronSurAxeSpecifique(obj.gameObject, ligne, colonne);
if (!place) place = SupportTorons.Instance.PoserToron(obj.gameObject);
}
if (place) { RpcOnPoser(toronNetId, obj.transform.position, obj.transform.rotation, false, true); }
else { Rigidbody rb = obj.GetComponent<Rigidbody>(); if (rb != null) { rb.isKinematic = false; rb.useGravity = true; rb.velocity = Vector3.zero; } RpcOnPoser(toronNetId, obj.transform.position, obj.transform.rotation, true, false); }
}
[Command]
void CmdPoserPDUSurSupport(uint pduNetId, int ligne, int colonne)
{
if (!NetworkServer.spawned.ContainsKey(pduNetId)) return;
_objetPorteNetId = 0;
var obj = NetworkServer.spawned[pduNetId];
if (obj == null) return;
bool place = false;
if (SupportPDU.Instance != null)
{
if (ligne >= 0 && colonne >= 0) place = SupportPDU.Instance.PoserPDUSurEmplacementSpecifique(obj.gameObject, ligne, colonne);
if (!place) place = SupportPDU.Instance.PoserPDU(obj.gameObject);
}
if (place) { RpcOnPoser(pduNetId, obj.transform.position, obj.transform.rotation, false, true); }
else { Rigidbody rb = obj.GetComponent<Rigidbody>(); if (rb != null) { rb.isKinematic = false; rb.useGravity = true; rb.velocity = Vector3.zero; } RpcOnPoser(pduNetId, obj.transform.position, obj.transform.rotation, true, false); }
}
[Command(channel = Channels.Unreliable)]
void CmdUpdateTransportPosition(Vector3 position, Quaternion rotation) { RpcUpdateTransportPosition(position, rotation); }
[Command]
void CmdAppuyerBoutonPower(uint equipNetId)
{
if (!NetworkServer.spawned.ContainsKey(equipNetId)) return;
var obj = NetworkServer.spawned[equipNetId];
BoutonPower bouton = obj.GetComponentInChildren<BoutonPower>();
if (bouton != null) { bouton.Appuyer(); RpcSyncBoutonPower(equipNetId, bouton.estAllume); }
}
[ClientRpc]
void RpcSyncBoutonPower(uint equipNetId, bool estAllume)
{
if (!NetworkClient.spawned.ContainsKey(equipNetId)) return;
var obj = NetworkClient.spawned[equipNetId];
BoutonPower bouton = obj.GetComponentInChildren<BoutonPower>();
if (bouton != null && bouton.estAllume != estAllume) bouton.Appuyer();
}
[Command]
void CmdBasculerInterrupteur(uint pduNetId)
{
if (!NetworkServer.spawned.ContainsKey(pduNetId)) return;
var obj = NetworkServer.spawned[pduNetId];
PriseC13 prise = obj.GetComponentInChildren<PriseC13>();
if (prise != null) prise.BasculerInterrupteur();
}
[Command]
void CmdInstallerPDU(uint pduNetId, Vector3 position, Quaternion rotation)
{
if (!NetworkServer.spawned.ContainsKey(pduNetId)) return;
var obj = NetworkServer.spawned[pduNetId];
PDU pdu = obj.GetComponent<PDU>();
if (pdu == null) return;
PDUNetworkState netState = obj.GetComponent<PDUNetworkState>();
if (netState != null) netState.ServerSetInstalle(true, (int)pdu.coteInstallation);
else pdu.estInstalle = true;
RpcInstallerPDU(pduNetId, position, rotation);
}
[ClientRpc]
void RpcInstallerPDU(uint pduNetId, Vector3 position, Quaternion rotation)
{
if (!NetworkClient.spawned.ContainsKey(pduNetId)) return;
var obj = NetworkClient.spawned[pduNetId];
PDU pdu = obj.GetComponent<PDU>();
if (pdu == null) return;
pdu.estInstalle = true;
if (!isLocalPlayer) { obj.transform.position = position; obj.transform.rotation = rotation; Rigidbody rb = obj.GetComponent<Rigidbody>(); if (rb != null) { rb.isKinematic = true; rb.useGravity = false; } }
}
[Command]
void CmdDesinstallerPDU(uint pduNetId)
{
if (!NetworkServer.spawned.ContainsKey(pduNetId)) return;
var obj = NetworkServer.spawned[pduNetId];
PDU pdu = obj.GetComponent<PDU>();
if (pdu == null) return;
PDUNetworkState netState = obj.GetComponent<PDUNetworkState>();
if (netState != null) netState.ServerSetInstalle(false);
else pdu.estInstalle = false;
RpcDesinstallerPDU(pduNetId);
}
[ClientRpc]
void RpcDesinstallerPDU(uint pduNetId)
{
if (!NetworkClient.spawned.ContainsKey(pduNetId)) return;
var obj = NetworkClient.spawned[pduNetId];
PDU pdu = obj.GetComponent<PDU>();
if (pdu != null) { pdu.estInstalle = false; Rigidbody rb = obj.GetComponent<Rigidbody>(); if (rb != null) { rb.isKinematic = false; rb.useGravity = true; } }
}
[Command]
void CmdInstallerDansSlot(uint equipNetId, uint baieNetId, int slotIndex)
{
if (!NetworkServer.spawned.ContainsKey(equipNetId) || !NetworkServer.spawned.ContainsKey(baieNetId)) return;
var equipObj = NetworkServer.spawned[equipNetId]; var baieObj = NetworkServer.spawned[baieNetId];
RackSlot[] slots = baieObj.GetComponentsInChildren<RackSlot>();
if (slotIndex >= 0 && slotIndex < slots.Length) { RackSlot slot = slots[slotIndex]; if (!slot.estOccupe) { slot.estOccupe = true; slot.equipementInstalle = equipObj.GetComponent<Interactable>(); } }
RpcInstallerDansSlot(equipNetId, baieNetId, slotIndex);
}
[ClientRpc]
void RpcInstallerDansSlot(uint equipNetId, uint baieNetId, int slotIndex)
{
if (isLocalPlayer) return;
if (!NetworkClient.spawned.ContainsKey(equipNetId) || !NetworkClient.spawned.ContainsKey(baieNetId)) return;
var equipObj = NetworkClient.spawned[equipNetId]; var baieObj = NetworkClient.spawned[baieNetId];
RackSlot[] slots = baieObj.GetComponentsInChildren<RackSlot>();
if (slotIndex >= 0 && slotIndex < slots.Length) { RackSlot slot = slots[slotIndex]; if (!slot.estOccupe) { slot.estOccupe = true; slot.equipementInstalle = equipObj.GetComponent<Interactable>(); } }
}
[Command]
void CmdTogglePorte(uint baieNetId, string nomPorte)
{
if (!NetworkServer.spawned.ContainsKey(baieNetId)) return;
var obj = NetworkServer.spawned[baieNetId];
foreach (PorteBaie porte in obj.GetComponentsInChildren<PorteBaie>())
if (porte.gameObject.name == nomPorte) { porte.TogglePorte(); RpcTogglePorte(baieNetId, nomPorte); break; }
}
// ══════════════════════════════════════════════════════════
// CLIENT RPCs
// ══════════════════════════════════════════════════════════
[ClientRpc]
void RpcOnRamasser(uint objetNetId)
{
if (isLocalPlayer) return;
if (!NetworkClient.spawned.ContainsKey(objetNetId)) return;
var obj = NetworkClient.spawned[objetNetId];
_objetPorteDistant = obj.gameObject;
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null) { rb.isKinematic = true; rb.velocity = Vector3.zero; }
Interactable inter = obj.GetComponent<Interactable>();
if (inter != null) inter.estRamasse = true;
}
[ClientRpc]
void RpcOnPoser(uint objetNetId, Vector3 position, Quaternion rotation, bool avecGravite, bool estRacke)
{
if (isLocalPlayer) return;
if (!NetworkClient.spawned.ContainsKey(objetNetId)) return;
var obj = NetworkClient.spawned[objetNetId];
obj.transform.position = position; obj.transform.rotation = rotation;
Rigidbody rb = obj.GetComponent<Rigidbody>();
if (rb != null)
{
if (estRacke) { if (!rb.isKinematic) { rb.velocity = Vector3.zero; rb.angularVelocity = Vector3.zero; } rb.isKinematic = true; }
else { rb.isKinematic = false; rb.useGravity = avecGravite; rb.velocity = Vector3.zero; }
}
Interactable inter = obj.GetComponent<Interactable>();
if (inter != null) inter.estRamasse = false;
_objetPorteDistant = null;
}
[ClientRpc(channel = Channels.Unreliable)]
void RpcUpdateTransportPosition(Vector3 position, Quaternion rotation)
{
if (isLocalPlayer) return;
if (_objetPorteDistant != null)
{
_objetPorteDistant.transform.position = Vector3.Lerp(_objetPorteDistant.transform.position, position, Time.deltaTime * 20f);
_objetPorteDistant.transform.rotation = Quaternion.Slerp(_objetPorteDistant.transform.rotation, rotation, Time.deltaTime * 20f);
}
}
[ClientRpc]
void RpcTogglePorte(uint baieNetId, string nomPorte)
{
if (isServer) return;
if (!NetworkClient.spawned.ContainsKey(baieNetId)) return;
var obj = NetworkClient.spawned[baieNetId];
foreach (PorteBaie porte in obj.GetComponentsInChildren<PorteBaie>())
if (porte.gameObject.name == nomPorte) { porte.TogglePorte(); break; }
}
// ══════════════════════════════════════════════════════════
// HOOK SyncVar
// ══════════════════════════════════════════════════════════
void OnObjetPorteChange(uint ancienId, uint nouveauId)
{
if (isLocalPlayer) return;
if (nouveauId == 0) _objetPorteDistant = null;
else if (NetworkClient.spawned.ContainsKey(nouveauId)) _objetPorteDistant = NetworkClient.spawned[nouveauId].gameObject;
}
// ══════════════════════════════════════════════════════════
// HELPERS LOCAUX
// ══════════════════════════════════════════════════════════
private bool EstColliderIgnore(Collider col) { for (int i = 0; i < _collisionsIgnorees.Count; i++) if (_collisionsIgnorees[i] == col) return true; return false; }
void SetEmplacementsVisibles(bool visible)
{
if (_tousEmplacements == null || _tousEmplacements.Length == 0) _tousEmplacements = FindObjectsOfType<EmplacementBaie>();
foreach (EmplacementBaie empl in _tousEmplacements) if (empl != null && !empl.estOccupe) empl.SetModeVisible(visible);
}
void SetEmplacementsPDUVisibles(bool visible)
{
foreach (EmplacementPDU empl in FindObjectsOfType<EmplacementPDU>()) if (empl != null && !empl.estOccupe) empl.SetModeVisible(visible);
}
// v6.9 : helper pour cacher les fantomes du chariot
void CacherFantomesChariotTous()
{
if (_chariotVise != null) { _chariotVise.CacherFantomes(); }
if (_chariotPilote != null) { _chariotPilote.CacherFantomes(); }
// v6.11 : CacherFantomes() inclut deja le fantome toron axe
// (cf. Chariot.CacherFantomes v7.3) mais on le reappelle explicitement ici
// au cas ou la version du Chariot n'aurait pas encore ete mise a jour
if (_chariotVise != null) _chariotVise.CacherFantomeToronAxe();
if (_chariotPilote != null) _chariotPilote.CacherFantomeToronAxe();
}
// ==================== DETECTION OBJET ====================
void DetecterObjet()
{
QuitterSurvolSlot(); QuitterSurvolEmplacement(); QuitterSurvolEmplacementPDU();
QuitterSurvolPrise(); QuitterSurvolBoutonPower();
_chariotVise = null; _visePoignee = false; _porteVisee = null;
if (_camera == null) return;
Ray rayon = new Ray(_camera.transform.position, _camera.transform.forward);
QueryTriggerInteraction triggerMode = detecterTriggers ? QueryTriggerInteraction.Collide : QueryTriggerInteraction.Ignore;
int maskObjets = ~LayerMask.GetMask("Joueur", "Baie");
RaycastHit hitSimple;
if (Physics.Raycast(rayon, out hitSimple, porteeInteraction, maskObjets, triggerMode))
{
if (!EstColliderIgnore(hitSimple.collider))
if (TraiterHitDetection(hitSimple)) return;
}
int maskBaie = LayerMask.GetMask("Baie");
if (Physics.Raycast(rayon, out hitSimple, porteeInteraction, maskBaie, triggerMode))
{
if (!EstColliderIgnore(hitSimple.collider))
if (TraiterHitBaie(hitSimple)) return;
}
QuitterSurvol();
}
bool TraiterHitDetection(RaycastHit impact)
{
// v6.8 : chaise pour s'asseoir
ChaiseSiege chaise = impact.collider.GetComponentInParent<ChaiseSiege>();
if (chaise != null && !chaise.estOccupee && _objetEnMain == null)
{
_chaiseSiegeVisee = chaise;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("[E] S'asseoir");
QuitterSurvol();
return true;
}
if (_chaiseSiegeVisee != null && chaise == null)
_chaiseSiegeVisee = null;
BoutonPower bouton = impact.collider.GetComponent<BoutonPower>();
if (bouton != null) { _boutonPowerVise = bouton; bouton.Survoler(true); return true; }
PriseC13 prise = impact.collider.GetComponent<PriseC13>();
if (prise != null) { _priseSurvolee = prise; prise.Survoler(true); return true; }
PorteBaie porte = impact.collider.GetComponentInParent<PorteBaie>();
if (porte != null) { _porteVisee = porte; QuitterSurvol(); return true; }
Chariot chariotHit = impact.collider.GetComponentInParent<Chariot>();
if (chariotHit != null && impact.collider.gameObject.name == "Ch_PoigneeZone")
{
_visePoignee = true;
Interactable ci = chariotHit.GetComponent<Interactable>();
if (ci != null) SetSurvol(ci);
return true;
}
MonitoringDatacenter monitoring = impact.collider.GetComponent<MonitoringDatacenter>();
if (monitoring != null) { Interactable interMon = impact.collider.GetComponent<Interactable>(); if (interMon != null) SetSurvol(interMon); return true; }
PDU pduVise = impact.collider.GetComponentInParent<PDU>();
if (pduVise != null && pduVise.estInstalle) { Interactable interPDU = pduVise.GetComponent<Interactable>(); if (interPDU != null) { SetSurvol(interPDU); return true; } }
Interactable obj = impact.collider.GetComponentInParent<Interactable>();
if (obj != null)
{
if (obj.GetComponent<Chariot>() != null && !_visePoignee) { QuitterSurvol(); return true; }
SetSurvol(obj); return true;
}
return false;
}
bool TraiterHitBaie(RaycastHit impact)
{
if (impact.collider.gameObject.name == "BaieZoneTrigger") return false;
PorteBaie porte = impact.collider.GetComponent<PorteBaie>();
if (porte != null) { _porteVisee = porte; QuitterSurvol(); return true; }
RackSlot slot = impact.collider.GetComponent<RackSlot>();
if (slot != null && slot.estOccupe && slot.equipementInstalle != null)
{
if (!PorteAvantOuverte(slot)) return false;
BoutonPower boutonRack = slot.equipementInstalle.GetComponentInChildren<BoutonPower>();
if (boutonRack != null && ViseBouton(boutonRack.transform.position)) { _boutonPowerVise = boutonRack; boutonRack.Survoler(true); return true; }
SetSurvol(slot.equipementInstalle); return true;
}
EmplacementPDU emplPDU = impact.collider.GetComponent<EmplacementPDU>();
if (emplPDU != null) return false;
return false;
}
// ==================== DETECTION CIBLE POSE ====================
void DetecterCiblePose()
{
_porteVisee = null;
QuitterSurvolSlot(); QuitterSurvolEmplacement(); QuitterSurvolEmplacementPDU();
if (_camera == null) return;
if (DetecterCibleSupportTorons()) return;
QuitterSurvolSupport();
if (DetecterCibleSupportPDU()) return;
QuitterSurvolSupportPDU();
Ray rayon = new Ray(_camera.transform.position, _camera.transform.forward);
bool portePDU = _objetEnMain != null && _objetEnMain.GetComponent<PDU>() != null;
List<Collider> colsPortes = new List<Collider>();
if (_objetEnMain != null)
foreach (Collider c in _objetEnMain.GetComponentsInChildren<Collider>())
if (c.enabled) { c.enabled = false; colsPortes.Add(c); }
int slotMask = LayerMask.GetMask("Baie", "Sol", "Emplacement");
RaycastHit[] hits = Physics.RaycastAll(rayon, porteeInteraction, slotMask, QueryTriggerInteraction.Collide);
System.Array.Sort(hits, (a, b) => a.distance.CompareTo(b.distance));
foreach (Collider c in colsPortes) c.enabled = true;
RackSlot slotTouche = null; EmplacementBaie emplTouche = null; EmplacementPDU emplPDUTouche = null;
foreach (var hit in hits)
{
if (hit.collider.gameObject.name == "BaieZoneTrigger") continue;
if (portePDU) { EmplacementPDU ep = hit.collider.GetComponent<EmplacementPDU>(); if (ep != null && !ep.estOccupe) { emplPDUTouche = ep; break; } continue; }
RackSlot s = hit.collider.GetComponent<RackSlot>(); if (s != null) { slotTouche = s; break; }
EmplacementBaie e = hit.collider.GetComponent<EmplacementBaie>(); if (e != null) { emplTouche = e; break; }
}
if (emplPDUTouche != null)
{
if (_emplacementPDUVise != emplPDUTouche) { QuitterSurvolEmplacementPDU(); _emplacementPDUVise = emplPDUTouche; _emplacementPDUVise.SurvolDebut(); }
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("[E] Installer PDU (" + (_emplacementPDUVise.cote == EmplacementPDU.CotePDU.Gauche ? "Gauche" : "Droit") + ")");
// v6.9 : on a trouve une cible non-chariot -> cacher les fantomes
CacherFantomesChariotTous();
_chariotVise = null; _chariotZoneEtagereVisee = false;
return;
}
if (slotTouche != null)
{
bool porteOuverte = PorteAvantOuverte(slotTouche);
if (_slotSurvole != slotTouche) { QuitterSurvolSlot(); _slotSurvole = slotTouche; if (!slotTouche.estOccupe && porteOuverte) _slotSurvole.SurvolDebut(); }
if (!slotTouche.estOccupe) { HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement(porteOuverte ? "U" + slotTouche.numeroSlot.ToString("D2") + " - [E] Installer ici" : "Ouvrir la porte avant d'abord"); }
CacherFantomesChariotTous();
_chariotVise = null; _chariotZoneEtagereVisee = false;
return;
}
if (emplTouche != null && !emplTouche.estOccupe)
{ if (_emplacementVise != emplTouche) { QuitterSurvolEmplacement(); _emplacementVise = emplTouche; _emplacementVise.SurvolDebut(); } CacherFantomesChariotTous(); _chariotVise = null; _chariotZoneEtagereVisee = false; return; }
HUDManager hudClear = GetComponent<HUDManager>(); if (hudClear != null && _slotSurvole == null) hudClear.SetInfoEquipement("");
// ───── v6.11 : Detection zones de pose du chariot (plateau, etagere, axes toron) ─────
Chariot ancienChariot = _chariotVise;
_chariotVise = null;
_chariotZoneEtagereVisee = false;
_chariotAxeToronCibleIndex = -1;
bool porteToron = (_objetEnMain != null && _objetEnMain.GetComponent<ToronProcedural>() != null);
int maskChariot = LayerMask.GetMask("Chariot");
RaycastHit[] hitsChariot;
if (maskChariot != 0)
hitsChariot = Physics.RaycastAll(rayon, porteeInteraction, maskChariot, QueryTriggerInteraction.Collide);
else
hitsChariot = Physics.RaycastAll(rayon, porteeInteraction, ~LayerMask.GetMask("Joueur", "Equipement", "Sol", "Baie", "Emplacement"), QueryTriggerInteraction.Collide);
System.Array.Sort(hitsChariot, (a, b) => a.distance.CompareTo(b.distance));
Chariot chariotHit = null;
int axeToronIdx = -1;
bool zoneTrouvee = false;
foreach (var h in hitsChariot)
{
Chariot c = h.collider.GetComponentInParent<Chariot>();
if (c == null) continue;
if (porteToron)
{
// Porte un toron : priorite aux axes toron, on ignore plateau/etagere
if (c.zonesToronAxes != null)
{
int iFound = -1;
for (int i = 0; i < c.zonesToronAxes.Length; i++)
{
if (c.zonesToronAxes[i] != null && h.collider.gameObject == c.zonesToronAxes[i])
{
// Verifier que l'axe est libre
bool libre = (c.equipementsToronAxes == null
|| i >= c.equipementsToronAxes.Count
|| c.equipementsToronAxes[i] == null);
if (libre) { iFound = i; break; }
}
}
if (iFound >= 0)
{
chariotHit = c;
axeToronIdx = iFound;
zoneTrouvee = true;
break;
}
}
}
else
{
// Equipement normal : zones plateau/etagere
bool estZonePlateau = (c.zonePosePlateau != null && h.collider.gameObject == c.zonePosePlateau);
bool estZoneEtagere = (c.zonePoseEtagere != null && h.collider.gameObject == c.zonePoseEtagere);
if (estZonePlateau || estZoneEtagere)
{
chariotHit = c;
_chariotZoneEtagereVisee = estZoneEtagere;
zoneTrouvee = true;
break;
}
}
// Fallback : garder le chariot comme candidat si pas encore trouve de zone
if (chariotHit == null) chariotHit = c;
}
if (chariotHit != null)
{
_chariotVise = chariotHit;
if (ancienChariot != null && ancienChariot != chariotHit)
{
ancienChariot.CacherFantomes();
ancienChariot.CacherFantomeToronAxe();
}
if (porteToron && zoneTrouvee && axeToronIdx >= 0)
{
_chariotAxeToronCibleIndex = axeToronIdx;
chariotHit.AfficherFantomeToronAxe(axeToronIdx);
HUDManager hud = GetComponent<HUDManager>();
if (hud != null)
hud.SetInfoEquipement(axeToronIdx == 0
? "[E] Accrocher le toron sur l'axe gauche"
: "[E] Accrocher le toron sur l'axe droit");
}
else if (!porteToron && zoneTrouvee)
{
int tailleU = (_objetEnMain != null && _objetEnMain.tailleU > 0) ? _objetEnMain.tailleU : 1;
chariotHit.AfficherFantomePose(tailleU, _chariotZoneEtagereVisee);
HUDManager hud = GetComponent<HUDManager>();
if (hud != null)
hud.SetInfoEquipement(_chariotZoneEtagereVisee ? "[E] Poser sur l'etagere basse" : "[E] Poser sur le plateau");
}
else
{
// Chariot vise mais pas de zone specifique : cacher les fantomes
chariotHit.CacherFantomes();
chariotHit.CacherFantomeToronAxe();
}
}
else
{
if (ancienChariot != null)
{
ancienChariot.CacherFantomes();
ancienChariot.CacherFantomeToronAxe();
}
}
}
// ==================== DÉTECTION SUPPORTS MURAUX ====================
bool DetecterCibleSupportTorons()
{
if (_objetEnMain == null) return false;
if (_objetEnMain.GetComponent<ToronProcedural>() == null) return false;
SupportTorons support = SupportTorons.Instance;
if (support == null) return false;
float dist = Vector3.Distance(transform.position, support.transform.position);
if (dist > distanceDetectionSupport) return false;
Vector3 dirSupport = (support.transform.position - _camera.transform.position);
float distCam = dirSupport.magnitude;
if (distCam < 0.01f) return false;
dirSupport /= distCam;
float angle = Vector3.Angle(_camera.transform.forward, dirSupport);
if (angle > angleDetectionSupport) return false;
int r, c; Vector3 posAxe;
if (!support.TrouverAxeLibreLePlusProche(transform.position, out r, out c, out posAxe)) return false;
support.AfficherSurvolAxe(r, c);
_axeSupportCibleLigne = r; _axeSupportCibleColonne = c;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("[E] Remettre le toron sur le support");
return true;
}
void QuitterSurvolSupport()
{
if (_axeSupportCibleLigne < 0) return;
if (SupportTorons.Instance != null) SupportTorons.Instance.CacherSurvolAxe();
_axeSupportCibleLigne = -1; _axeSupportCibleColonne = -1;
}
bool DetecterCibleSupportPDU()
{
if (_objetEnMain == null) return false;
PDU pdu = _objetEnMain.GetComponent<PDU>();
if (pdu == null) return false;
if (pdu.estInstalle) return false;
SupportPDU support = SupportPDU.Instance;
if (support == null) return false;
float dist = Vector3.Distance(transform.position, support.transform.position);
if (dist > distanceDetectionSupport) return false;
Vector3 dirSupport = (support.transform.position - _camera.transform.position);
float distCam = dirSupport.magnitude;
if (distCam < 0.01f) return false;
dirSupport /= distCam;
float angle = Vector3.Angle(_camera.transform.forward, dirSupport);
if (angle > angleDetectionSupport) return false;
int r, c; Vector3 posEmpl;
if (!support.TrouverEmplacementLibreLePlusProche(transform.position, out r, out c, out posEmpl)) return false;
support.AfficherSurvolEmplacement(r, c);
_emplacementSupportPDUCibleLigne = r; _emplacementSupportPDUCibleColonne = c;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("[E] Remettre le PDU sur le support");
return true;
}
void QuitterSurvolSupportPDU()
{
if (_emplacementSupportPDUCibleLigne < 0) return;
if (SupportPDU.Instance != null) SupportPDU.Instance.CacherSurvolEmplacement();
_emplacementSupportPDUCibleLigne = -1; _emplacementSupportPDUCibleColonne = -1;
}
// ==================== SURVOL HELPERS ====================
void SetSurvol(Interactable obj) { if (_objetSurvole != obj) { if (_objetSurvole != null) _objetSurvole.SurvoleeFin(); _objetSurvole = obj; _objetSurvole.SurvoleDebut(); } }
void QuitterSurvol() { if (_objetSurvole != null) { _objetSurvole.SurvoleeFin(); _objetSurvole = null; } }
void QuitterSurvolSlot() { if (_slotSurvole != null) { _slotSurvole.SurvolFin(); _slotSurvole = null; } }
void QuitterSurvolEmplacement() { if (_emplacementVise != null) { _emplacementVise.SurvolFin(); _emplacementVise = null; } }
void QuitterSurvolEmplacementPDU() { if (_emplacementPDUVise != null) { _emplacementPDUVise.SurvolFin(); _emplacementPDUVise = null; } }
void QuitterSurvolPrise() { if (_priseSurvolee != null) { _priseSurvolee.Survoler(false); _priseSurvolee = null; } }
void QuitterSurvolBoutonPower() { if (_boutonPowerVise != null) { _boutonPowerVise.Survoler(false); _boutonPowerVise = null; } }
// ==================== RAMASSER ====================
void Ramasser()
{
TerminalCommande terminal = _objetSurvole.GetComponent<TerminalCommande>();
if (terminal != null) { terminal.Interagir(); QuitterSurvol(); return; }
MonitoringDatacenter monitoring = _objetSurvole.GetComponent<MonitoringDatacenter>();
if (monitoring != null) { monitoring.Interagir(); QuitterSurvol(); return; }
TableauTickets tableau = _objetSurvole.GetComponent<TableauTickets>();
if (tableau != null) { tableau.Interagir(); QuitterSurvol(); return; }
AgrandissementSalle agr = _objetSurvole.GetComponentInParent<AgrandissementSalle>();
if (agr != null && !agr.estAgrandi && !agr.enAnimation) { agr.DemanderAgrandir(); QuitterSurvol(); return; }
Chariot chariot = _objetSurvole.GetComponent<Chariot>();
if (chariot != null && _visePoignee)
{
if (_chariotPilote == chariot)
{
LacherChariot();
}
else
{
if (_chariotPilote != null) LacherChariot();
PrendreChariot(chariot);
QuitterSurvol();
}
return;
}
Chariot chariotParent = _objetSurvole.GetComponentInParent<Chariot>();
if (chariotParent != null && chariot == null)
{
if (chariotParent.estPilote) { QuitterSurvol(); return; }
// v6.11 : verifier en PREMIER si le toron survole est accroche sur un axe lateral
if (chariotParent.equipementsToronAxes != null)
{
for (int i = 0; i < chariotParent.equipementsToronAxes.Count; i++)
{
if (chariotParent.equipementsToronAxes[i] == _objetSurvole.gameObject)
{
GameObject t = chariotParent.RetirerToronDeAxeChariot(i);
if (t != null)
{
PrendreEnMain(t.GetComponent<Interactable>());
return;
}
}
}
}
// v6.9 : detecter si l'objet est sur le plateau ou sur l'etagere basse
bool surEtagere = chariotParent.equipementsSurEtagere != null
&& chariotParent.equipementsSurEtagere.Contains(_objetSurvole.gameObject);
GameObject top = surEtagere
? chariotParent.RetirerEquipementSurEtagere()
: chariotParent.RetirerEquipementDuDessus();
if (top != null)
{
// v6.13 Bug 2 : broadcaster le detachement a tous les clients
// (sinon sur les autres clients l'objet reste parente au chariot et
// vole avec lui quand on le lache).
NetworkIdentity topNetId = top.GetComponent<NetworkIdentity>();
if (topNetId != null) CmdDetacherDuChariot(topNetId.netId);
PrendreEnMain(top.GetComponent<Interactable>());
return;
}
}
PDU pdu = _objetSurvole.GetComponent<PDU>();
if (pdu != null && pdu.estInstalle)
{
bool cables = false;
foreach (var p in pdu.prises) if (p != null && p.estConnectee) { cables = true; break; }
if (cables) { Debug.LogWarning("Impossible : des cables sont branches !"); QuitterSurvol(); return; }
pdu.Desinstaller();
NetworkIdentity pduNetIdentity = pdu.GetComponent<NetworkIdentity>();
if (pduNetIdentity != null) CmdDesinstallerPDU(pduNetIdentity.netId);
PrendreEnMain(_objetSurvole);
return;
}
BaieRack baieRack = _objetSurvole.GetComponent<BaieRack>() ?? _objetSurvole.GetComponentInChildren<BaieRack>();
if (baieRack != null) { EmplacementBaie empl = TrouverEmplacementDeBaie(_objetSurvole.gameObject); if (empl != null) empl.RetirerBaie(); }
RackSlot slotSrc = TrouverSlotDeEquipement(_objetSurvole);
if (slotSrc != null)
{
if (!PorteAvantOuverte(slotSrc)) { HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("Ouvrir la porte avant d'abord"); QuitterSurvol(); return; }
RackSlot[] slotsArr = ObtenirSlotsParent(slotSrc);
if (slotsArr != null) slotSrc.Liberer(slotsArr);
}
ZoneLivraison zone = _objetSurvole.GetComponentInParent<ZoneLivraison>();
if (zone == null) zone = ZoneLivraison.Instance;
if (zone != null)
{
Interactable plusHaut = zone.TrouverPlusHautDansPile(_objetSurvole);
if (plusHaut == null) plusHaut = _objetSurvole;
Vector3 pos = plusHaut.transform.position;
if (plusHaut != _objetSurvole) _objetSurvole.SurvoleeFin();
plusHaut.transform.SetParent(null);
IgnorerCollisionsAvecStructure(plusHaut.gameObject, zone.transform, true);
PrendreEnMain(plusHaut);
// v6.13 Bug 1 : reorganisation cote SERVEUR pour que les positions
// soient sync via NetworkTransform a tous les clients.
CmdReorganiserZoneLivraison(pos);
return;
}
PrendreEnMain(_objetSurvole);
}
void PrendreEnMain(Interactable obj)
{
if (obj == null) return;
CartonLivraison carton = obj.GetComponent<CartonLivraison>();
if (carton != null && carton.ContientBaie()) { QuitterSurvol(); carton.OuvrirPlanPlacement(); return; }
_objetEnMain = obj; _objetSurvole = null;
_objetEnMain.Ramasser();
_rbEnMain = _objetEnMain.GetComponent<Rigidbody>();
if (_rbEnMain != null)
{
_rbEnMain.isKinematic = false; _rbEnMain.useGravity = false;
_rbEnMain.freezeRotation = true;
_rbEnMain.collisionDetectionMode = CollisionDetectionMode.Continuous;
_rbEnMain.interpolation = RigidbodyInterpolation.Interpolate;
Vector3 dirJoueur = new Vector3(_camera.transform.forward.x, 0, _camera.transform.forward.z).normalized;
Quaternion rotJoueur = Quaternion.LookRotation(dirJoueur);
_rotationOffsetTransport = Quaternion.Inverse(rotJoueur) * _objetEnMain.transform.rotation;
}
NetworkIdentity objNetId = obj.GetComponent<NetworkIdentity>();
if (objNetId != null) CmdRamasser(objNetId.netId);
}
// ==================== POSER ====================
void Poser()
{
RestaurerCollisionsIgnorees();
if (_axeSupportCibleLigne >= 0 && _objetEnMain != null && _objetEnMain.GetComponent<ToronProcedural>() != null)
{ PoserSurSupportTorons(); return; }
if (_emplacementSupportPDUCibleLigne >= 0 && _objetEnMain != null && _objetEnMain.GetComponent<PDU>() != null)
{ PoserSurSupportPDU(); return; }
// v6.11 : pose d'un toron sur un axe lateral du chariot (prioritaire sur plateau/etagere)
if (_chariotVise != null && _chariotAxeToronCibleIndex >= 0
&& _objetEnMain != null && _objetEnMain.GetComponent<ToronProcedural>() != null)
{
int indexAxe = _chariotAxeToronCibleIndex;
if (_chariotVise.PoserToronSurAxeChariot(_objetEnMain.gameObject, indexAxe))
{
_chariotVise.CacherFantomeToronAxe();
Vector3 posT = _objetEnMain.transform.position;
Quaternion rotT = _objetEnMain.transform.rotation;
NotifierPoserReseau(false, posT, rotT, estRacke: true);
// v6.13 Bug 2 : broadcast parenting a tous les clients
NetworkIdentity eqNetId = _objetEnMain.GetComponent<NetworkIdentity>();
NetworkIdentity chNetId = _chariotVise.GetComponent<NetworkIdentity>();
if (eqNetId != null && chNetId != null)
CmdParenterAuChariot(eqNetId.netId, chNetId.netId);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
_chariotAxeToronCibleIndex = -1;
HUDManager hud = GetComponent<HUDManager>();
if (hud != null) hud.SetInfoEquipement("");
return;
}
}
bool estNonRackable = (_objetEnMain.tailleU <= 0 || _objetEnMain.GetComponent<Chariot>() != null);
bool estCarton = _objetEnMain.GetComponent<CartonLivraison>() != null;
bool estBaie = _objetEnMain.GetComponent<BaieRack>() != null;
bool estPDU = _objetEnMain.GetComponent<PDU>() != null;
Vector3 posFinal = _objetEnMain.transform.position;
Quaternion rotFinal = _objetEnMain.transform.rotation;
bool gravite = true;
if (estPDU && _emplacementPDUVise != null && !_emplacementPDUVise.estOccupe)
{
PDU pduObj = _objetEnMain.GetComponent<PDU>();
pduObj.InstallerSurEmplacement(_emplacementPDUVise);
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal, estRacke: true);
NetworkIdentity pduNetId = _objetEnMain.GetComponent<NetworkIdentity>();
if (pduNetId != null) CmdInstallerPDU(pduNetId.netId, posFinal, rotFinal);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
QuitterSurvolEmplacementPDU();
HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("");
CacherFantomesChariotTous();
return;
}
if (estPDU)
{
PDU pduObj = _objetEnMain.GetComponent<PDU>();
GestionnaireAlimentation ga = GetComponent<GestionnaireAlimentation>();
if (ga != null && !pduObj.estInstalle && ga.TenterSnapPDU(pduObj))
{
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal);
NetworkIdentity pduNetId = _objetEnMain.GetComponent<NetworkIdentity>();
if (pduNetId != null) CmdInstallerPDU(pduNetId.netId, posFinal, rotFinal);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
CacherFantomesChariotTous();
return;
}
}
if (estCarton)
{
CartonLivraison carton = _objetEnMain.GetComponent<CartonLivraison>();
if (carton != null && carton.ContientEquipement())
{
if (_rbEnMain != null) { _rbEnMain.isKinematic = true; _rbEnMain.velocity = Vector3.zero; }
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
carton.TransformerAuSol();
CacherFantomesChariotTous();
return;
}
}
if (!estNonRackable && !estBaie && !estCarton && !estPDU && _slotSurvole != null && !_slotSurvole.estOccupe)
{
if (!PorteAvantOuverte(_slotSurvole)) { HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("Ouvrir la porte avant d'abord"); return; }
RackSlot[] slotsArr = ObtenirSlotsParent(_slotSurvole);
if (slotsArr != null && _slotSurvole.TenterInstallation(_objetEnMain, slotsArr))
{
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal, estRacke: true);
NetworkIdentity equipNetId = _objetEnMain.GetComponent<NetworkIdentity>();
NetworkIdentity baieNetId = _slotSurvole.GetComponentInParent<NetworkIdentity>();
if (equipNetId != null && baieNetId != null) { RackSlot[] allSlots = baieNetId.GetComponentsInChildren<RackSlot>(); int slotIdx = System.Array.IndexOf(allSlots, _slotSurvole); CmdInstallerDansSlot(equipNetId.netId, baieNetId.netId, slotIdx); }
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
QuitterSurvolSlot();
HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("");
CacherFantomesChariotTous();
return;
}
}
// ───── v6.10 : Pose sur chariot (plateau ou etagere basse) ─────
// IMPORTANT : estRacke=true pour que le serveur mette isKinematic=true.
// Sinon les objets poses deviennent non-kinematic sans gravite cote serveur
// et flottent en apesanteur des qu'on en retire un du dessus.
if (_chariotVise != null && !estNonRackable && !estBaie && !estCarton && !estPDU)
{
bool pose = _chariotZoneEtagereVisee
? _chariotVise.PoserEquipementSurEtagere(_objetEnMain.gameObject)
: _chariotVise.PoserEquipement(_objetEnMain.gameObject);
if (pose)
{
_chariotVise.CacherFantomes();
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal, estRacke: true);
// v6.13 Bug 2 : broadcast parenting a tous les clients
NetworkIdentity eqNetId = _objetEnMain.GetComponent<NetworkIdentity>();
NetworkIdentity chNetId = _chariotVise.GetComponent<NetworkIdentity>();
if (eqNetId != null && chNetId != null)
CmdParenterAuChariot(eqNetId.netId, chNetId.netId);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
return;
}
}
if (_chariotPilote != null && _chariotVise == null && !estNonRackable && !estBaie && !estCarton && !estPDU)
if (_chariotPilote.PoserEquipement(_objetEnMain.gameObject))
{
_chariotPilote.CacherFantomes();
gravite = false; posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(gravite, posFinal, rotFinal, estRacke: true);
// v6.13 Bug 2 : broadcast parenting a tous les clients
NetworkIdentity eqNetId = _objetEnMain.GetComponent<NetworkIdentity>();
NetworkIdentity chNetId = _chariotPilote.GetComponent<NetworkIdentity>();
if (eqNetId != null && chNetId != null)
CmdParenterAuChariot(eqNetId.netId, chNetId.netId);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
return;
}
// Drop au sol
if (_rbEnMain != null) { _rbEnMain.useGravity = true; _rbEnMain.velocity = Vector3.zero; _rbEnMain.interpolation = RigidbodyInterpolation.None; }
posFinal = _objetEnMain.transform.position; rotFinal = _objetEnMain.transform.rotation;
NotifierPoserReseau(true, posFinal, rotFinal);
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
QuitterSurvolSlot(); QuitterSurvolEmplacement(); QuitterSurvolEmplacementPDU();
QuitterSurvolSupport();
QuitterSurvolSupportPDU();
CacherFantomesChariotTous();
}
void PoserSurSupportTorons()
{
if (_objetEnMain == null) return;
int ligneCible = _axeSupportCibleLigne; int colonneCible = _axeSupportCibleColonne;
NetworkIdentity toronNetId = _objetEnMain.GetComponent<NetworkIdentity>();
if (toronNetId != null) CmdPoserToronSurSupport(toronNetId.netId, ligneCible, colonneCible);
if (!isServer && SupportTorons.Instance != null) { bool place = SupportTorons.Instance.PoserToronSurAxeSpecifique(_objetEnMain.gameObject, ligneCible, colonneCible); if (!place) SupportTorons.Instance.PoserToron(_objetEnMain.gameObject); }
else if (toronNetId == null && SupportTorons.Instance != null) { bool place = SupportTorons.Instance.PoserToronSurAxeSpecifique(_objetEnMain.gameObject, ligneCible, colonneCible); if (!place) SupportTorons.Instance.PoserToron(_objetEnMain.gameObject); }
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
QuitterSurvolSupport();
HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("");
}
void PoserSurSupportPDU()
{
if (_objetEnMain == null) return;
int ligneCible = _emplacementSupportPDUCibleLigne; int colonneCible = _emplacementSupportPDUCibleColonne;
NetworkIdentity pduNetId = _objetEnMain.GetComponent<NetworkIdentity>();
if (pduNetId != null) CmdPoserPDUSurSupport(pduNetId.netId, ligneCible, colonneCible);
if (!isServer && SupportPDU.Instance != null) { bool place = SupportPDU.Instance.PoserPDUSurEmplacementSpecifique(_objetEnMain.gameObject, ligneCible, colonneCible); if (!place) SupportPDU.Instance.PoserPDU(_objetEnMain.gameObject); }
else if (pduNetId == null && SupportPDU.Instance != null) { bool place = SupportPDU.Instance.PoserPDUSurEmplacementSpecifique(_objetEnMain.gameObject, ligneCible, colonneCible); if (!place) SupportPDU.Instance.PoserPDU(_objetEnMain.gameObject); }
_objetEnMain.Poser(); _objetEnMain = null; _rbEnMain = null;
QuitterSurvolSupportPDU();
HUDManager hud = GetComponent<HUDManager>(); if (hud != null) hud.SetInfoEquipement("");
}
void NotifierPoserReseau(bool avecGravite, Vector3 position, Quaternion rotation, bool estRacke = false)
{
if (_objetEnMain == null) return;
NetworkIdentity objNetId = _objetEnMain.GetComponent<NetworkIdentity>();
if (objNetId != null) CmdPoser(objNetId.netId, position, rotation, avecGravite, estRacke);
}
// ==================== HELPERS ====================
bool ViseBouton(Vector3 positionMonde, float seuilPixels = 60f)
{
if (_camera == null) return false;
Vector3 screenPos = _camera.WorldToScreenPoint(positionMonde);
if (screenPos.z <= 0f) return false;
return Vector2.Distance(new Vector2(Screen.width / 2f, Screen.height / 2f), new Vector2(screenPos.x, screenPos.y)) < seuilPixels;
}
bool PorteAvantOuverte(RackSlot slot)
{
if (slot == null) return true;
Transform baie = slot.transform.parent; if (baie == null) return true;
foreach (Transform enfant in baie)
if (enfant.name == "Pivot_PorteAvant") { PorteBaie porte = enfant.GetComponent<PorteBaie>(); if (porte != null) return porte.estOuverte; }
return true;
}
RackSlot[] ObtenirSlotsParent(RackSlot slot)
{
if (slot == null) return null;
BaieRack baieRack = slot.GetComponentInParent<BaieRack>(); if (baieRack != null && baieRack.slots != null) return baieRack.slots;
BaieProcedurale baieProc = slot.GetComponentInParent<BaieProcedurale>(); if (baieProc != null && baieProc.slots != null) return baieProc.slots;
Transform parent = slot.transform.parent; if (parent != null) return parent.GetComponentsInChildren<RackSlot>();
return null;
}
private void IgnorerCollisionsAvecStructure(GameObject objet, Transform parent, bool ignorer)
{
Collider objCollider = objet.GetComponent<Collider>(); if (objCollider == null) return;
_collisionsIgnorees.Clear();
for (int i = 0; i < parent.childCount; i++)
{
Transform enfant = parent.GetChild(i); string nom = enfant.name;
if (nom.StartsWith("Planche_") || nom.StartsWith("Montant_") || nom.StartsWith("Etiquette_"))
{ Collider sc = enfant.GetComponent<Collider>(); if (sc != null) { Physics.IgnoreCollision(objCollider, sc, ignorer); if (ignorer) _collisionsIgnorees.Add(sc); } }
Interactable inter = enfant.GetComponent<Interactable>();
if (inter != null && enfant.gameObject != objet)
{ Collider ec = enfant.GetComponent<Collider>(); if (ec != null) { Physics.IgnoreCollision(objCollider, ec, ignorer); if (ignorer) _collisionsIgnorees.Add(ec); } }
}
}
private void RestaurerCollisionsIgnorees()
{
if (_objetEnMain == null || _collisionsIgnorees.Count == 0) return;
Collider objCollider = _objetEnMain.GetComponent<Collider>();
if (objCollider == null) { _collisionsIgnorees.Clear(); return; }
foreach (Collider col in _collisionsIgnorees) if (col != null) Physics.IgnoreCollision(objCollider, col, false);
_collisionsIgnorees.Clear();
}
// v6.12 : gestion pilotage chariot + transfert d'autorite multijoueur
void PrendreChariot(Chariot chariot)
{
if (chariot == null) return;
_chariotPilote = chariot;
// Demander l'autorite au serveur pour pouvoir modifier transform.position
// via NetworkTransform ClientToServer. Sans ca, Mirror ignore les changements
// cote client et les autres joueurs ne voient pas le chariot bouger.
NetworkIdentity ni = chariot.GetComponent<NetworkIdentity>();
if (ni != null) CmdDemanderAutoriteChariot(ni.netId);
chariot.TogglePilotage(transform);
}
void LacherChariot()
{
if (_chariotPilote == null) return;
NetworkIdentity ni = _chariotPilote.GetComponent<NetworkIdentity>();
_chariotPilote.CacherFantomes();
_chariotPilote.TogglePilotage(transform);
// Relacher l'autorite APRES TogglePilotage pour que la position finale
// soit bien envoyee au serveur avant que le client perde son droit d'ecrire.
if (ni != null) CmdRelacherAutoriteChariot(ni.netId);
_chariotPilote = null;
}
[Command]
void CmdDemanderAutoriteChariot(uint chariotNetId)
{
if (!NetworkServer.spawned.ContainsKey(chariotNetId)) return;
NetworkIdentity ni = NetworkServer.spawned[chariotNetId];
if (ni == null) return;
// Si un autre client a deja l'autorite, la retirer d'abord
if (ni.connectionToClient != null && ni.connectionToClient != connectionToClient)
ni.RemoveClientAuthority();
ni.AssignClientAuthority(connectionToClient);
}
[Command]
void CmdRelacherAutoriteChariot(uint chariotNetId)
{
if (!NetworkServer.spawned.ContainsKey(chariotNetId)) return;
NetworkIdentity ni = NetworkServer.spawned[chariotNetId];
if (ni == null) return;
// Ne retirer l'autorite que si c'est bien ce joueur qui l'a actuellement,
// pour eviter de voler l'autorite a quelqu'un d'autre par accident.
if (ni.connectionToClient == connectionToClient)
ni.RemoveClientAuthority();
}
// ===== v6.13 Bug 1 : reorganisation ZoneLivraison cote serveur =====
[Command]
void CmdReorganiserZoneLivraison(Vector3 posRetireeWorld)
{
if (ZoneLivraison.Instance != null)
ZoneLivraison.Instance.ReorganiserNiveauComplet(posRetireeWorld);
}
// ===== v6.13 Bug 2 : sync du parenting equipement <-> chariot =====
[Command]
void CmdParenterAuChariot(uint equipNetId, uint chariotNetId)
{
if (!NetworkServer.spawned.ContainsKey(equipNetId)) return;
if (!NetworkServer.spawned.ContainsKey(chariotNetId)) return;
RpcParenterAuChariot(equipNetId, chariotNetId);
}
[ClientRpc]
void RpcParenterAuChariot(uint equipNetId, uint chariotNetId)
{
if (!NetworkClient.spawned.TryGetValue(equipNetId, out NetworkIdentity eqNi)) return;
if (!NetworkClient.spawned.TryGetValue(chariotNetId, out NetworkIdentity chNi)) return;
if (eqNi == null || chNi == null) return;
// worldPositionStays=true : conserver la position monde actuelle. L'equipement
// est deja a la bonne position (via NetworkTransform), on veut juste l'attacher
// au chariot pour qu'il suive les futurs mouvements.
eqNi.transform.SetParent(chNi.transform, true);
}
[Command]
void CmdDetacherDuChariot(uint equipNetId)
{
if (!NetworkServer.spawned.ContainsKey(equipNetId)) return;
RpcDetacherDuChariot(equipNetId);
}
[ClientRpc]
void RpcDetacherDuChariot(uint equipNetId)
{
if (!NetworkClient.spawned.TryGetValue(equipNetId, out NetworkIdentity eqNi)) return;
if (eqNi == null) return;
eqNi.transform.SetParent(null, true);
}
EmplacementBaie TrouverEmplacementDeBaie(GameObject baie)
{
if (_tousEmplacements == null) _tousEmplacements = FindObjectsOfType<EmplacementBaie>();
foreach (EmplacementBaie empl in _tousEmplacements) if (empl.estOccupe && empl.baieInstallee == baie) return empl;
return null;
}
RackSlot TrouverSlotDeEquipement(Interactable equipement)
{
if (equipement == null) return null;
foreach (BaieRack baie in FindObjectsOfType<BaieRack>()) { if (baie.slots == null) continue; foreach (RackSlot s in baie.slots) if (s != null && s.equipementInstalle == equipement) return s; }
foreach (BaieProcedurale baie in FindObjectsOfType<BaieProcedurale>()) { if (baie.slots == null) continue; foreach (RackSlot s in baie.slots) if (s != null && s.equipementInstalle == equipement) return s; }
return null;
}
}