using Cthangover.Core.Scenes;
using Cthangover.Core.UI.Event;
using Cthangover.Core.UI.Theming;
using Godot;
using Cthangover.Core.UI.Messages;
namespace Cthangover.Mods.Interface.Messages
{
///
/// Floating text message with auto-fade. Appears at a vertical position,
/// stays visible for 3 seconds, then fades over 2/Speed seconds. Self-
/// destructs (QueueFree) when fully transparent. Registers with
/// SceneEventController for per-frame updates; unregisters on tree exit
/// to prevent leaks. Speed affects only the fade duration, not the display
/// duration, so faster speed = shorter fade = message disappears more quickly.
///
public partial class UiMessage : Control, IOnUpdateEvent
{
[Export] private Label textField;
/// Tint of the message text; defaults to the warning colour.
[Export] public Color Color { get; set; } = UiTokens.Warning;
/// Fade speed multiplier — 1 is normal; higher values shorten the fade-out.
[Export] public float Speed { get; set; } = 1f;
private float timestamp;
///
/// The text content displayed by this message. Reads/writes the child
/// textField label directly.
///
public string Text
{
get => textField.Text;
set => textField.Text = value;
}
///
/// Initializes the message: positions it, sets text/speed/color, records
/// the start timestamp, and registers with the
/// for per-frame updates. Called by .
///
/// Vertical Y-offset in pixels.
/// The message text.
/// Fade speed multiplier.
/// The text color.
public void Setup(float pos, string text, float speed, Color color)
{
OffsetLeft = 50;
OffsetRight = -10;
Size = new Vector2(Size.X, 50);
Position = new Vector2(0, pos + 200);
Text = text;
Speed = speed;
Color = color;
timestamp = (float)Godot.Time.GetTicksMsec() / 1000f;
if (SceneContextNode.Instance != null)
{
var eventController = SceneContextNode.FindNode("EventController");
eventController?.AddUpdateEventListener(this);
}
}
/// Detaches from the event controller so a freed message stops receiving updates.
public override void _ExitTree()
{
if (SceneContextNode.Instance != null)
{
var eventController = SceneContextNode.FindNode("EventController");
eventController?.RemoveUpdateEventListener(this);
}
}
///
/// Priority 1 — lightweight enough for many concurrent messages.
///
public int Priority => 1;
///
/// Per-frame update: displays for 3 seconds, then fades out over
/// 2 / seconds. Self-destructs via
/// QueueFree when fully transparent.
///
public void OnUpdate()
{
var currentTime = (float)Godot.Time.GetTicksMsec() / 1000f;
var elapsed = currentTime - timestamp;
if (elapsed < 3f)
return;
var fadeDuration = 2f / Speed;
var fadeElapsed = elapsed - 3f;
var progress = Mathf.Clamp(fadeElapsed / fadeDuration, 0f, 1f);
textField.SelfModulate = UiTokens.Alpha(Colors.White, 1f - progress);
if (progress >= 1f)
QueueFree();
}
}
}