Faccion.cs

Component that represents a faction for a GameObject. Stores a faction name and allied faction names, provides ally/enemy checks, and supports temporary combat groups (SQUAD N) that override faction and display a group marker. Also hooks into a Vida component to remove group on death and provides a console command to clear all groups in the scene.

File Access
namespace SBH;

using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Bando al que pertenece este GameObject. Dos objetos son aliados si
/// tienen el mismo bando, o si uno lista al bando del otro en Aliados.
/// Un objeto SIN este componente se considera enemigo de todos.
/// Va junto a Vida, en sprunkis y en el jugador.
/// </summary>
public sealed class Faccion : Component
{
	[Property, Description( "Nombre del bando (ej: naranja, verde, jugadores)" )]
	public string Bando { get; set; } = "neutral";

	[Property, Description( "Bandos aliados además del propio" )]
	public List<string> Aliados { get; set; } = new();

	public bool EsAliadoDe( Faccion otra )
	{
		if ( otra == null ) return false;

		if ( MismoBando( Bando, otra.Bando ) ) return true;

		// Alianza declarada en cualquiera de las dos direcciones
		if ( Aliados != null && Aliados.Any( b => MismoBando( b, otra.Bando ) ) ) return true;
		if ( otra.Aliados != null && otra.Aliados.Any( b => MismoBando( b, Bando ) ) ) return true;

		return false;
	}

	/// <summary>
	/// Regla de combate: sin facción se es enemigo de todos.
	/// </summary>
	public static bool SonEnemigos( Faccion a, Faccion b )
	{
		if ( a == null || b == null ) return true;
		return !a.EsAliadoDe( b );
	}

	static bool MismoBando( string a, string b )
		=> string.Equals( a?.Trim(), b?.Trim(), StringComparison.OrdinalIgnoreCase );

	// ---------- Grupos de combate (la tecla SbhGroup de la base) ----------

	public const int MaxGrupos = 4;

	/// <summary> Grupo de combate actual (0 = ninguno, conserva su bando normal). </summary>
	public int Grupo { get; private set; }

	// El bando de fábrica, para restaurarlo al salir del grupo
	string _bandoOriginal;
	List<string> _aliadosOriginales;
	Vida _vida;

	// Los muertos dejan el grupo: se apaga el fueguito (y el cadáver vuelve
	// a su bando de fábrica, que ya no le importa a nadie)
	protected override void OnEnabled()
	{
		_vida = Components.Get<Vida>();
		if ( _vida.IsValid() )
			_vida.AlMorir += QuitarGrupo;
	}

	protected override void OnDisabled()
	{
		if ( _vida.IsValid() )
			_vida.AlMorir -= QuitarGrupo;
	}

	/// <summary>
	/// Mete al objeto en un grupo de combate (1..MaxGrupos; 0 o menos = lo
	/// saca). Cada grupo es un bando "SQUAD N": los del mismo número son
	/// aliados entre sí y enemigos de todo lo demás (incluido su propio
	/// personaje en otro grupo). El número flota sobre la cabeza; el bando
	/// original se guarda la primera vez para poder restaurarlo.
	/// </summary>
	public void AsignarGrupo( int grupo )
	{
		if ( grupo <= 0 )
		{
			QuitarGrupo();
			return;
		}

		grupo = grupo.Clamp( 1, MaxGrupos );

		if ( Grupo == 0 )
		{
			_bandoOriginal = Bando;
			_aliadosOriginales = Aliados;
		}

		Grupo = grupo;
		Bando = $"SQUAD {grupo}";
		Aliados = new();
		CartelGrupo.Actualizar( GameObject, grupo );
	}

	/// <summary> Cicla el grupo: sin grupo → 1 → 2 → 3 → 4 → restaura el bando original. </summary>
	public void CiclarGrupo()
		=> AsignarGrupo( Grupo >= MaxGrupos ? 0 : Grupo + 1 );

	/// <summary> Saca al objeto de su grupo y le devuelve bando y aliados originales. </summary>
	public void QuitarGrupo()
	{
		if ( Grupo == 0 ) return;

		Grupo = 0;
		Bando = _bandoOriginal;
		Aliados = _aliadosOriginales;
		CartelGrupo.Actualizar( GameObject, 0 );
	}

	/// <summary> Deshace todos los grupos de combate de la escena. </summary>
	[ConCmd( "sbh.grupos.limpiar", Help = "Saca a todos los nextbots de sus grupos de combate (SQUAD N) y restaura sus bandos." )]
	public static void LimpiarGrupos()
	{
		var escena = Game.ActiveScene;
		if ( escena == null ) return;

		foreach ( var faccion in escena.GetAllComponents<Faccion>().ToArray() )
			faccion.QuitarGrupo();
	}
}