using System; using System.Collections.Generic; using System.Linq; using TMPro; using UnityEngine; namespace ManifoldGardenArchipelago { public class NotificationManager : MonoBehaviour { public static NotificationManager InitializeNotifications(UIManager uiManager) { GameObject gameObject = new("Notifications"); gameObject.transform.parent = uiManager.transform; Vector3 groupPosition = new(1260f, 680f, 0f); gameObject.transform.set_position_Injected(ref groupPosition); NotificationManager notificationManager = gameObject.AddComponent(); Plugin.Logger.LogInfo("Notification manager initialized"); return notificationManager; } public const int MAX_VISIBLE = 5; private readonly List _notifications = []; private readonly List _queuedMessages = []; private int _counter = 0; void Update() { if (_notifications.Count > 0 && _notifications[_notifications.Count - 1].ReadyToBeDestroyed()) { NotificationLine toDestroy = _notifications[_notifications.Count - 1]; GameObject.Destroy(toDestroy.gameObject); _notifications.RemoveAt(_notifications.Count - 1); } if (_notifications.Count > 0 && _notifications[_notifications.Count - 1].IsIdling()) { _notifications[_notifications.Count - 1].StopIdling(); } if (_queuedMessages.Count > 0) { if (_notifications.Count > 0 && !_notifications[0].ReadyToBeSurplanted()) { if (!_notifications[0].IsMoving() && _notifications.Count(n => !n.IsFadingOut()) < MAX_VISIBLE) { foreach (var notification in _notifications) { notification.SlideDown(); } } } else { foreach (var notification in _notifications) { notification.ConfirmMovement(); } GameObject newNotify = new($"Message_{_counter}"); _counter += 1; newNotify.transform.parent = transform; Vector3 freshPos = new(); newNotify.transform.set_localPosition_Injected(ref freshPos); TextMeshProUGUI textMeshPro = newNotify.AddComponent(); textMeshPro.alignment = TextAlignmentOptions.TopRight; textMeshPro.enableWordWrapping = false; textMeshPro.text = _queuedMessages[0]; textMeshPro.alpha = 0f; textMeshPro.autoSizeTextContainer = true; Vector2 pivot = new(1f, 0.5f); textMeshPro.rectTransform.set_pivot_Injected(ref pivot); _queuedMessages.RemoveAt(0); NotificationLine newLine = newNotify.AddComponent(); _notifications.Insert(0, newLine); } } } public void AddMessage(string message) { _queuedMessages.Add(message); } } public class NotificationLine : MonoBehaviour { public const float ALPHA_SPEED = 1f; public const float SLIDE_SPEED = 75f; public const float SLIDE_DISPLACEMENT = 50f; private enum FadeState { FadingIn, Idling, Waiting, FadingOut, ReadyToDestroy, } private enum MoveState { Waiting, Moving, Ready, } private TextMeshProUGUI _textMeshPro; private float _alphaTarget = 1f; private FadeState _fadeState = FadeState.FadingIn; private float _waitTimer = 10f; private MoveState _moveState = MoveState.Waiting; private float _targetY = 0f; void Start() { _textMeshPro = GetComponent(); } void Update() { if (_textMeshPro.alpha != _alphaTarget) { float alpha = _textMeshPro.alpha; if (alpha < _alphaTarget) { alpha = Math.Min(_alphaTarget, alpha + ALPHA_SPEED * Time.deltaTime); } else { alpha = Math.Max(_alphaTarget, alpha - ALPHA_SPEED * Time.deltaTime); } _textMeshPro.alpha = alpha; } if (_fadeState == FadeState.FadingIn) { if (_textMeshPro.alpha >= _alphaTarget) { _fadeState = FadeState.Idling; } } else if (_fadeState == FadeState.Waiting) { _waitTimer -= ALPHA_SPEED * Time.deltaTime; if (_waitTimer < 0) { _fadeState = FadeState.FadingOut; _alphaTarget = 0f; } } else if (_fadeState == FadeState.FadingOut) { if (_textMeshPro.alpha <= _alphaTarget) { _fadeState = FadeState.ReadyToDestroy; } } if (_moveState == MoveState.Moving) { if (transform.localPosition.y > _targetY) { Vector3 localPos = transform.localPosition; localPos.y = Math.Max(_targetY, localPos.y - SLIDE_SPEED * Time.deltaTime); transform.set_localPosition_Injected(ref localPos); if (transform.localPosition.y <= _targetY) { _moveState = MoveState.Ready; } } } } public bool ReadyToBeSurplanted() => _moveState == MoveState.Ready; public bool IsMoving() => _moveState == MoveState.Moving; public void SlideDown() { _moveState = MoveState.Moving; _targetY = transform.localPosition.y - SLIDE_DISPLACEMENT; } public void ConfirmMovement() { _moveState = MoveState.Waiting; } public bool IsIdling() => _fadeState == FadeState.Idling; public void StopIdling() { _fadeState = FadeState.Waiting; } public bool IsFadingOut() => _fadeState == FadeState.FadingOut; public bool ReadyToBeDestroyed() => _fadeState == FadeState.ReadyToDestroy; } }