using AfterHours.Data; using AfterHours.Interaction; using AfterHours.Networking; using Unity.Netcode; using UnityEngine; namespace AfterHours.Shop { /// /// Ein Regalfach: hält genau eine Produktsorte. Auffüllen geht über /// Interact(), Entnahme durch Kunden serverseitig über TakeOne(). /// public sealed class ShelfSlot : NetworkBehaviour, IInteractable { [SerializeField] private ProductCatalog _catalog; [SerializeField] private int _capacityUnits = 12; [Tooltip("Ist dieses Fach vom Schaufenster aus einsehbar? Setzt der Level-Designer.")] [SerializeField] private bool _exposedToStreet; [SerializeField] private Transform _displayRoot; private readonly NetworkVariable _productId = new(-1); private readonly NetworkVariable _quantity = new(0); private readonly NetworkVariable _price = new(0f); public int ProductId => _productId.Value; public int Quantity => _quantity.Value; public float Price => _price.Value; public bool ExposedToStreet => _exposedToStreet; public bool IsEmpty => _quantity.Value <= 0; public override void OnNetworkSpawn() { _quantity.OnValueChanged += (_, _) => RefreshVisuals(); _productId.OnValueChanged += (_, _) => RefreshVisuals(); RefreshVisuals(); if (IsServer) ShelfRegistry.Instance.Register(this); } public override void OnNetworkDespawn() { if (IsServer) ShelfRegistry.Instance.Unregister(this); } public string GetPrompt(ulong clientId) { if (IsEmpty) return "Regal befüllen"; var product = _catalog.Get(_productId.Value); string name = product != null ? product.DisplayName : "?"; return $"{name} — {_quantity.Value}/{Capacity()} — {_price.Value:0.00} €"; } /// Server: Spieler füllt aus der Hand auf. public void Interact(NetworkObject actor) { if (!IsServer) return; if (!actor.TryGetComponent(out var hands) || hands.IsEmpty) return; // Fach ist mit anderer Ware belegt -> nichts tun. if (!IsEmpty && _productId.Value != hands.ProductId) return; int free = Capacity() - _quantity.Value; if (free <= 0) return; int moved = hands.TryGive(Mathf.Min(free, hands.Quantity)); if (moved <= 0) return; if (IsEmpty) { _productId.Value = hands.ProductId >= 0 ? hands.ProductId : _productId.Value; var def = _catalog.Get(_productId.Value); if (def != null && _price.Value <= 0f) _price.Value = def.RecommendedPrice; } _quantity.Value += moved; } /// Server: Kunde entnimmt ein Stück. public bool TakeOne() { if (!IsServer || IsEmpty) return false; _quantity.Value--; if (_quantity.Value == 0) _productId.Value = -1; return true; } /// Server: Preis setzen (vom Preis-UI aus). public void SetPrice(float price) { if (!IsServer) return; _price.Value = Mathf.Max(0.01f, price); } /// Kapazität in Stück, abhängig von der Produktgröße. private int Capacity() { var product = _catalog.Get(_productId.Value); int size = product != null ? (int)product.ShelfSize : 1; return Mathf.Max(1, _capacityUnits / size); } private void RefreshVisuals() { // TODO: Verpackungs-Prefabs nach Menge ein-/ausblenden (Object Pool). // Läuft auf allen Peers, rein kosmetisch. } } }