// Adapts the owned Intel addon's team discovery messages to successor events. // Core already owns the local phase briefing; this never hides or replaces it. modded class ERR_GameMode { protected ref map m_ERRIntelSeen = new map(); protected ref map m_ERRIntelPending = new map(); protected float m_ERRIntelMessageAt; protected bool m_ERRIntelFeedbackStopped; protected int m_ERRIntelMessageSequence; protected int m_ERRIntelReceivedSequence; protected override void EOnInit(IEntity owner) { super.EOnInit(owner); if (SCR_Global.IsEditMode() || !Replication.IsServer()) return; OnLocationDiscovered().Insert(ERR_QueueDiscoveryFeedback); OnTick().Insert(ERR_FlushDiscoveryFeedback); OnStop().Insert(ERR_StopDiscoveryFeedback); } protected void ERR_QueueDiscoveryFeedback(ERR_LocationComponent location, int previous, int current) { if (!Replication.IsServer() || m_ERRIntelFeedbackStopped || !Active() || Phase() == ERR_Phase.PREPARING || !location) return; // Initial prison/cache setup and automatic extraction disclosure are already // explained by Core. A rough clue must never disclose a site's name or kind. if (location.Kind() == ERR_LocationKind.PRISON || location.Kind() == ERR_LocationKind.EXTRACTION) return; if (previous < 0 || current <= previous || current > 2 || current != location.Knowledge()) return; int seen; if (m_ERRIntelSeen.Find(location, seen) && current <= seen) return; if (!m_ERRIntelSeen.Contains(location) && m_ERRIntelSeen.Count() >= 64) return; m_ERRIntelSeen.Set(location, current); if (m_ERRIntelPending.IsEmpty()) m_ERRIntelMessageAt = Math.Max(m_ERRIntelMessageAt, GetWorld().GetWorldTime() + 1500); // Several folders recovered in one inventory scan become one final update. m_ERRIntelPending.Set(location, current); } protected void ERR_FlushDiscoveryFeedback(float now) { if (!Replication.IsServer() || m_ERRIntelFeedbackStopped || !Active() || m_ERRIntelPending.IsEmpty() || now < m_ERRIntelMessageAt) return; int identified; int rough; string label; foreach (ERR_LocationComponent location, int knowledge : m_ERRIntelPending) { if (!location) continue; if (knowledge == 2) { identified++; label = location.Label(); } else rough++; } m_ERRIntelPending.Clear(); m_ERRIntelMessageAt = now + 5000; string message = ERR_IntelDiscoveryText.Message(identified, rough, label); if (message.IsEmpty()) return; // The listen host and remote recipients share the same per-round sequence. // Local delivery is explicit and cannot duplicate a transport callback. m_ERRIntelMessageSequence++; ERR_ReceiveDiscoveryFeedback(m_ERRIntelMessageSequence, message); Rpc(ERR_ReceiveDiscoveryFeedback, m_ERRIntelMessageSequence, message); } [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)] protected void ERR_ReceiveDiscoveryFeedback(int sequence, string message) { if (!GetGame() || this != ERR_GameMode.Get() || m_ERRIntelFeedbackStopped || !Active() || !GetGame().GetPlayerController() || SCR_Global.IsEditMode() || message.IsEmpty() || sequence <= m_ERRIntelReceivedSequence) return; m_ERRIntelReceivedSequence = sequence; SCR_ChatComponent.RadioProtocolMessage(message); } protected void ERR_StopDiscoveryFeedback() { m_ERRIntelFeedbackStopped = true; m_ERRIntelPending.Clear(); m_ERRIntelSeen.Clear(); m_ERRIntelMessageAt = 0; OnLocationDiscovered().Remove(ERR_QueueDiscoveryFeedback); OnTick().Remove(ERR_FlushDiscoveryFeedback); } } class ERR_IntelDiscoveryText { static string Message(int identified, int rough, string label) { if (identified + rough == 0) return ""; if (identified == 0 && rough == 1) return "A new 500 m search area is marked on your field map. Recover another folder to identify the location."; if (identified == 1 && rough == 0) { if (label.IsEmpty()) label = "A location"; return label + " identified. Check your field map."; } return string.Format("Field map updated: %1 identified locations and %2 search areas. Open a carried map to review them.", identified, rough); } }