Add _displayRotationEuler to Lieferkarton/ShelfSlot so package prefabs with a non-upright export orientation can be corrected per-container without touching PackageDisplay code. Fixes a bug where flying packages landed with a different rotation than the resting shelf display (PlayerCarry used the raw shelf transform instead of ShelfSlot.GetSlotWorldRotation()). Also adds the pocoregal shelf model (Blender export) and its materials, places it in the Shop scene, registers ShelfSlot as a network prefab, and rescales EmptyBox. See docs/PACKAGE_DISPLAY_SETUP.md for the display-grid setup/troubleshooting reference. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
150 lines
5.9 KiB
C#
150 lines
5.9 KiB
C#
using AfterHours.Data;
|
|
using AfterHours.Interaction;
|
|
using AfterHours.Networking;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace AfterHours.Shop
|
|
{
|
|
[RequireComponent(typeof(NetworkObject))]
|
|
[RequireComponent(typeof(Carryable))]
|
|
public class Lieferkarton : NetworkBehaviour, IInteractable, IPourSource
|
|
{
|
|
[Header("Deckel Objekte")]
|
|
[SerializeField] private Transform deckelLinks;
|
|
[SerializeField] private Transform deckelRechts;
|
|
|
|
[Header("Rotations-Einstellungen")]
|
|
[SerializeField] private float oeffnungsWinkel = 110f; // Winkel in Grad nach außen
|
|
[SerializeField] private float geschwindigkeit = 5f; // Geschwindigkeit des Aufklappens
|
|
|
|
[Header("Inhalt")]
|
|
[SerializeField] private ProductCatalog _catalog;
|
|
[SerializeField] private int _productId = -1;
|
|
[SerializeField] private int _startingQuantity = 20;
|
|
[SerializeField] private Transform _contentsRoot;
|
|
[SerializeField] private int _maxVisiblePackages = 12;
|
|
|
|
[Header("Anzeige-Raster (sauber gestapelt, so wie Platz ist)")]
|
|
[SerializeField] private int _displayColumns = 3;
|
|
[SerializeField] private int _displayRows = 3;
|
|
[SerializeField] private Vector3 _displaySpacing = new(0.12f, 0.1f, 0.12f);
|
|
[SerializeField] private Vector3 _displayRotationEuler = Vector3.zero;
|
|
|
|
// Eine synchronisierte Variable, die den Zustand des Kartons speichert
|
|
private NetworkVariable<bool> istOffen = new NetworkVariable<bool>(
|
|
false,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server // Nur der Server darf den Zustand ändern
|
|
);
|
|
|
|
private readonly NetworkVariable<int> _quantity = new(
|
|
0,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server
|
|
);
|
|
|
|
public bool IsOpen => istOffen.Value;
|
|
public int ProductId => _productId;
|
|
public int Quantity => _quantity.Value;
|
|
public bool IsEmpty => _quantity.Value <= 0;
|
|
|
|
// Interne Variablen zum Speichern der Ziel-Rotationen
|
|
private Quaternion linksGeschlossen;
|
|
private Quaternion linksOffen;
|
|
private Quaternion rechtsGeschlossen;
|
|
private Quaternion rechtsOffen;
|
|
|
|
private Carryable _carryable;
|
|
private PackageDisplay _packageDisplay;
|
|
|
|
private void Awake()
|
|
{
|
|
_carryable = GetComponent<Carryable>();
|
|
_packageDisplay = new PackageDisplay(_contentsRoot, _displayColumns, _displayRows, _displaySpacing, _displayRotationEuler);
|
|
|
|
// Start-Rotationen (Geschlossen) direkt beim Laden sichern
|
|
if (deckelLinks != null) linksGeschlossen = deckelLinks.localRotation;
|
|
if (deckelRechts != null) rechtsGeschlossen = deckelRechts.localRotation;
|
|
|
|
// Ziel-Rotationen berechnen (X-Achse = Scharnierkante entlang der Längsseite)
|
|
linksOffen = linksGeschlossen * Quaternion.Euler(-oeffnungsWinkel, 0, 0);
|
|
rechtsOffen = rechtsGeschlossen * Quaternion.Euler(oeffnungsWinkel, 0, 0);
|
|
}
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
if (IsServer) _quantity.Value = _startingQuantity;
|
|
|
|
_quantity.OnValueChanged += (_, _) => RefreshVisuals();
|
|
istOffen.OnValueChanged += (_, _) => RefreshVisuals();
|
|
RefreshVisuals();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// Beide Deckel weich auf jedem Client rotieren lassen basierend auf der NetworkVariable
|
|
if (deckelLinks != null)
|
|
{
|
|
Quaternion zielLinks = istOffen.Value ? linksOffen : linksGeschlossen;
|
|
deckelLinks.localRotation = Quaternion.Slerp(deckelLinks.localRotation, zielLinks, Time.deltaTime * geschwindigkeit);
|
|
}
|
|
|
|
if (deckelRechts != null)
|
|
{
|
|
Quaternion zielRechts = istOffen.Value ? rechtsOffen : rechtsGeschlossen;
|
|
deckelRechts.localRotation = Quaternion.Slerp(deckelRechts.localRotation, zielRechts, Time.deltaTime * geschwindigkeit);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wird von deinem Interaktionssystem aufgerufen (z. B. wenn "E" gedrückt wird).
|
|
/// Geschlossen -> öffnen. Offen und nicht getragen -> aufheben.
|
|
/// </summary>
|
|
public void Interact(NetworkObject actor)
|
|
{
|
|
if (!IsServer) return;
|
|
|
|
if (!istOffen.Value)
|
|
{
|
|
istOffen.Value = true;
|
|
return;
|
|
}
|
|
|
|
if (!_carryable.IsCarried && actor.TryGetComponent<PlayerCarry>(out var carry))
|
|
carry.TryPickUp(_carryable);
|
|
}
|
|
|
|
/// <summary>Server: nimmt ein Stück Ware aus dem Karton. Für das Ausschütten (siehe PlayerCarry).</summary>
|
|
public bool TryTakeOne()
|
|
{
|
|
if (!IsServer || IsEmpty) return false;
|
|
_quantity.Value--;
|
|
return true;
|
|
}
|
|
|
|
// Gibt den dynamischen Text für das UI-Prompt zurück
|
|
public string GetPrompt(ulong clientId)
|
|
{
|
|
if (!istOffen.Value) return "Karton öffnen";
|
|
|
|
var product = _catalog != null ? _catalog.Get(_productId) : null;
|
|
string name = product != null ? product.DisplayName : "?";
|
|
return IsEmpty ? $"{name} — leer" : $"{name} aufheben ({_quantity.Value} Stk.)";
|
|
}
|
|
|
|
/// <summary>Verpackungs-Prefab des aktuellen Produkts (auch für das Flug-Visual beim Ausschütten, siehe PlayerCarry).</summary>
|
|
public GameObject GetPackagePrefab()
|
|
{
|
|
var product = _catalog != null ? _catalog.Get(_productId) : null;
|
|
return product != null ? product.PackagePrefab : null;
|
|
}
|
|
|
|
private void RefreshVisuals()
|
|
{
|
|
int visibleCount = istOffen.Value ? _quantity.Value : 0;
|
|
_packageDisplay.SetTarget(GetPackagePrefab(), visibleCount, _maxVisiblePackages);
|
|
}
|
|
}
|
|
}
|