This commit is contained in:
2026-08-10 19:23:04 +02:00
commit 404d15a56c
134 changed files with 5374 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
namespace AfterHours.Data
{
/// <summary>Grobes Sortiment-Segment. Beeinflusst Kundschaft, Marge und Ruf.</summary>
public enum ProductCategory
{
Wellness, // Massage, Öle, Pflege niedrige Hemmschwelle, kleine Marge
Couples, // Paar-Sortiment Kern des Ladens
Lingerie, // Textil hohe Marge, braucht Umkleide
Novelty, // Scherzartikel Laufkundschaft, kaum Beratung
Specialty // Nische hohe Marge, hohe Hemmschwelle, braucht Beratung
}
public enum ShelfSize
{
Small = 1,
Medium = 2,
Large = 4
}
public enum CustomerState
{
Entering,
Browsing,
SeekingAdvice,
WaitingForStaff,
Queueing,
Paying,
Leaving,
Abandoning // geht ohne Kauf Komfort unter Schwelle
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a86bac230bc87784ca31703d750385db
+46
View File
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using UnityEngine;
namespace AfterHours.Data
{
/// <summary>Zentrale Auflösung ProductId -> ProductDefinition.</summary>
[CreateAssetMenu(menuName = "AfterHours/Product Catalog", fileName = "ProductCatalog")]
public sealed class ProductCatalog : ScriptableObject
{
[SerializeField] private List<ProductDefinition> _products = new();
private Dictionary<int, ProductDefinition> _byId;
public IReadOnlyList<ProductDefinition> All => _products;
private void EnsureIndexed()
{
if (_byId != null) return;
_byId = new Dictionary<int, ProductDefinition>(_products.Count);
foreach (var product in _products)
{
if (product == null) continue;
if (_byId.ContainsKey(product.ProductId))
{
Debug.LogError($"[ProductCatalog] Doppelte ProductId {product.ProductId} bei '{product.name}'.");
continue;
}
_byId[product.ProductId] = product;
}
}
public bool TryGet(int productId, out ProductDefinition product)
{
EnsureIndexed();
return _byId.TryGetValue(productId, out product);
}
public ProductDefinition Get(int productId) => TryGet(productId, out var p) ? p : null;
#if UNITY_EDITOR
private void OnValidate() => _byId = null;
#endif
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: ff37cbc356c13f849995837f2a7dfc89
+45
View File
@@ -0,0 +1,45 @@
using UnityEngine;
namespace AfterHours.Data
{
/// <summary>
/// Stammdaten eines Artikels. Wird NIE über das Netzwerk übertragen
/// im Netzwerk zirkuliert ausschließlich die ProductId.
/// </summary>
[CreateAssetMenu(menuName = "AfterHours/Product", fileName = "Product_")]
public sealed class ProductDefinition : ScriptableObject
{
[Header("Identität")]
[SerializeField] private int _productId = -1;
[SerializeField] private string _displayName = "Unbenannt";
[SerializeField] private ProductCategory _category = ProductCategory.Wellness;
[Header("Wirtschaft")]
[SerializeField, Min(0.01f)] private float _wholesalePrice = 5f;
[SerializeField, Min(0.01f)] private float _recommendedPrice = 12f;
[Header("Laden")]
[SerializeField] private ShelfSize _shelfSize = ShelfSize.Small;
[Tooltip("0 = greift jeder mit, 1 = Kunde will damit nicht gesehen werden.")]
[SerializeField, Range(0f, 1f)] private float _discretionDemand = 0.3f;
[Tooltip("Wahrscheinlichkeit, dass der Kunde vor dem Kauf Beratung sucht.")]
[SerializeField, Range(0f, 1f)] private float _adviceLikelihood = 0.2f;
[Header("Darstellung (stilisierte Verpackung siehe CLAUDE.md §7)")]
[SerializeField] private GameObject _packagePrefab;
[SerializeField] private Sprite _icon;
public int ProductId => _productId;
public string DisplayName => _displayName;
public ProductCategory Category => _category;
public float WholesalePrice => _wholesalePrice;
public float RecommendedPrice => _recommendedPrice;
public ShelfSize ShelfSize => _shelfSize;
public float DiscretionDemand => _discretionDemand;
public float AdviceLikelihood => _adviceLikelihood;
public GameObject PackagePrefab => _packagePrefab;
public Sprite Icon => _icon;
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5e52a7daaf085524caf591a33107a59d