Game/GameText.cs
using Forp.Object;
using Forp.Util;
using Sandbox.Diagnostics;
using System;
using System.Collections.Generic;
using System.Text;

namespace Forp.Game;

public class GameText : Component
{
	[RequireComponent] protected TextRenderer TextRenderer { get; set; }

	public void SetText(string Text)
	{
		TextRenderer.Text = Text;
	}

	public void SetScale(float Scale)
	{
		TextRenderer.Scale = Scale;
	}

	protected override void OnStart()
	{
		base.OnStart();
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if (GameManager.Instance.Mode != EGameManagerMode.Menu) // meh
		{
			Vector3 Direction = WorldPosition - GamePlayer.Local.Camera.WorldPosition;
			TextRenderer.WorldRotation = Rotation.LookAt(Direction);
		}
	}

	public static T CreateTextObject<T>(Transform Transform, string Text = "") where T : GameText
	{
		var TextPrefab = GameManager.Instance.GetTextPrefab<T>();
		Assert.NotNull(TextPrefab);

		var ConstructionClone = TextPrefab.Clone();
		ConstructionClone.WorldTransform = Transform;

		var TextObject = ConstructionClone.GetComponent<T>();
		TextObject.SetText($"{Text}");

		return TextObject;
	}
}

public sealed class QueuedObjectText : GameText
{
	private TimeSince TimeSinceHovered;
	private bool _isHovered = false;
	public bool IsHovered
	{
		get => _isHovered;
		set
		{
			if (value)
			{
				TimeSinceHovered = 0;
			}

			if (_isHovered == value)
			{
				return;
			}

			_isHovered = value;
			TextRenderer.Color = _isHovered ? Color.Red : Color.White;
		}
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		if (_isHovered && TimeSinceHovered > 0.066f)
		{
			IsHovered = false;
		}
	}

	public Hex OwnerHex = null;
	public Guid GameObjectId = Guid.Empty;

	public void OnClick()
	{
		if (OwnerHex == null || GameObjectId == Guid.Empty)
		{
			Log.Warning($"{this} hex text was invalid!");
			return;
		}

		OwnerHex.Server_RemoveUnitFromQueue(GameObjectId);
		DestroyGameObject();
	}
}

public sealed class DamageText : GameText
{
	private int TicksAlive = 0;

	protected override void OnStart()
	{
		base.OnStart();

		TextRenderer.Color = Color.Red;
	}

	protected override void OnFixedUpdate()
	{
		base.OnFixedUpdate();

		WorldPosition += Vector3.Up * 5;
		TicksAlive++;

		if (TicksAlive > 50)
		{
			DestroyGameObject();
		}
	}
}