Team Registration & Info Actors

"Membership" (an agent's tags) and "declared teams" (named teams with cosmetics and a registry) are separate layers. Basic attitude needs only the first; the second adds display assets, player auto-assignment, faction-wide tag stacks, and a queryable registry.

The pieces

TypeRole
UCrimsonTeamCreationComponentOn the Game State (server). Spawns the configured team-info actors, assigns players (ServerChooseTeamForPlayer), and supports runtime AddTeams / RemoveTeams
ACrimsonTeamInfoBaseAbstract AInfo. Holds the team FGameplayTag + a FCrimsonTagStackContainer of faction tag stacks; registers with the subsystem on BeginPlay
ACrimsonTeamPublicInfoReplicated to all clients; carries the UCrimsonTeamDisplayAsset reference
ACrimsonTeamPrivateInfoServer/owner-scoped; for team-internal state you don't broadcast to opponents
UCrimsonTeamSubsystemWorld subsystem registry. Resolves agents, gates damage, exposes faction tag stacks, and drives the dynamic-attitude refresh
Registration is automatic
Team-info actors register/unregister themselves in BeginPlay/EndPlay. Never call RegisterTeamInfo / UnregisterTeamInfo yourself.

Faction tag stacks

Team-wide flags and buffs (zone control, global modifiers) live as tag stacks on the team-info actor, mutated through the subsystem and shared by every member of the faction.

cpp
const FGameplayTag RedTeam = FGameplayTag::RequestGameplayTag(FName("Team.Red"));
const FGameplayTag ZoneCaptured = FGameplayTag::RequestGameplayTag(FName("Team.Buff.ZoneCaptured"));
UCrimsonTeamSubsystem* TS = GetWorld()->GetSubsystem<UCrimsonTeamSubsystem>();
TS->AddTeamTagStack(RedTeam, ZoneCaptured, 1); // server
const int32 N = TS->GetTeamTagStackCount(RedTeam, ZoneCaptured);
const bool bHas = TS->TeamHasTag(RedTeam, ZoneCaptured);