Happiness is not a destination, it's a way of life ☀️

This commit is contained in:
Simon Lübeß 2024-04-06 00:57:29 +02:00
parent 3f907d13b9
commit adac70b9a5
4 changed files with 141 additions and 24 deletions

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization; using UnityEngine.Serialization;
using Utility; using Utility;
using Random = UnityEngine.Random;
[Serializable] [Serializable]
public struct DeveloperStats public struct DeveloperStats
@ -13,18 +14,26 @@ public struct DeveloperStats
public double CaffeineDrainFactor; public double CaffeineDrainFactor;
public double HungerDrainFactor; public double HungerDrainFactor;
[FormerlySerializedAs("UrgeToUrinateFillFactor")] public double UrinationDrainFactor; public double UrinationDrainFactor;
public double HappinessDrainFactor;
public DeveloperStats(double baseEfficiency, int fingers, double caffeineDrainFactor, double hungerDrainFactor, double urinationDrainFactor) // 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)
{ {
BaseEfficiency = baseEfficiency; BaseEfficiency = baseEfficiency;
Fingers = fingers; Fingers = fingers;
CaffeineDrainFactor = caffeineDrainFactor; CaffeineDrainFactor = caffeineDrainFactor;
HungerDrainFactor = hungerDrainFactor; HungerDrainFactor = hungerDrainFactor;
UrinationDrainFactor = urinationDrainFactor; UrinationDrainFactor = urinationDrainFactor;
HappinessDrainFactor = happinessDrainFactor;
CoffeePreference = coffeePreference;
MatePreference = matePreference;
} }
public static readonly DeveloperStats Default = new DeveloperStats(1.0, 10, 1, 1, 1); public static readonly DeveloperStats Default = new DeveloperStats(1.0, 10, 1, 1, 1, 1, 0.5, 0.5);
} }
public class Developer : MonoBehaviour public class Developer : MonoBehaviour
@ -49,6 +58,9 @@ public class Developer : MonoBehaviour
[SerializeField] [SerializeField]
private double _urgeToUrinateLevel = 1.0; private double _urgeToUrinateLevel = 1.0;
[SerializeField]
private double _happiness = 0.75;
[SerializeField, ShowOnly] [SerializeField, ShowOnly]
private bool _isSleeping = false; private bool _isSleeping = false;
@ -78,10 +90,27 @@ public class Developer : MonoBehaviour
public string Name => _name; public string Name => _name;
[Serializable, Flags]
public enum WantedConsumable
{
None,
Food = 0x01,
Drink = 0x02,
Coffee = Drink | 0x04,
Mate = Drink | 0x08,
Pizza = Food | 0x10
}
[SerializeField] [SerializeField]
private GameObject _caffeineNeed; private GameObject _caffeineNeed;
[SerializeField] private WantedConsumable _wantedDrink;
[SerializeField] [SerializeField]
private GameObject _hungerNeed; private GameObject _hungerNeed;
[SerializeField] private WantedConsumable _wantedFood;
[SerializeField] [SerializeField]
private GameObject _toiletNeed; private GameObject _toiletNeed;
@ -92,63 +121,121 @@ public class Developer : MonoBehaviour
_fingersLeft = _baseStats.Fingers; _fingersLeft = _baseStats.Fingers;
} }
[ContextMenu("Give Drink")] [ContextMenu("Give Coffee")]
private void TestDrink() private void TestCoffee()
{ {
GiveDrink(0.25); GiveDrink(0.3, WantedConsumable.Coffee);
}
[ContextMenu("Give Mate")]
private void TestMate()
{
GiveDrink(0.3, WantedConsumable.Mate);
} }
[ContextMenu("Give Food")] [ContextMenu("Give Food")]
private void TestFood() private void TestFood()
{ {
GiveFood(0.25); GiveFood(0.3, WantedConsumable.Pizza);
} }
[ContextMenu("Drain Bladder")] [ContextMenu("Drain Bladder")]
private void TestPee() private void TestPee()
{ {
Pee(0.25); Pee(0.3, true);
} }
public void GiveDrink(double caffeineAmount) public void GiveDrink(double caffeineAmount, WantedConsumable drinkType)
{ {
_caffeineLevel += caffeineAmount; 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);
if (_caffeineNeed != null && _caffeineLevel > GameManager.Instance.NeedNotificationThreshold) if (_caffeineNeed != null && _caffeineLevel > GameManager.Instance.NeedNotificationThreshold)
{ {
NeedFullfilled(_caffeineNeed); NeedFullfilled(_caffeineNeed);
} _caffeineNeed = null;
} }
public void GiveFood(double foodAmount) if (_wantedDrink != WantedConsumable.None)
{ {
_hungerLevel += foodAmount; // 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;
}
_wantedDrink = WantedConsumable.None;
}
public void GiveFood(double foodAmount, WantedConsumable foodType)
{
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);
if (_hungerNeed != null && _hungerLevel > GameManager.Instance.NeedNotificationThreshold) if (_hungerNeed != null && _hungerLevel > GameManager.Instance.NeedNotificationThreshold)
{ {
NeedFullfilled(_hungerNeed); NeedFullfilled(_hungerNeed);
} _hungerNeed = null;
} }
public void Pee(double peeAmount) if (_wantedFood != WantedConsumable.None)
{ {
_urgeToUrinateLevel += peeAmount; if (foodType == _wantedFood)
_happiness += 0.2;
else
_happiness -= 0.2;
}
else
{
_happiness += 0.2;
}
_wantedFood = WantedConsumable.None;
}
/// <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)
{
_urgeToUrinateLevel = Math.Min(_urgeToUrinateLevel += peeAmount, 1.0);
if (_toiletNeed != null && _urgeToUrinateLevel > GameManager.Instance.NeedNotificationThreshold) if (_toiletNeed != null && _urgeToUrinateLevel > GameManager.Instance.NeedNotificationThreshold)
{ {
NeedFullfilled(_toiletNeed); NeedFullfilled(_toiletNeed);
} _toiletNeed = null;
} }
public void UpdateStats(double caffeineDrain, double hungerDrain, double urinationDrain) if (onToilet)
_happiness += 0.2;
else
_happiness -= 0.2;
}
public void UpdateStats(double caffeineDrain, double hungerDrain, double urinationDrain, double happinessDrain)
{ {
_caffeineLevel -= caffeineDrain * _baseStats.CaffeineDrainFactor; _caffeineLevel -= caffeineDrain * _baseStats.CaffeineDrainFactor;
_hungerLevel -= hungerDrain * _baseStats.HungerDrainFactor; _hungerLevel -= hungerDrain * _baseStats.HungerDrainFactor;
_urgeToUrinateLevel -= urinationDrain * _baseStats.UrinationDrainFactor; _urgeToUrinateLevel -= urinationDrain * _baseStats.UrinationDrainFactor;
_happiness -= happinessDrain * _baseStats.UrinationDrainFactor;
_caffeineLevel = Math.Clamp(_caffeineLevel, 0.0, 2.0); _caffeineLevel = Math.Max(_caffeineLevel, 0.0);
_hungerLevel = Math.Clamp(_hungerLevel, 0.0, 1.0); _hungerLevel = Math.Max(_hungerLevel, 0.0);
_urgeToUrinateLevel = Math.Clamp(_urgeToUrinateLevel, 0.0, 1.0); _urgeToUrinateLevel = Math.Max(_urgeToUrinateLevel, 0.0);
_happiness = Math.Max(_happiness, 0.0);
_isHyperactive = _caffeineLevel > 1.0; _isHyperactive = _caffeineLevel > 1.0;
_isOvercaffeinated = _caffeineLevel > 1.5; _isOvercaffeinated = _caffeineLevel > 1.5;
@ -156,12 +243,24 @@ public class Developer : MonoBehaviour
if (_caffeineLevel < GameManager.Instance.NeedNotificationThreshold && _caffeineNeed == null) if (_caffeineLevel < GameManager.Instance.NeedNotificationThreshold && _caffeineNeed == null)
{ {
_caffeineNeed = _developerNeeds.SpawnCaffeineNeed(); // 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;
}
} }
if (_hungerLevel < GameManager.Instance.NeedNotificationThreshold && _hungerNeed == null) if (_hungerLevel < GameManager.Instance.NeedNotificationThreshold && _hungerNeed == null)
{ {
_hungerNeed = _developerNeeds.SpawnHungerNeed(); _hungerNeed = _developerNeeds.SpawnHungerNeed();
_wantedFood = WantedConsumable.Pizza;
} }
if (_urgeToUrinateLevel < GameManager.Instance.NeedNotificationThreshold && _toiletNeed == null) if (_urgeToUrinateLevel < GameManager.Instance.NeedNotificationThreshold && _toiletNeed == null)
@ -187,11 +286,12 @@ public class Developer : MonoBehaviour
public void UpdateEfficiency() public void UpdateEfficiency()
{ {
_currentEfficiency = _baseStats.BaseEfficiency * (_fingersLeft / 10.0) * CalculateCaffeineEfficiency() * CalculateHungerEfficiency() * CalculateUrinationEfficiency(); _currentEfficiency = _baseStats.BaseEfficiency * (_fingersLeft / 10.0) * CalculateCaffeineEfficiency() * CalculateHungerEfficiency() * CalculateUrinationEfficiency() * CalculateHappinessEfficiency();
} }
// TODO: Es könnte sich als schwierig erweisen, die Effizienz hoch zu halten. // TODO: Es könnte sich als schwierig erweisen, die Effizienz hoch zu halten.
// Man könnte ggf. die Funktionen so anpassen, dass sie eine ganze Weile 100% Effizienz geben. // 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 🤯
private double CalculateCaffeineEfficiency() private double CalculateCaffeineEfficiency()
{ {
@ -211,16 +311,28 @@ public class Developer : MonoBehaviour
private double CalculateHungerEfficiency() private double CalculateHungerEfficiency()
{ {
if (_hungerLevel > 1.0)
return 1.0;
// https://easings.net/#easeOutCirc // https://easings.net/#easeOutCirc
return Math.Sqrt(1.0 - Math.Pow(_caffeineLevel - 1.0, 2.0)); return Math.Sqrt(1.0 - Math.Pow(_hungerLevel - 1.0, 2.0));
} }
private double CalculateUrinationEfficiency() private double CalculateUrinationEfficiency()
{ {
if (_urgeToUrinateLevel > 1.0)
return 1.0;
// https://easings.net/#easeOutExpo // https://easings.net/#easeOutExpo
return Math.Abs(_urgeToUrinateLevel - 1.0) < 0.0001f ? 1.0 : 1.0 - Math.Pow(2, -10 * _urgeToUrinateLevel); return Math.Abs(_urgeToUrinateLevel - 1.0) < 0.0001f ? 1.0 : 1.0 - Math.Pow(2, -10 * _urgeToUrinateLevel);
} }
private double CalculateHappinessEfficiency()
{
// 50% bei 0 Happiness, 100% bei 0.75 Happiness, 125% bei 1 Happiness
return 0.5 + _happiness * 2.0 / 3.0;
}
/// <summary> /// <summary>
/// Der Entwickler wird verletzt und der Idiot bricht sich ausgerechnet einen Finger... /// Der Entwickler wird verletzt und der Idiot bricht sich ausgerechnet einen Finger...
/// </summary> /// </summary>

View File

@ -25,7 +25,8 @@ public class DeveloperNeeds : MonoBehaviour
// TODO: Enums statt strings verwenden // TODO: Enums statt strings verwenden
// TODO: Multiple Needs möglich übereinander anzeigen? // TODO: Multiple Needs möglich übereinander anzeigen?
public GameObject SpawnCaffeineNeed() => spawnNeed(Random.Range(0.0f, 1.0f) < 0.5f ? "coffee" : "mate"); public GameObject SpawnCoffeeNeed() => spawnNeed("coffee");
public GameObject SpawnMateNeed() => spawnNeed("mate");
public GameObject SpawnToiletNeed() => spawnNeed("toilet"); public GameObject SpawnToiletNeed() => spawnNeed("toilet");
public GameObject SpawnHungerNeed() => spawnNeed("hunger"); public GameObject SpawnHungerNeed() => spawnNeed("hunger");
public GameObject SpawnMoneyNeed() => spawnNeed("money"); public GameObject SpawnMoneyNeed() => spawnNeed("money");

View File

@ -147,13 +147,15 @@ public partial class GameManager : MonoBehaviourSingleton<GameManager>
{ {
double developerEfficiency = 0.0f; double developerEfficiency = 0.0f;
// TODO: Und eine weitere absolut nicht handle-bare variable...
double caffeineDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.CaffeineDrainScaling * GameProgress); double caffeineDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.CaffeineDrainScaling * GameProgress);
double hungerDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.HungerDrainScaling * GameProgress); double hungerDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.HungerDrainScaling * GameProgress);
double urinationDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.UrinationDrainScaling * GameProgress); double urinationDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.UrinationDrainScaling * GameProgress);
double happinessDrain = _generalNeedDrainScaling * Math.Pow(2, _difficultySettings.HappinessDrainScaling * GameProgress);
foreach (Developer developer in _developers) foreach (Developer developer in _developers)
{ {
developer.UpdateStats(caffeineDrain, hungerDrain, urinationDrain); developer.UpdateStats(caffeineDrain, hungerDrain, urinationDrain, happinessDrain);
developer.UpdateEfficiency(); developer.UpdateEfficiency();
developerEfficiency += developer.CurrentEfficiency; developerEfficiency += developer.CurrentEfficiency;
} }

View File

@ -22,6 +22,7 @@ namespace Utility
public double CaffeineDrainScaling { get; protected set; } public double CaffeineDrainScaling { get; protected set; }
public double HungerDrainScaling { get; protected set; } public double HungerDrainScaling { get; protected set; }
public double UrinationDrainScaling { get; protected set; } public double UrinationDrainScaling { get; protected set; }
public double HappinessDrainScaling { get; protected set; }
} }
public class EasyDifficulty : DifficultySettings public class EasyDifficulty : DifficultySettings
@ -42,6 +43,7 @@ namespace Utility
CaffeineDrainScaling = 1.5; CaffeineDrainScaling = 1.5;
HungerDrainScaling = 1.5; HungerDrainScaling = 1.5;
UrinationDrainScaling = 1.5; UrinationDrainScaling = 1.5;
HappinessDrainScaling = 1.5;
} }
} }