Component that represents a faction for a GameObject. Stores the faction name and allied faction names, determines ally/enemy relationships, and manages temporary combat groups (SQUAD 1..4) which override the object's faction and show a group marker.
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();
}
}