134 lines
4.6 KiB
C#
134 lines
4.6 KiB
C#
using AfterHours.Data;
|
|
using AfterHours.Interaction;
|
|
using AfterHours.Networking;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace AfterHours.Shop
|
|
{
|
|
/// <summary>
|
|
/// Ein Regalfach: hält genau eine Produktsorte. Auffüllen geht über
|
|
/// Interact(), Entnahme durch Kunden serverseitig über TakeOne().
|
|
/// </summary>
|
|
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<int> _productId = new(-1);
|
|
private readonly NetworkVariable<int> _quantity = new(0);
|
|
private readonly NetworkVariable<float> _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} €";
|
|
}
|
|
|
|
/// <summary>Server: Spieler füllt aus der Hand auf.</summary>
|
|
public void Interact(NetworkObject actor)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
// TODO: Debug.Log-Zeilen entfernen, sobald Regal-Befüllen zuverlässig läuft.
|
|
if (!actor.TryGetComponent<PlayerHands>(out var hands) || hands.IsEmpty)
|
|
{
|
|
Debug.Log("[ShelfSlot] Abbruch: keine PlayerHands am Actor oder Hände leer.");
|
|
return;
|
|
}
|
|
|
|
int incomingProductId = hands.ProductId;
|
|
|
|
// Fach ist mit anderer Ware belegt -> nichts tun.
|
|
if (!IsEmpty && _productId.Value != incomingProductId)
|
|
{
|
|
Debug.Log($"[ShelfSlot] Abbruch: Fach belegt mit ProductId {_productId.Value}, Hand trägt {incomingProductId}.");
|
|
return;
|
|
}
|
|
|
|
int free = Capacity() - _quantity.Value;
|
|
if (free <= 0)
|
|
{
|
|
Debug.Log($"[ShelfSlot] Abbruch: Fach voll ({_quantity.Value}/{Capacity()}).");
|
|
return;
|
|
}
|
|
|
|
int moved = hands.TryGive(Mathf.Min(free, hands.Quantity));
|
|
if (moved <= 0)
|
|
{
|
|
Debug.Log("[ShelfSlot] Abbruch: TryGive() lieferte 0 Stück.");
|
|
return;
|
|
}
|
|
|
|
if (IsEmpty)
|
|
{
|
|
_productId.Value = incomingProductId;
|
|
var def = _catalog.Get(_productId.Value);
|
|
if (def != null && _price.Value <= 0f) _price.Value = def.RecommendedPrice;
|
|
}
|
|
|
|
_quantity.Value += moved;
|
|
Debug.Log($"[ShelfSlot] Eingeräumt: {moved} Stück, jetzt {_quantity.Value}/{Capacity()}.");
|
|
}
|
|
|
|
/// <summary>Server: Kunde entnimmt ein Stück.</summary>
|
|
public bool TakeOne()
|
|
{
|
|
if (!IsServer || IsEmpty) return false;
|
|
|
|
_quantity.Value--;
|
|
if (_quantity.Value == 0) _productId.Value = -1;
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Server: Preis setzen (vom Preis-UI aus).</summary>
|
|
public void SetPrice(float price)
|
|
{
|
|
if (!IsServer) return;
|
|
_price.Value = Mathf.Max(0.01f, price);
|
|
}
|
|
|
|
/// <summary>Kapazität in Stück, abhängig von der Produktgröße.</summary>
|
|
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.
|
|
}
|
|
}
|
|
}
|