diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index c57fe6c464..6f79bb9d29 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -10,6 +10,15 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Log; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using Content.Server.GameObjects.Components; +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Interactable; +using Content.Server.GameObjects.EntitySystems; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Robust.Shared.Interfaces.Map; namespace Content.Server.Chat { @@ -21,6 +30,7 @@ namespace Content.Server.Chat private const int VoiceRange = 7; // how far voice goes in world units #pragma warning disable 649 + [Dependency] private readonly IEntitySystemManager _entitySystemManager; [Dependency] private readonly IServerNetManager _netManager; [Dependency] private readonly IPlayerManager _playerManager; [Dependency] private readonly ILocalizationManager _localizationManager; @@ -67,6 +77,9 @@ namespace Content.Server.Chat msg.MessageWrap = $"{source.Name} says, \"{{0}}\""; msg.SenderEntity = source.Uid; _netManager.ServerSendToMany(msg, clients.ToList()); + + var listeners = _entitySystemManager.GetEntitySystem(); + listeners.PingListeners(source, pos, message); } public void EntityMe(IEntity source, string action) diff --git a/Content.Server/GameObjects/Components/ListeningComponent.cs b/Content.Server/GameObjects/Components/ListeningComponent.cs new file mode 100644 index 0000000000..655e594bef --- /dev/null +++ b/Content.Server/GameObjects/Components/ListeningComponent.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Content.Server.GameObjects.Components.Interactable; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.GameObjects.Components +{ + [RegisterComponent] + public class ListeningComponent : Component + { + + public override string Name => "Listening"; + + public void PassSpeechData(string speech, IEntity source, float distance) + { + + foreach (var listener in Owner.GetAllComponents()) + { + if (distance > listener.GetListenRange()) { continue; } + listener.HeardSpeech(speech, source); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/RadioComponent.cs b/Content.Server/GameObjects/Components/RadioComponent.cs new file mode 100644 index 0000000000..e5e7661a5b --- /dev/null +++ b/Content.Server/GameObjects/Components/RadioComponent.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Content.Server.GameObjects.EntitySystems; +using Content.Server.Interfaces; +using Content.Server.Interfaces.Chat; +using Content.Server.Interfaces.GameObjects.Components.Interaction; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Interactable +{ + [RegisterComponent] + class RadioComponent : Component, IUse, IListen + { +#pragma warning disable 649 + [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; + [Dependency] private readonly IServerNotifyManager _notifyManager = default!; +#pragma warning restore 649 + + public override string Name => "Radio"; + + private bool _radioOn; + private int _listenRange = 7; + private RadioSystem _radioSystem = default!; + + [ViewVariables] + public bool RadioOn + { + get => _radioOn; + private set + { + _radioOn = value; + Dirty(); + } + } + + public override void Initialize() + { + base.Initialize(); + + _radioSystem = _entitySystemManager.GetEntitySystem(); + + RadioOn = false; + } + + public void PassOnMessage(string message) + { + if(RadioOn) + { + _radioSystem.SpreadMessage(Owner, message); + } + } + + public void Speaker(string message) + { + var chat = IoCManager.Resolve(); + chat.EntitySay(Owner, message); + } + + public bool UseEntity(UseEntityEventArgs eventArgs) + { + RadioOn = !RadioOn; + if(RadioOn) + { + _notifyManager.PopupMessage(Owner, eventArgs.User, "The radio is now on."); + } + else + { + _notifyManager.PopupMessage(Owner, eventArgs.User, "The radio is now off."); + } + return true; + } + + public void HeardSpeech(string speech, IEntity source) + { + PassOnMessage(speech); + } + + public int GetListenRange() + { + return _listenRange; + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/ListeningSystem.cs b/Content.Server/GameObjects/EntitySystems/ListeningSystem.cs new file mode 100644 index 0000000000..e4e518e1a8 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ListeningSystem.cs @@ -0,0 +1,38 @@ +using Content.Server.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Server.GameObjects.EntitySystems +{ + class ListeningSystem : EntitySystem + { +#pragma warning disable 649 + [Dependency] private readonly IMapManager _mapManager; + [Dependency] private readonly IEntitySystemManager _entitySystemManager; +#pragma warning restore 649 + + public override void Initialize() + { + base.Initialize(); + EntityQuery = new TypeEntityQuery(typeof(ListeningComponent)); + } + + public void PingListeners(IEntity source, GridCoordinates sourcePos, string message) + { + foreach (var listener in RelevantEntities) + { + var dist = sourcePos.Distance(_mapManager, listener.Transform.GridPosition); + + listener.GetComponent() + .PassSpeechData(message, source, dist); + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/RadioSystem.cs b/Content.Server/GameObjects/EntitySystems/RadioSystem.cs new file mode 100644 index 0000000000..0d8ddf85db --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/RadioSystem.cs @@ -0,0 +1,49 @@ +using Content.Server.GameObjects.Components.Interactable; +using JetBrains.Annotations; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Content.Server.GameObjects.EntitySystems +{ + class RadioSystem : EntitySystem + { + private List _messages; + + public override void Initialize() + { + base.Initialize(); + + EntityQuery = new TypeEntityQuery(typeof(RadioComponent)); + _messages = new List(); + } + + public void SpreadMessage(IEntity source, string message) + { + if (_messages.Contains(message)) + { + return; + } + + _messages.Add(message); + + foreach (var radioEntity in RelevantEntities) + { + var radio = radioEntity.GetComponent(); + if (radioEntity == source || !radio.RadioOn) + { + continue; + } + + radio.Speaker(message); + } + + _messages.Remove(message); + } + } +} diff --git a/Content.Server/Interfaces/IListen.cs b/Content.Server/Interfaces/IListen.cs new file mode 100644 index 0000000000..c778c0adee --- /dev/null +++ b/Content.Server/Interfaces/IListen.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Interfaces.GameObjects; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Content.Server.Interfaces +{ + /// + /// Interface for objects such as radios meant to have an effect when speech is heard. + /// + public interface IListen + { + void HeardSpeech(string speech, IEntity source); + + int GetListenRange(); + } +} diff --git a/Resources/Prototypes/Entities/Objects/radio.yml b/Resources/Prototypes/Entities/Objects/radio.yml new file mode 100644 index 0000000000..f087bc6a2c --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/radio.yml @@ -0,0 +1,18 @@ +- type: entity + name: "baseradio" + parent: BaseItem + id: BaseRadio + abstract: true + +- type: entity + name: "Handheld Radio" + description: A handy handheld radio. + parent: BaseRadio + id: handrad + components: + - type: Sprite + texture: Objects/Devices/radio.png + - type: Icon + texture: Objects/Devices/radio.png + - type: Listening + - type: Radio diff --git a/Resources/Textures/Objects/Devices/radio.png b/Resources/Textures/Objects/Devices/radio.png new file mode 100644 index 0000000000..eb57264b85 Binary files /dev/null and b/Resources/Textures/Objects/Devices/radio.png differ