UI/FloatingTextManager/FloatingText.razor
@using Sandbox;
@using Sandbox.UI;
@namespace Milaine.UI

@inherits Panel

<root>@Text</root>

@code {
  public string Text { get; set; } = "+1";
  public float SpawnX { get; set; }
  public float SpawnY { get; set; }
  public float Duration { get; set; } = 0.5f;

  private float driftX;
  private float driftY;
  private TimeSince timeSinceCreated;
  private bool initialized;

  protected override void OnAfterTreeRender(bool firstTime)
  {
    base.OnAfterTreeRender(firstTime);

    if (!initialized)
    {
      initialized = true;
      timeSinceCreated = 0;

      driftX = Game.Random.Float(-40f, 40f);
      driftY = Game.Random.Float(-80f, 80f);
    }
  }

  public override void Tick()
  {
    base.Tick();

    if (!initialized)
    {
      return;
    }

    float t = ((float)timeSinceCreated / Duration).Clamp(0f, 1f);

    Style.Opacity = 1f - t;

    Style.Left = Length.Pixels(SpawnX + driftX * t);
    Style.Top = Length.Pixels(SpawnY + driftY * t);

    if (t >= 1f)
    {
      Delete();
    }
  }

  protected override int BuildHash() => System.HashCode.Combine(Text);
}