Player/Components/TeamComponent.cs

Team and team-related utilities. Defines Team enum, ITeam interface and TeamChangedEvent, plus extension methods to get a GameObject's team, check friendliness between objects/players/states, map teams to display names, colors and asset paths, and get the opposing team.

Reflection
using Sandbox.Events;

namespace KOTH;

/// <summary>
/// A component that has a team on it.
/// </summary>
public interface ITeam : IValid
{
	public Team Team { get; set; }
}

public enum Team
{
	Unassigned = 0,

	Terrorist,
	CounterTerrorist
}

public record TeamChangedEvent(Team Before, Team After) : IGameEvent;

public static class TeamExtensions
{
	/// <summary>
	/// Accessor to get the team of someone/something.
	/// </summary>
	/// <param name="gameObject"></param>
	/// <returns></returns>
	public static Team GetTeam(this GameObject gameObject)
	{
		if (gameObject.Root.Components.Get<PlayerState>() is { IsValid: true } state)
		{
			return state.Team;
		}

		return Team.Unassigned;
	}

	//
	// For all of this, maybe the gamemode should be controlling it, and not some global methods
	//

	/// <summary>
	/// Are we friendly with this other team?
	/// </summary>
	/// <param name="teamOne"></param>
	/// <param name="teamTwo"></param>
	/// <returns></returns>
	private static bool IsFriendly(Team teamOne, Team teamTwo)
	{
		if (teamOne == Team.Unassigned || teamTwo == Team.Unassigned) return false;
		return teamOne == teamTwo;
	}

	/// <summary>
	/// Are these two GameObjects friends with eachother?
	/// </summary>
	/// <param name="self"></param>
	/// <param name="other"></param>
	/// <returns></returns>
	public static bool IsFriendly(this GameObject self, GameObject other)
	{
		if (!self.IsValid() || !other.IsValid()) return false;
		return IsFriendly(self.GetTeam(), other.GetTeam());
	}

	/// <summary>
	/// Are these two <see cref="PlayerPawn"/>s friends with each other?
	/// </summary>
	public static bool IsFriendly(this PlayerPawn self, PlayerPawn other)
	{
		if (!self.IsValid() || !other.IsValid()) return false;
		return IsFriendly(self.Team, other.Team);
	}

	/// <summary>
	/// Are these two <see cref="PlayerState"/>s friends with each other?
	/// </summary>
	public static bool IsFriendly(this PlayerState self, PlayerState other)
	{
		if (!self.IsValid() || !other.IsValid()) return false;
		return IsFriendly(self.Team, other.Team);
	}

	public static string GetName(this Team team)
	{
		return team switch
		{
			Team.CounterTerrorist => "ROUG",
			Team.Terrorist => "VERD",
			_ => "Unassigned",
		};
	}

	private static Dictionary<Team, Tuple<Color, Color>> teamColors = new()
	{
		{ Team.CounterTerrorist, new(new(33, 122, 37), new(49, 183, 56)) },
		{ Team.Terrorist, new(new(181, 64, 56), new(241, 85, 73)) },
		{ Team.Unassigned, new(new(150, 150, 150), Color.White) },
	};

	public static Color GetColor(this Team team, bool Dark)
	{
		return Dark ? teamColors[team].Item1 : teamColors[team].Item2;
	}

	public static string GetIconPath(this Team team)
	{
		return team switch
		{
			Team.CounterTerrorist => "/ui/teams/operators_logo.png",
			Team.Terrorist => "/ui/teams/anarchists_logo.png",
			_ => ""
		};
	}

	public static string GetBannerPath(this Team team)
	{
		return team switch
		{
			Team.CounterTerrorist => "/ui/teams/operators_logo_banner.png",
			Team.Terrorist => "/ui/teams/anarchists_logo_banner.png",
			_ => ""
		};
	}

	public static Team GetOpponents(this Team team)
	{
		return team switch
		{
			Team.CounterTerrorist => Team.Terrorist,
			Team.Terrorist => Team.CounterTerrorist,
			_ => Team.Unassigned
		};
	}
}