Files
SixShopSimulator/Assets/Scripts/Customers/ComfortModel.cs
T
2026-08-10 19:23:04 +02:00

76 lines
3.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
namespace AfterHours.Customers
{
/// <summary>
/// Kern-Mechanik des Spiels: Wie wohl fühlt sich ein Kunde gerade?
/// Fällt der Komfort unter <see cref="AbandonThreshold"/>, legt der Kunde
/// die Ware zurück und geht.
///
/// Engine-frei und damit testbar siehe Tests/EditMode/ComfortModelTests.cs.
/// </summary>
public static class ComfortModel
{
public const float AbandonThreshold = 0.25f;
/// <summary>Komfortverlust pro anderem Kunden im Nahbereich.</summary>
private const float CrowdingPenaltyPerPerson = 0.09f;
/// <summary>Malus, wenn Personal ungefragt in Reichweite steht.</summary>
private const float UnwantedStaffPenalty = 0.18f;
/// <summary>Malus, wenn das Regal von der Straße aus einsehbar ist.</summary>
private const float ExposedShelfPenalty = 0.22f;
/// <summary>Erholung pro Sekunde, wenn niemand stört.</summary>
private const float RecoveryPerSecond = 0.12f;
/// <summary>
/// Zielkomfort für die aktuelle Situation (0..1).
/// </summary>
/// <param name="baseTolerance">Charaktereigenschaft des Kunden, 0..1. Höher = unerschrockener.</param>
/// <param name="discretionDemand">Diskretionsbedarf des betrachteten Produkts, 0..1.</param>
/// <param name="nearbyCustomers">Andere Kunden im Nahbereich.</param>
/// <param name="staffNearbyUnrequested">Personal steht daneben, ohne dass Beratung gewünscht war.</param>
/// <param name="shelfExposedToStreet">Regal ist vom Schaufenster aus einsehbar.</param>
public static float EvaluateTarget(
float baseTolerance,
float discretionDemand,
int nearbyCustomers,
bool staffNearbyUnrequested,
bool shelfExposedToStreet)
{
if (nearbyCustomers < 0) throw new ArgumentOutOfRangeException(nameof(nearbyCustomers));
float tolerance = Clamp01(baseTolerance);
float sensitivity = Clamp01(discretionDemand);
// Ein toleranter Kunde bei unkritischer Ware ist praktisch unstörbar.
float exposure = sensitivity * (1f - tolerance * 0.6f);
float penalty = 0f;
penalty += nearbyCustomers * CrowdingPenaltyPerPerson;
if (staffNearbyUnrequested) penalty += UnwantedStaffPenalty;
if (shelfExposedToStreet) penalty += ExposedShelfPenalty;
return Clamp01(1f - penalty * exposure * 2f);
}
/// <summary>Bewegt den aktuellen Komfort auf den Zielwert zu (Frame-Schritt).</summary>
public static float Step(float current, float target, float deltaTime)
{
if (target >= current)
{
// Erholung ist träge …
return Clamp01(current + RecoveryPerSecond * deltaTime);
}
// … Unbehagen schlägt sofort durch.
return Clamp01(target);
}
public static bool ShouldAbandon(float comfort) => comfort < AbandonThreshold;
private static float Clamp01(float v) => v < 0f ? 0f : (v > 1f ? 1f : v);
}
}