Dynamic Attitude & Multiplayer

Attitude is never stored - it is computed on demand from the current tags on both sides via GetTeamAttitudeBetween. That makes it always-correct for direct queries (damage, targeting, UI). The hard part is the systems that cache attitude. Unreal's AI perception is the worst offender: it evaluates affiliation once per stimulus and never re-checks, so a mid-game defection would otherwise leave AI treating a new enemy as a friend forever.

How a change propagates

StepWhat happens
1. Mutate (server)JoinTeam / AddHostileTags / ... change the replicated TeamTags / HostileToTags
2. BroadcastThe component fires OnTeamTagsChanged / OnHostileTagsChanged with the owning actor (on the server now, and on clients via OnRep)
3. Refresh perceptionThe subsystem (which registered every agent) re-evaluates each AI perceiver via RequestStimuliListenerUpdate - preserving last-known-location, no ForgetActor
4. NotifyUCrimsonTeamSubsystem::OnTeamAttitudeChanged(ChangedActor) broadcasts for any custom AI/UI re-evaluation

Because the refresh re-runs affiliation rather than forgetting the actor, an AI that should now see a new enemy starts perceiving it, and one whose enemy became friendly stops - both without losing what they already knew.

The interface-on-actor rule

Perception reads attitude with FGenericTeamId::GetAttitude(PerceiverActor, Target), which casts the perceiving actor to IGenericTeamAgentInterface. CrimsonTeams puts that interface on the component, so the actor must also expose it (forwarding to GetTeamAttitudeBetween) for perception to read Crimson teams. A component added by UGFA_AddTeams (CrimsonCore - requires all Crimson plugins) cannot supply an interface - interfaces are compile-time on the class - so this forward lives on your AI controller/pawn (CrimsonCore's classes already have it). See How-To: Make an Actor a Team Agent, step 3.

Multiplayer authority
Team state is server-authoritative with no client RPC. Mutate only where HasAuthority() is true; clients get the result by replication and re-fire the change delegate from OnRep so client-side UI updates. A client-side mutation silently no-ops.

Replication summary

StateMechanism
TeamTags / HostileToTags (per agent)ReplicatedUsing -> OnRep re-broadcasts the change delegate on clients
Team info actor + its tagReplicated ACrimsonTeamPublicInfo; ACrimsonTeamPrivateInfo is server/owner-scoped
Faction tag stacksFCrimsonTagStackContainer on the team info actor (FastArray, delta-replicated)