using System.Collections.Generic;
using System.Linq;
using Cthangover.Core.Characters;
using Cthangover.Core.Scenes;
using Cthangover.Core.UI;
using Cthangover.Core.UI.Theming;
using Godot;
namespace Cthangover.FFBattle.UI
{
///
/// Panel that arranges player character widgets horizontally at the bottom of
/// the battle screen. Creates one per player
/// character, wires click and health-change handlers, and positions them using
/// fixed horizontal spacing via . When a player's HP drops
/// to zero, the widget plays its death animation and is removed; the remaining
/// widgets are repositioned. Exposes for win/loss checks.
///
public partial class FFPlayerPanel : ModWidget
{
private const float CELL_SPACING = 20f;
/// All player character widgets, including dead ones until their death animation completes.
public List Widgets { get; } = new();
/// Raised when a player widget is clicked — routed to for character selection.
public event System.Action OnWidgetClicked;
protected override void Construct()
{
CthangoverTheme.Apply(this);
}
///
/// Creates instances for each player, scales
/// them uniformly, wires click and health-change handlers, and calls
/// to position them horizontally.
///
public void Init(Character[] players, float scale)
{
foreach (var child in Widgets)
child.QueueFree();
Widgets.Clear();
for (int i = 0; i < players.Length; i++)
{
var widget = (FFCharacterWidget)TscnScenes.LoadAndBuild("scenes/ff_character_widget.tscn");
widget.EnsureConstructed();
widget.Scale = new Vector2(scale, scale);
widget.Init(players[i]);
widget.IsPlayer = true;
widget.MouseFilter = MouseFilterEnum.Stop;
widget.GuiInput += (evt) =>
{
if (evt is InputEventMouseButton mb && mb.Pressed && mb.ButtonIndex == MouseButton.Left)
OnWidgetClicked?.Invoke(widget);
};
AddChild(widget);
Widgets.Add(widget);
players[i].Attributes.Health.OnChange += (value, baseValue) =>
{
widget.UpdateInfo();
if (value <= 0 && !widget.IsDead)
{
widget.PlayDeathAnimation(() =>
{
RemoveWidget(widget);
Redraw();
});
}
};
}
Redraw();
}
/// Repositions all widgets in a horizontal row with fixed CELL_SPACING between them.
public void Redraw()
{
for (int i = 0; i < Widgets.Count; i++)
{
var widget = Widgets[i];
if (widget == null)
continue;
var effWidth = widget.Size.X * widget.Scale.X;
var effHeight = widget.Size.Y * widget.Scale.Y;
widget.Position = new Vector2(i * (effWidth + CELL_SPACING) + CELL_SPACING, Size.Y - effHeight - CELL_SPACING);
}
}
/// Returns true if any widget is alive and has positive health.
public bool HasAlive()
{
return Widgets.Any(w => !w.IsDead && w.Card?.Attributes?.Health?.Value > 0);
}
/// Frees all widget instances and clears the widget list. Called on battle cleanup.
public void ClearAll()
{
foreach (var widget in Widgets.ToList())
{
if (GodotObject.IsInstanceValid(widget))
widget.QueueFree();
}
Widgets.Clear();
Redraw();
}
/// Removes a widget from the panel, frees it, and repositions remaining widgets.
public void RemoveWidget(FFCharacterWidget widget)
{
Widgets.Remove(widget);
widget.QueueFree();
Redraw();
}
}
}