103 lines
3.3 KiB
C#
103 lines
3.3 KiB
C#
using System.Collections.Generic;
|
|
using AfterHours.Core;
|
|
using AfterHours.Data;
|
|
using AfterHours.Interaction;
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
|
|
namespace AfterHours.Shop
|
|
{
|
|
/// <summary>
|
|
/// Kasse. Kunden reihen sich ein; ein Spieler scannt Position für Position
|
|
/// und schließt ab. Sämtliche Geldbewegung passiert hier auf dem Server.
|
|
/// </summary>
|
|
public sealed class CashRegister : NetworkBehaviour, IInteractable
|
|
{
|
|
[SerializeField] private ProductCatalog _catalog;
|
|
[SerializeField] private Transform _queueStart;
|
|
[SerializeField] private float _queueSpacing = 0.8f;
|
|
|
|
/// <summary>Server-only. Clients sehen die Schlange über die NPC-Positionen.</summary>
|
|
private readonly List<Customers.CustomerAgent> _queue = new();
|
|
|
|
private readonly NetworkVariable<float> _pendingTotal = new(0f);
|
|
private readonly NetworkVariable<int> _scannedItems = new(0);
|
|
|
|
public float PendingTotal => _pendingTotal.Value;
|
|
public int QueueLength => _queue.Count;
|
|
|
|
public string GetPrompt(ulong clientId)
|
|
{
|
|
if (_queue.Count == 0) return "Kasse (frei)";
|
|
return _scannedItems.Value > 0
|
|
? $"Kassieren — {_pendingTotal.Value:0.00} €"
|
|
: "Artikel scannen";
|
|
}
|
|
|
|
/// <summary>Server: ein Klick = ein Artikel scannen, oder abschließen wenn fertig.</summary>
|
|
public void Interact(NetworkObject actor)
|
|
{
|
|
if (!IsServer || _queue.Count == 0) return;
|
|
|
|
var customer = _queue[0];
|
|
if (customer == null) { _queue.RemoveAt(0); return; }
|
|
|
|
if (_scannedItems.Value < customer.BasketCount)
|
|
{
|
|
ScanNext(customer);
|
|
return;
|
|
}
|
|
|
|
Finalize(customer);
|
|
}
|
|
|
|
private void ScanNext(Customers.CustomerAgent customer)
|
|
{
|
|
var line = customer.BasketAt(_scannedItems.Value);
|
|
_pendingTotal.Value += line.Price;
|
|
_scannedItems.Value++;
|
|
}
|
|
|
|
private void Finalize(Customers.CustomerAgent customer)
|
|
{
|
|
ShopEconomy.Instance.Earn(_pendingTotal.Value);
|
|
ShopEconomy.Instance.RegisterSale(customer.Comfort);
|
|
|
|
customer.OnPaid();
|
|
_queue.RemoveAt(0);
|
|
|
|
_pendingTotal.Value = 0f;
|
|
_scannedItems.Value = 0;
|
|
|
|
ReflowQueue();
|
|
}
|
|
|
|
/// <summary>Server: Kunde stellt sich an. Gibt die Zielposition zurück.</summary>
|
|
public Vector3 Enqueue(Customers.CustomerAgent customer)
|
|
{
|
|
if (!IsServer) return _queueStart.position;
|
|
|
|
if (!_queue.Contains(customer)) _queue.Add(customer);
|
|
return SlotPosition(_queue.IndexOf(customer));
|
|
}
|
|
|
|
/// <summary>Server: Kunde verlässt die Schlange (z. B. weil er aufgibt).</summary>
|
|
public void Remove(Customers.CustomerAgent customer)
|
|
{
|
|
if (!IsServer) return;
|
|
if (_queue.Remove(customer)) ReflowQueue();
|
|
}
|
|
|
|
private void ReflowQueue()
|
|
{
|
|
for (int i = 0; i < _queue.Count; i++)
|
|
if (_queue[i] != null) _queue[i].MoveTo(SlotPosition(i));
|
|
}
|
|
|
|
private Vector3 SlotPosition(int index)
|
|
{
|
|
return _queueStart.position + _queueStart.forward * (-_queueSpacing * index);
|
|
}
|
|
}
|
|
}
|