GameVsJam/3d Prototyp/Assets/Scripts/Developer.cs

460 lines
14 KiB
C#
Raw Normal View History

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 System.ComponentModel;
2024-04-04 15:26:31 +02:00
using UnityEngine;
using UnityEngine.Serialization;
using Utility;
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;
public double UrinationDrainFactor;
public double HappinessDrainFactor;
2024-04-05 19:24:26 +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;
HappinessDrainFactor = happinessDrainFactor;
CoffeePreference = coffeePreference;
MatePreference = matePreference;
2024-04-05 19:24:26 +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-07 00:09:53 +02:00
[Header("Basis Informationen")]
[SerializeField]
private string _name;
[SerializeField]
2024-04-05 19:24:26 +02:00
private DeveloperStats _baseStats = DeveloperStats.Default;
2024-04-07 00:09:53 +02:00
[SerializeField]
private float _talkRange = 2.0f;
[Header("Aktuelle Informationen")]
2024-04-05 19:32:50 +02:00
[SerializeField, ShowOnly]
2024-04-05 19:24:26 +02:00
private double _currentEfficiency = 1.0;
[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
[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;
/// <summary>
2024-04-05 19:24:26 +02:00
/// Gibt die Grunddaten des Entwicklers zurück.
/// </summary>
2024-04-05 19:24:26 +02:00
public DeveloperStats BaseStats => _baseStats;
/// <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;
public string Name => _name;
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _caffeineNeed;
[SerializeField] private WantedConsumable _wantedDrink;
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _hungerNeed;
[SerializeField] private WantedConsumable _wantedFood;
2024-04-05 19:24:26 +02:00
[SerializeField]
private GameObject _toiletNeed;
private AudioSource _audioSource;
private float _talkPauseTime = 15.0f;
[SerializeField, ShowOnly]
private float _talkTimer;
2024-04-06 20:02:57 +02:00
private bool _hasTalkedWhileHyperactive = false;
private bool _hasTalkedWhileOvercaffeinated = false;
private List<GameObject> _needList = new List<GameObject>();
2024-04-05 19:24:26 +02:00
void Start()
{
_developerNeeds = gameObject.GetComponent<DeveloperNeeds>();
_audioSource = GetComponent<AudioSource>();
_talkTimer = UnityEngine.Random.Range(5.0f, 15.0f);
2024-04-05 19:24:26 +02:00
_fingersLeft = _baseStats.Fingers;
}
private void Update()
{
if (!_audioSource.isPlaying)
{
_talkTimer -= Time.deltaTime;
if (_talkTimer < 0.0f)
{
_talkTimer = _talkPauseTime;
TalkIfInRange();
}
}
}
[ContextMenu("Give Coffee")]
private void TestCoffee()
2024-04-05 19:24:26 +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()
{
GiveFood(0.3, WantedConsumable.Pizza);
2024-04-05 19:24:26 +02:00
}
2024-04-05 19:24:26 +02:00
[ContextMenu("Drain Bladder")]
private void TestPee()
{
Pee(0.3, true);
2024-04-05 19:24:26 +02:00
}
public void GiveDrink(double caffeineAmount, WantedConsumable drinkType)
2024-04-05 19:24:26 +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)
{
UpdateNeedPositions(_caffeineNeed);
_needList.Remove(_caffeineNeed);
2024-04-05 19:24:26 +02:00
NeedFullfilled(_caffeineNeed);
_caffeineNeed = null;
}
if (_wantedDrink != WantedConsumable.None)
{
// TODO: Wie wäre es damit, das nicht fest zu coden?
if (drinkType == _wantedDrink)
2024-04-06 20:02:57 +02:00
{
Talk($"The Devolper thanks Gottfried for the {drinkType.GetAsString()}");
_happiness += 0.2;
2024-04-06 20:02:57 +02:00
}
else
2024-04-06 20:02:57 +02:00
{
Talk($"The Devolper blames Gottfried for bringing {drinkType.GetAsString()} but he actaully wanted a {_wantedDrink.GetAsString()}");
_happiness -= 0.2;
2024-04-06 20:02:57 +02:00
}
}
else
{
_happiness += 0.2;
2024-04-05 19:24:26 +02:00
}
_wantedDrink = WantedConsumable.None;
2024-04-05 19:24:26 +02:00
}
public void GiveFood(double foodAmount, WantedConsumable foodType)
2024-04-05 19:24:26 +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)
{
UpdateNeedPositions(_hungerNeed);
_needList.Remove(_hungerNeed);
2024-04-05 19:24:26 +02:00
NeedFullfilled(_hungerNeed);
_hungerNeed = null;
2024-04-06 20:02:57 +02:00
Talk("The Developer thanks Gottfried for the Pizza");
2024-04-05 19:24:26 +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
}
/// <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
{
_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
{
UpdateNeedPositions(_toiletNeed);
_needList.Remove(_toiletNeed);
2024-04-05 19:24:26 +02:00
NeedFullfilled(_toiletNeed);
_toiletNeed = null;
2024-04-06 20:02:57 +02:00
Talk("The Developer finally can go to the toilet");
2024-04-05 19:24:26 +02:00
}
if (onToilet)
_happiness += 0.2;
else
_happiness -= 0.2;
2024-04-05 19:24:26 +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;
_happiness -= happinessDrain * _baseStats.UrinationDrainFactor;
2024-04-05 19:24:26 +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
2024-04-06 20:02:57 +02:00
_isHyperactive = _caffeineLevel > 1.0 && _caffeineLevel <= 1.5;
2024-04-05 19:24:26 +02:00
_isOvercaffeinated = _caffeineLevel > 1.5;
_isSleeping = _caffeineLevel <= 0.0;
2024-04-06 20:02:57 +02:00
2024-04-05 19:24:26 +02:00
if (_caffeineLevel < GameManager.Instance.NeedNotificationThreshold && _caffeineNeed == null)
{
// TODO: Wir können hier anhand von Präferenz gewichten.
if (Random.Range(0.0f, 1.0f) > 0.5f)
{
_caffeineNeed = _developerNeeds.SpawnMateNeed(_needList.Count);
_wantedDrink = WantedConsumable.Mate;
_needList.Add(_caffeineNeed);
}
else
{
_caffeineNeed = _developerNeeds.SpawnCoffeeNeed(_needList.Count);
_wantedDrink = WantedConsumable.Coffee;
_needList.Add(_caffeineNeed);
}
2024-04-05 19:24:26 +02:00
}
if (_hungerLevel < GameManager.Instance.NeedNotificationThreshold && _hungerNeed == null)
{
_hungerNeed = _developerNeeds.SpawnHungerNeed(_needList.Count);
_wantedFood = WantedConsumable.Pizza;
_needList.Add(_hungerNeed);
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(_needList.Count);
_needList.Add(_toiletNeed);
2024-04-05 19:24:26 +02:00
}
if (_hungerLevel <= 0.0)
{
Die();
}
}
private void UpdateNeedPositions(GameObject reference)
{
int referenceIndex = _needList.IndexOf(reference);
if (referenceIndex == -1)
{
Debug.LogError("Reference GameObject not in List.");
return;
}
for (int i = referenceIndex + 1; i < _needList.Count; i++)
{
GameObject currentItem = _needList[i];
currentItem.GetComponent<SpinningSpinner>().originalY -= 0.5f;
}
}
2024-04-05 19:24:26 +02:00
private void NeedFullfilled(GameObject needObject)
{
Instantiate(GameManager.Instance.NeedFullfilledParticleEffect, needObject.transform.position, needObject.transform.rotation);
Destroy(needObject);
}
public void UpdateEfficiency()
{
_currentEfficiency = _baseStats.BaseEfficiency * (_fingersLeft / 10.0) * CalculateCaffeineEfficiency() * CalculateHungerEfficiency() * CalculateUrinationEfficiency() * CalculateHappinessEfficiency();
}
2024-04-05 19:24:26 +02:00
// 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.
// 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;
2024-04-06 20:02:57 +02:00
if (_isHyperactive)
{
if (!_hasTalkedWhileHyperactive)
{
Talk("The Developer is surprisingly productive right now duo to caffeine");
_hasTalkedWhileHyperactive = true;
}
}
else
{
_hasTalkedWhileHyperactive = false;
}
2024-04-05 19:24:26 +02:00
if (_isOvercaffeinated)
2024-04-06 20:02:57 +02:00
{
if (!_hasTalkedWhileOvercaffeinated)
{
Talk("The Developer drank too much caffeine, The Developer needs a break immediately");
_hasTalkedWhileOvercaffeinated = true;
}
2024-04-05 19:24:26 +02:00
return 0.0;
2024-04-06 20:02:57 +02:00
}
else
{
_hasTalkedWhileOvercaffeinated = false;
}
2024-04-05 19:24:26 +02:00
// https://easings.net/#easeOutCubic
return 1.0 - Math.Pow(1.0 - _caffeineLevel, 3.0);
}
private double CalculateHungerEfficiency()
{
if (_hungerLevel > 1.0)
return 1.0;
2024-04-05 19:24:26 +02:00
// https://easings.net/#easeOutCirc
return Math.Sqrt(1.0 - Math.Pow(_hungerLevel - 1.0, 2.0));
2024-04-05 19:24:26 +02:00
}
private double CalculateUrinationEfficiency()
{
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
}
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>
/// 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.");
}
private bool IsGottfriedInRange()
{
float distanceToPlayer = Vector3.Distance(transform.position, GameManager.Instance.PlayerTransform.position);
return distanceToPlayer <= _talkRange;
}
/// <summary>
/// Starts talking when player is near to the Dev.
/// </summary>
public void TalkIfInRange()
{
if (IsGottfriedInRange())
{
Talk();
}
}
/// <summary>
/// Dev starts talking.
/// </summary>
2024-04-06 20:02:57 +02:00
public void Talk(string context = null)
{
2024-04-06 20:02:57 +02:00
if (context == null)
{
context = GameManager.Instance.GetContextAsString();
}
if (!_audioSource.isPlaying)
{
GetComponent<Text2Speech>().Generate(context);
}
}
2024-04-04 15:26:31 +02:00
}