Files
2026-06-22 16:18:34 +02:00

119 lines
4.5 KiB
C#

using UnityEngine;
using System.Collections.Generic;
using DG.Tweening;
using Ashwild.Inventory;
using Ashwild.Player;
namespace Ashwild.UI
{
public class NotificationUI : MonoBehaviour
{
[Header("References")]
[SerializeField] private RectTransform container;
[SerializeField] private GameObject notificationPrefab;
[Header("Layout")]
[SerializeField] private float notificationHeight = 40f;
[SerializeField] private float spacing = 6f;
[SerializeField] private float startOffsetY = 20f;
[Header("Timing")]
[SerializeField] private float displayDuration = 4f;
[Header("Animation")]
[SerializeField] private float slideInDuration = 0.35f;
[SerializeField] private Ease slideInEase = Ease.OutBack;
[SerializeField] private float stackSlideDuration = 0.25f;
[SerializeField] private Ease stackSlideEase = Ease.OutQuad;
[Header("Colors")]
[SerializeField] private Color gainColor = new Color(0.45f, 1f, 0.45f); // picked up / added
[SerializeField] private Color lossColor = new Color(1f, 0.45f, 0.45f); // dropped / consumed
private readonly List<NotificationItemUI> activeNotifications = new List<NotificationItemUI>();
private void OnEnable()
{
PlayerEvents.ItemAdded += OnItemAdded;
PlayerEvents.ItemDropped += OnItemDropped;
PlayerEvents.ItemConsumed += OnItemConsumed;
PlayerEvents.FoodPlacedToCook += OnItemSpent;
PlayerEvents.FuelAdded += OnItemSpent;
}
private void OnDisable()
{
PlayerEvents.ItemAdded -= OnItemAdded;
PlayerEvents.ItemDropped -= OnItemDropped;
PlayerEvents.ItemConsumed -= OnItemConsumed;
PlayerEvents.FoodPlacedToCook -= OnItemSpent;
PlayerEvents.FuelAdded -= OnItemSpent;
}
private void OnItemAdded(ItemData item, int quantity) => Push(item, quantity);
private void OnItemDropped(ItemData item, int quantity) => Push(item, -quantity);
private void OnItemConsumed(ItemData item) => Push(item, -1);
// Food placed on a cooking station or fuel fed to it both leave the inventory by one.
private void OnItemSpent(ItemData item) => Push(item, -1);
private void Push(ItemData item, int signedQuantity)
{
if (item == null || signedQuantity == 0) return;
// Clean up destroyed notifications
activeNotifications.RemoveAll(n => n == null);
bool gain = signedQuantity > 0;
// Merge only with a notification for the same item AND same direction
for (int i = 0; i < activeNotifications.Count; i++)
{
if (activeNotifications[i] != null && activeNotifications[i].ItemData == item
&& activeNotifications[i].IsGain == gain)
{
activeNotifications[i].AddQuantity(signedQuantity, displayDuration);
return;
}
}
// Create new notification
GameObject go = Instantiate(notificationPrefab, container);
NotificationItemUI notification = go.GetComponent<NotificationItemUI>();
float targetY = startOffsetY + activeNotifications.Count * (notificationHeight + spacing);
RectTransform rt = notification.RectTransform;
rt.anchoredPosition = new Vector2(0f, targetY - 30f);
notification.Initialize(item, signedQuantity, gain ? gainColor : lossColor, displayDuration, slideInDuration, slideInEase);
// Slide in from below
rt.DOAnchorPosY(targetY, slideInDuration).SetEase(slideInEase);
activeNotifications.Add(notification);
}
private void LateUpdate()
{
// Clean up destroyed notifications and reposition remaining ones
int removed = activeNotifications.RemoveAll(n => n == null);
if (removed > 0)
RepositionAll();
}
private void RepositionAll()
{
for (int i = 0; i < activeNotifications.Count; i++)
{
if (activeNotifications[i] == null) continue;
float targetY = startOffsetY + i * (notificationHeight + spacing);
activeNotifications[i].RectTransform
.DOAnchorPosY(targetY, stackSlideDuration)
.SetEase(stackSlideEase);
}
}
}
}