2024-04-05 19:24:26 +02:00
using System ;
2024-04-04 15:26:31 +02:00
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
2024-04-04 18:13:08 +02:00
using UnityEngine.Serialization ;
2024-04-04 19:41:19 +02:00
using Utility ;
2024-04-06 00:57:29 +02:00
using Random = UnityEngine . Random ;
2024-04-04 15:26:31 +02:00
2024-04-05 19:24:26 +02:00
[Serializable]
public struct DeveloperStats
{
public double BaseEfficiency ;
public int Fingers ;
public double CaffeineDrainFactor ;
public double HungerDrainFactor ;
2024-04-06 00:57:29 +02:00
public double UrinationDrainFactor ;
public double HappinessDrainFactor ;
2024-04-05 19:24:26 +02:00
2024-04-06 00:57:29 +02:00
// TODO: Not yet used
public double CoffeePreference ;
public double MatePreference ;
public DeveloperStats ( double baseEfficiency , int fingers , double caffeineDrainFactor , double hungerDrainFactor , double urinationDrainFactor , double happinessDrainFactor , double coffeePreference , double matePreference )
2024-04-05 19:24:26 +02:00
{
BaseEfficiency = baseEfficiency ;
Fingers = fingers ;
CaffeineDrainFactor = caffeineDrainFactor ;
HungerDrainFactor = hungerDrainFactor ;
UrinationDrainFactor = urinationDrainFactor ;
2024-04-06 00:57:29 +02:00
HappinessDrainFactor = happinessDrainFactor ;
CoffeePreference = coffeePreference ;
MatePreference = matePreference ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
public static readonly DeveloperStats Default = new DeveloperStats ( 1.0 , 10 , 1 , 1 , 1 , 1 , 0.5 , 0.5 ) ;
2024-04-05 19:24:26 +02:00
}
2024-04-04 15:26:31 +02:00
public class Developer : MonoBehaviour
{
2024-04-04 18:13:08 +02:00
private string _name ;
[SerializeField]
2024-04-05 19:24:26 +02:00
private DeveloperStats _baseStats = DeveloperStats . Default ;
2024-04-04 16:33:12 +02:00
2024-04-05 19:32:50 +02:00
[SerializeField, ShowOnly]
2024-04-05 19:24:26 +02:00
private double _currentEfficiency = 1.0 ;
2024-04-04 18:13:08 +02:00
[SerializeField]
private int _fingersLeft = 10 ;
2024-04-05 19:24:26 +02:00
[SerializeField]
private double _caffeineLevel = 1.0 ;
[SerializeField]
private double _hungerLevel = 1.0 ;
2024-04-05 19:32:50 +02:00
[SerializeField]
private double _urgeToUrinateLevel = 1.0 ;
2024-04-05 19:24:26 +02:00
2024-04-06 00:57:29 +02:00
[SerializeField]
private double _happiness = 0.75 ;
2024-04-05 19:24:26 +02:00
[SerializeField, ShowOnly]
private bool _isSleeping = false ;
[SerializeField, ShowOnly]
private bool _isHyperactive = false ;
[SerializeField, ShowOnly]
private bool _isOvercaffeinated = false ;
[SerializeField]
private DeveloperNeeds _developerNeeds ;
2024-04-04 18:13:08 +02:00
/// <summary>
2024-04-05 19:24:26 +02:00
/// Gibt die Grunddaten des Entwicklers zurück.
2024-04-04 18:13:08 +02:00
/// </summary>
2024-04-05 19:24:26 +02:00
public DeveloperStats BaseStats = > _baseStats ;
2024-04-04 18:13:08 +02:00
/// <summary>
/// Gibt die Anzahl der Finger zurück.
/// </summary>
public int FingersLeft = > _fingersLeft ;
/// <summary>
/// Gibt die aktuelle Effizienz des Entwicklers in Prozent zurück.
/// </summary>
2024-04-05 19:24:26 +02:00
public double CurrentEfficiency = > _currentEfficiency ;
2024-04-04 18:13:08 +02:00
public string Name = > _name ;
2024-04-05 19:24:26 +02:00
2024-04-06 00:57:29 +02:00
[Serializable, Flags]
public enum WantedConsumable
{
None ,
Food = 0x01 ,
Drink = 0x02 ,
Coffee = Drink | 0x04 ,
Mate = Drink | 0x08 ,
Pizza = Food | 0x10
}
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _caffeineNeed ;
2024-04-06 00:57:29 +02:00
[SerializeField] private WantedConsumable _wantedDrink ;
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _hungerNeed ;
2024-04-06 00:57:29 +02:00
[SerializeField] private WantedConsumable _wantedFood ;
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _toiletNeed ;
void Start ( )
{
_developerNeeds = gameObject . GetComponent < DeveloperNeeds > ( ) ;
_fingersLeft = _baseStats . Fingers ;
}
2024-04-06 00:57:29 +02:00
[ContextMenu("Give Coffee")]
private void TestCoffee ( )
2024-04-05 19:24:26 +02:00
{
2024-04-06 00:57:29 +02:00
GiveDrink ( 0.3 , WantedConsumable . Coffee ) ;
}
[ContextMenu("Give Mate")]
private void TestMate ( )
{
GiveDrink ( 0.3 , WantedConsumable . Mate ) ;
2024-04-05 19:24:26 +02:00
}
[ContextMenu("Give Food")]
private void TestFood ( )
{
2024-04-06 00:57:29 +02:00
GiveFood ( 0.3 , WantedConsumable . Pizza ) ;
2024-04-05 19:24:26 +02:00
}
2024-04-04 18:13:08 +02:00
2024-04-05 19:24:26 +02:00
[ContextMenu("Drain Bladder")]
private void TestPee ( )
{
2024-04-06 00:57:29 +02:00
Pee ( 0.3 , true ) ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
public void GiveDrink ( double caffeineAmount , WantedConsumable drinkType )
2024-04-05 19:24:26 +02:00
{
2024-04-06 00:57:29 +02:00
if ( ! drinkType . HasFlag ( WantedConsumable . Drink ) )
throw new ArgumentException ( nameof ( drinkType ) ,
$"{nameof(drinkType)} must be a value with the \" { WantedConsumable . Drink } \ " flag" ) ;
_caffeineLevel = Math . Min ( _caffeineLevel + caffeineAmount , 2.0 ) ;
2024-04-05 19:24:26 +02:00
if ( _caffeineNeed ! = null & & _caffeineLevel > GameManager . Instance . NeedNotificationThreshold )
{
NeedFullfilled ( _caffeineNeed ) ;
2024-04-06 00:57:29 +02:00
_caffeineNeed = null ;
}
if ( _wantedDrink ! = WantedConsumable . None )
{
// TODO: Wie wäre es damit, das nicht fest zu coden?
if ( drinkType = = _wantedDrink )
_happiness + = 0.2 ;
else
_happiness - = 0.2 ;
}
else
{
_happiness + = 0.2 ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
_wantedDrink = WantedConsumable . None ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
public void GiveFood ( double foodAmount , WantedConsumable foodType )
2024-04-05 19:24:26 +02:00
{
2024-04-06 00:57:29 +02:00
if ( ! foodType . HasFlag ( WantedConsumable . Food ) )
throw new ArgumentException ( nameof ( foodType ) ,
$"{nameof(foodType)} must be a value with the \" { WantedConsumable . Food } \ " flag" ) ;
_hungerLevel = Math . Min ( _hungerLevel + foodAmount , 1.0 ) ;
2024-04-05 19:24:26 +02:00
if ( _hungerNeed ! = null & & _hungerLevel > GameManager . Instance . NeedNotificationThreshold )
{
NeedFullfilled ( _hungerNeed ) ;
2024-04-06 00:57:29 +02:00
_hungerNeed = null ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
if ( _wantedFood ! = WantedConsumable . None )
{
if ( foodType = = _wantedFood )
_happiness + = 0.2 ;
else
_happiness - = 0.2 ;
}
else
{
_happiness + = 0.2 ;
}
_wantedFood = WantedConsumable . None ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
/// <summary>
/// Der Entwickler Pinkelt die angegebene Menge aus.
/// </summary>
/// <param name="peeAmount">Die Menge die ausgepinkelt wird.</param>
/// <param name="onToilet">Wenn true, dann pinkelt der Entwickler auf einer Toilette. Wenn false, dann pinkelt er auf den Boden (was schlecht ist)</param>
public void Pee ( double peeAmount , bool onToilet )
2024-04-05 19:24:26 +02:00
{
2024-04-06 00:57:29 +02:00
_urgeToUrinateLevel = Math . Min ( _urgeToUrinateLevel + = peeAmount , 1.0 ) ;
2024-04-05 19:24:26 +02:00
2024-04-05 19:32:50 +02:00
if ( _toiletNeed ! = null & & _urgeToUrinateLevel > GameManager . Instance . NeedNotificationThreshold )
2024-04-05 19:24:26 +02:00
{
NeedFullfilled ( _toiletNeed ) ;
2024-04-06 00:57:29 +02:00
_toiletNeed = null ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
if ( onToilet )
_happiness + = 0.2 ;
else
_happiness - = 0.2 ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
public void UpdateStats ( double caffeineDrain , double hungerDrain , double urinationDrain , double happinessDrain )
2024-04-05 19:24:26 +02:00
{
_caffeineLevel - = caffeineDrain * _baseStats . CaffeineDrainFactor ;
_hungerLevel - = hungerDrain * _baseStats . HungerDrainFactor ;
2024-04-05 19:32:50 +02:00
_urgeToUrinateLevel - = urinationDrain * _baseStats . UrinationDrainFactor ;
2024-04-06 00:57:29 +02:00
_happiness - = happinessDrain * _baseStats . UrinationDrainFactor ;
2024-04-05 19:24:26 +02:00
2024-04-06 00:57:29 +02:00
_caffeineLevel = Math . Max ( _caffeineLevel , 0.0 ) ;
_hungerLevel = Math . Max ( _hungerLevel , 0.0 ) ;
_urgeToUrinateLevel = Math . Max ( _urgeToUrinateLevel , 0.0 ) ;
_happiness = Math . Max ( _happiness , 0.0 ) ;
2024-04-05 19:24:26 +02:00
_isHyperactive = _caffeineLevel > 1.0 ;
_isOvercaffeinated = _caffeineLevel > 1.5 ;
_isSleeping = _caffeineLevel < = 0.0 ;
if ( _caffeineLevel < GameManager . Instance . NeedNotificationThreshold & & _caffeineNeed = = null )
{
2024-04-06 00:57:29 +02:00
// TODO: Wir können hier anhand von Präferenz gewichten.
if ( Random . Range ( 0.0f , 1.0f ) > 0.5f )
{
_caffeineNeed = _developerNeeds . SpawnMateNeed ( ) ;
_wantedDrink = WantedConsumable . Mate ;
}
else
{
_caffeineNeed = _developerNeeds . SpawnCoffeeNeed ( ) ;
_wantedDrink = WantedConsumable . Coffee ;
}
2024-04-05 19:24:26 +02:00
}
if ( _hungerLevel < GameManager . Instance . NeedNotificationThreshold & & _hungerNeed = = null )
{
_hungerNeed = _developerNeeds . SpawnHungerNeed ( ) ;
2024-04-06 00:57:29 +02:00
_wantedFood = WantedConsumable . Pizza ;
2024-04-05 19:24:26 +02:00
}
2024-04-05 19:32:50 +02:00
if ( _urgeToUrinateLevel < GameManager . Instance . NeedNotificationThreshold & & _toiletNeed = = null )
2024-04-05 19:24:26 +02:00
{
// TODO: Go to toilet
Debug . Log ( "Ich muss aufs Klo!" ) ;
_toiletNeed = _developerNeeds . SpawnToiletNeed ( ) ;
}
if ( _hungerLevel < = 0.0 )
{
Die ( ) ;
}
}
private void NeedFullfilled ( GameObject needObject )
{
Instantiate ( GameManager . Instance . NeedFullfilledParticleEffect , needObject . transform . position , needObject . transform . rotation ) ;
Destroy ( needObject ) ;
}
2024-04-04 16:33:12 +02:00
public void UpdateEfficiency ( )
{
2024-04-06 00:57:29 +02:00
_currentEfficiency = _baseStats . BaseEfficiency * ( _fingersLeft / 10.0 ) * CalculateCaffeineEfficiency ( ) * CalculateHungerEfficiency ( ) * CalculateUrinationEfficiency ( ) * CalculateHappinessEfficiency ( ) ;
2024-04-04 18:13:08 +02:00
}
2024-04-05 19:24:26 +02:00
// TODO: Es könnte sich als schwierig erweisen, die Effizienz hoch zu halten.
2024-04-06 00:57:29 +02:00
// Man könnte ggf. die Funktionen so anpassen, dass sie eine ganze Weile 100% Effizienz geben.
// TODO: Das auch nur ansatzweise zu balancieren wird wiztig 🤯
2024-04-05 19:24:26 +02:00
private double CalculateCaffeineEfficiency ( )
{
if ( _isSleeping )
return 0.0 ;
// Die Formel hat schon recht vielversprechendes Verhalten im Bereich > 1
//if (_isHyperactive)
// return 1.0 + (_caffeineLevel - 1.0) * (_caffeineLevel - 1.0);
if ( _isOvercaffeinated )
return 0.0 ;
// https://easings.net/#easeOutCubic
return 1.0 - Math . Pow ( 1.0 - _caffeineLevel , 3.0 ) ;
}
private double CalculateHungerEfficiency ( )
{
2024-04-06 00:57:29 +02:00
if ( _hungerLevel > 1.0 )
return 1.0 ;
2024-04-05 19:24:26 +02:00
// https://easings.net/#easeOutCirc
2024-04-06 00:57:29 +02:00
return Math . Sqrt ( 1.0 - Math . Pow ( _hungerLevel - 1.0 , 2.0 ) ) ;
2024-04-05 19:24:26 +02:00
}
private double CalculateUrinationEfficiency ( )
{
2024-04-06 00:57:29 +02:00
if ( _urgeToUrinateLevel > 1.0 )
return 1.0 ;
2024-04-05 19:24:26 +02:00
// https://easings.net/#easeOutExpo
2024-04-05 19:32:50 +02:00
return Math . Abs ( _urgeToUrinateLevel - 1.0 ) < 0.0001f ? 1.0 : 1.0 - Math . Pow ( 2 , - 10 * _urgeToUrinateLevel ) ;
2024-04-05 19:24:26 +02:00
}
2024-04-06 00:57:29 +02:00
private double CalculateHappinessEfficiency ( )
{
// 50% bei 0 Happiness, 100% bei 0.75 Happiness, 125% bei 1 Happiness
return 0.5 + _happiness * 2.0 / 3.0 ;
}
2024-04-04 18:13:08 +02:00
/// <summary>
/// Der Entwickler wird verletzt und der Idiot bricht sich ausgerechnet einen Finger...
/// </summary>
public void Hurt ( )
{
_fingersLeft - - ;
// Ob er stirbt oder nicht, für uns hat er auf jeden Fall seinen Nutzen verloren.
if ( _fingersLeft = = 0 )
Die ( ) ;
}
private void Die ( )
{
Debug . Log ( $"{Name} ist verreckt." ) ;
2024-04-04 16:33:12 +02:00
}
2024-04-04 15:26:31 +02:00
}