Line endings: LF everywhere
.gitattributes forces LF (CRLF only for .bat/.cmd), core.autocrlf is off; files that differed only by CRLF are normalized.
This commit is contained in:
1 parent
3fb418fa4a
commit
c4c90116ed
23 files changed
+1681
-1651
No files matched your search
+119
-119
@@ -1,121 +1,121 @@
|
||||
using Cthangover.Core.Events;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
using Cthangover.Core.Events;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
using Cthangover.Core.UI.Messages;
|
||||
|
||||
namespace Cthangover.Mods.Interface.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Static factory for floating UI messages (like "Quest updated" or item
|
||||
/// pickup notifications). Manages a cycling position index of 0-5 so
|
||||
/// consecutive messages stack vertically without overlapping. Each message
|
||||
/// is instantiated from message.tscn and parented to the Background node
|
||||
/// (found via scene tree search). Messages auto-fade and self-destruct
|
||||
/// via UiMessage's OnUpdate.
|
||||
/// </summary>
|
||||
public static class MessagesHelper
|
||||
{
|
||||
private const int maxMessageIndex = 5;
|
||||
private static int lastMessageIndex = 0;
|
||||
|
||||
private static ModScenePrefab prefab;
|
||||
|
||||
private static int GetNextIndex(int index)
|
||||
{
|
||||
if (index++ >= maxMessageIndex)
|
||||
index = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
private static float GetPositionByLastIndex(int index)
|
||||
{
|
||||
return (index - 1) * 60;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the cycling position index to 0. Call when UI state resets
|
||||
/// to prevent messages from accumulating at high Y positions.
|
||||
/// </summary>
|
||||
public static void ClearLastIndex()
|
||||
{
|
||||
lastMessageIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message in the default warning colour at the next vertical slot.
|
||||
/// </summary>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="speed">Fade speed multiplier (higher = faster fade, default 1).</param>
|
||||
public static void AddMessage(string text, float speed = 1f)
|
||||
{
|
||||
AddMessage(text, UiTokens.Warning, speed);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message with a custom color at the next vertical slot.
|
||||
/// Messages auto-fade and self-destruct after ~3 seconds of display plus
|
||||
/// a fade duration determined by <paramref name="speed"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
/// <param name="speed">Fade speed multiplier (higher = faster fade, default 1).</param>
|
||||
public static void AddMessage(string text, Color color, float speed = 1f)
|
||||
{
|
||||
var nextIndex = GetNextIndex(lastMessageIndex);
|
||||
AddMessage(GetPositionByLastIndex(nextIndex), text, color, speed);
|
||||
lastMessageIndex = nextIndex;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message in the default warning colour at a specific vertical
|
||||
/// position, bypassing the cycling slot system.
|
||||
/// </summary>
|
||||
/// <param name="position">Y-offset in pixels for this message.</param>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="speed">Fade speed multiplier (default 1).</param>
|
||||
public static void AddMessage(float position, string text, float speed = 1f)
|
||||
{
|
||||
AddMessage(position, text, UiTokens.Warning, speed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="UiMessage"/> from <c>message.tscn</c>, parents
|
||||
/// it to the Background node, and configures it for display and auto-fade.
|
||||
/// </summary>
|
||||
/// <param name="position">Y-offset in pixels for this message.</param>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
/// <param name="speed">Fade speed multiplier (default 1).</param>
|
||||
public static void AddMessage(float position, string text, Color color, float speed = 1f)
|
||||
{
|
||||
if (prefab == null)
|
||||
|
||||
namespace Cthangover.Mods.Interface.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// Static factory for floating UI messages (like "Quest updated" or item
|
||||
/// pickup notifications). Manages a cycling position index of 0-5 so
|
||||
/// consecutive messages stack vertically without overlapping. Each message
|
||||
/// is instantiated from message.tscn and parented to the Background node
|
||||
/// (found via scene tree search). Messages auto-fade and self-destruct
|
||||
/// via UiMessage's OnUpdate.
|
||||
/// </summary>
|
||||
public static class MessagesHelper
|
||||
{
|
||||
private const int maxMessageIndex = 5;
|
||||
private static int lastMessageIndex = 0;
|
||||
|
||||
private static ModScenePrefab prefab;
|
||||
|
||||
private static int GetNextIndex(int index)
|
||||
{
|
||||
if (index++ >= maxMessageIndex)
|
||||
index = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
private static float GetPositionByLastIndex(int index)
|
||||
{
|
||||
return (index - 1) * 60;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the cycling position index to 0. Call when UI state resets
|
||||
/// to prevent messages from accumulating at high Y positions.
|
||||
/// </summary>
|
||||
public static void ClearLastIndex()
|
||||
{
|
||||
lastMessageIndex = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message in the default warning colour at the next vertical slot.
|
||||
/// </summary>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="speed">Fade speed multiplier (higher = faster fade, default 1).</param>
|
||||
public static void AddMessage(string text, float speed = 1f)
|
||||
{
|
||||
AddMessage(text, UiTokens.Warning, speed);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message with a custom color at the next vertical slot.
|
||||
/// Messages auto-fade and self-destruct after ~3 seconds of display plus
|
||||
/// a fade duration determined by <paramref name="speed"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
/// <param name="speed">Fade speed multiplier (higher = faster fade, default 1).</param>
|
||||
public static void AddMessage(string text, Color color, float speed = 1f)
|
||||
{
|
||||
var nextIndex = GetNextIndex(lastMessageIndex);
|
||||
AddMessage(GetPositionByLastIndex(nextIndex), text, color, speed);
|
||||
lastMessageIndex = nextIndex;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message in the default warning colour at a specific vertical
|
||||
/// position, bypassing the cycling slot system.
|
||||
/// </summary>
|
||||
/// <param name="position">Y-offset in pixels for this message.</param>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="speed">Fade speed multiplier (default 1).</param>
|
||||
public static void AddMessage(float position, string text, float speed = 1f)
|
||||
{
|
||||
AddMessage(position, text, UiTokens.Warning, speed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a <see cref="UiMessage"/> from <c>message.tscn</c>, parents
|
||||
/// it to the Background node, and configures it for display and auto-fade.
|
||||
/// </summary>
|
||||
/// <param name="position">Y-offset in pixels for this message.</param>
|
||||
/// <param name="text">The message text to display.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
/// <param name="speed">Fade speed multiplier (default 1).</param>
|
||||
public static void AddMessage(float position, string text, Color color, float speed = 1f)
|
||||
{
|
||||
if (prefab == null)
|
||||
prefab = new ModScenePrefab("scenes/ui/message.tscn");
|
||||
|
||||
var instance = prefab.Instantiate<UiMessage>();
|
||||
var background = SceneContextNode.FindNode<Control>("Background");
|
||||
if (background == null)
|
||||
{
|
||||
background = SceneContextNode.Instance?.GetTree()?.GetFirstNodeInGroup("Background") as Control;
|
||||
if (background == null)
|
||||
{
|
||||
GameLogger.Log("UI", "Background node not found for messages", LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
background.AddChild(instance);
|
||||
instance.Setup(position, text, speed, color);
|
||||
|
||||
ModEventBus.Publish(new NotificationRequestedEvent
|
||||
{
|
||||
Text = text,
|
||||
Category = "message",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var instance = prefab.Instantiate<UiMessage>();
|
||||
var background = SceneContextNode.FindNode<Control>("Background");
|
||||
if (background == null)
|
||||
{
|
||||
background = SceneContextNode.Instance?.GetTree()?.GetFirstNodeInGroup("Background") as Control;
|
||||
if (background == null)
|
||||
{
|
||||
GameLogger.Log("UI", "Background node not found for messages", LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
background.AddChild(instance);
|
||||
instance.Setup(position, text, speed, color);
|
||||
|
||||
ModEventBus.Publish(new NotificationRequestedEvent
|
||||
{
|
||||
Text = text,
|
||||
Category = "message",
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+103
-103
@@ -1,104 +1,104 @@
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.UI.Event;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Godot;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public partial class UiMessage : Control, IOnUpdateEvent
|
||||
{
|
||||
|
||||
[Export] private Label textField;
|
||||
/// <summary>Tint of the message text; defaults to the warning colour.</summary>
|
||||
[Export] public Color Color { get; set; } = UiTokens.Warning;
|
||||
/// <summary>Fade speed multiplier — 1 is normal; higher values shorten the fade-out.</summary>
|
||||
[Export] public float Speed { get; set; } = 1f;
|
||||
private float timestamp;
|
||||
|
||||
/// <summary>
|
||||
/// The text content displayed by this message. Reads/writes the child
|
||||
/// <c>textField</c> label directly.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get => textField.Text;
|
||||
set => textField.Text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the message: positions it, sets text/speed/color, records
|
||||
/// the start timestamp, and registers with the <see cref="SceneEventController"/>
|
||||
/// for per-frame updates. Called by <see cref="MessagesHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="pos">Vertical Y-offset in pixels.</param>
|
||||
/// <param name="text">The message text.</param>
|
||||
/// <param name="speed">Fade speed multiplier.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
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<SceneEventController>("EventController");
|
||||
eventController?.AddUpdateEventListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Detaches from the event controller so a freed message stops receiving updates.</summary>
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (SceneContextNode.Instance != null)
|
||||
{
|
||||
var eventController = SceneContextNode.FindNode<SceneEventController>("EventController");
|
||||
eventController?.RemoveUpdateEventListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority <c>1</c> — lightweight enough for many concurrent messages.
|
||||
/// </summary>
|
||||
public int Priority => 1;
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame update: displays for 3 seconds, then fades out over
|
||||
/// <c>2 / <see cref="Speed"/></c> seconds. Self-destructs via
|
||||
/// <c>QueueFree</c> when fully transparent.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Cthangover.Mods.Interface.Messages
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public partial class UiMessage : Control, IOnUpdateEvent
|
||||
{
|
||||
|
||||
[Export] private Label textField;
|
||||
/// <summary>Tint of the message text; defaults to the warning colour.</summary>
|
||||
[Export] public Color Color { get; set; } = UiTokens.Warning;
|
||||
/// <summary>Fade speed multiplier — 1 is normal; higher values shorten the fade-out.</summary>
|
||||
[Export] public float Speed { get; set; } = 1f;
|
||||
private float timestamp;
|
||||
|
||||
/// <summary>
|
||||
/// The text content displayed by this message. Reads/writes the child
|
||||
/// <c>textField</c> label directly.
|
||||
/// </summary>
|
||||
public string Text
|
||||
{
|
||||
get => textField.Text;
|
||||
set => textField.Text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the message: positions it, sets text/speed/color, records
|
||||
/// the start timestamp, and registers with the <see cref="SceneEventController"/>
|
||||
/// for per-frame updates. Called by <see cref="MessagesHelper"/>.
|
||||
/// </summary>
|
||||
/// <param name="pos">Vertical Y-offset in pixels.</param>
|
||||
/// <param name="text">The message text.</param>
|
||||
/// <param name="speed">Fade speed multiplier.</param>
|
||||
/// <param name="color">The text color.</param>
|
||||
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<SceneEventController>("EventController");
|
||||
eventController?.AddUpdateEventListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Detaches from the event controller so a freed message stops receiving updates.</summary>
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (SceneContextNode.Instance != null)
|
||||
{
|
||||
var eventController = SceneContextNode.FindNode<SceneEventController>("EventController");
|
||||
eventController?.RemoveUpdateEventListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority <c>1</c> — lightweight enough for many concurrent messages.
|
||||
/// </summary>
|
||||
public int Priority => 1;
|
||||
|
||||
/// <summary>
|
||||
/// Per-frame update: displays for 3 seconds, then fades out over
|
||||
/// <c>2 / <see cref="Speed"/></c> seconds. Self-destructs via
|
||||
/// <c>QueueFree</c> when fully transparent.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user