Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Port] Collective mind #69

Merged
merged 29 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions Content.Client/Chat/CollectiveMindSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Content.Client.Chat.Managers;
using Content.Shared.CollectiveMind;
using Robust.Client.Player;

namespace Content.Client.Chat
{
public sealed class CollectiveMindSystem : EntitySystem
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CollectiveMindComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<CollectiveMindComponent, ComponentRemove>(OnRemove);
}

public bool IsCollectiveMind => CompOrNull<CollectiveMindComponent>(_playerManager.LocalPlayer?.ControlledEntity) != null;

private void OnInit(EntityUid uid, CollectiveMindComponent component, ComponentInit args)
{
_chatManager.UpdatePermissions();
}

private void OnRemove(EntityUid uid, CollectiveMindComponent component, ComponentRemove args)
{
_chatManager.UpdatePermissions();
}
}
}
10 changes: 10 additions & 0 deletions Content.Client/Chat/Managers/ChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ internal sealed class ChatManager : IChatManager
[Dependency] private readonly IEntitySystemManager _systems = default!;

private ISawmill _sawmill = default!;
public event Action? PermissionsUpdated;

public void Initialize()
{
Expand Down Expand Up @@ -77,8 +78,17 @@ public void SendMessage(string text, ChatSelectChannel channel)
_consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\"");
break;

case ChatSelectChannel.CollectiveMind:
_consoleHost.ExecuteCommand($"cmsay \"{CommandParsing.Escape(str)}\"");
break;

default:
throw new ArgumentOutOfRangeException(nameof(channel), channel, null);
}
}

public void UpdatePermissions()
{
PermissionsUpdated?.Invoke();
}
}
8 changes: 8 additions & 0 deletions Content.Client/Chat/Managers/IChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ namespace Content.Client.Chat.Managers
{
public interface IChatManager : ISharedChatManager
{
void Initialize();

/// <summary>
/// Will refresh perms.
/// </summary>
event Action PermissionsUpdated;

public void SendMessage(string text, ChatSelectChannel channel);
public void UpdatePermissions();
}
}
12 changes: 11 additions & 1 deletion Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public sealed class ChatUIController : UIController

[UISystemDependency] private readonly ExamineSystem? _examine = default;
[UISystemDependency] private readonly GhostSystem? _ghost = default;
[UISystemDependency] private readonly CollectiveMindSystem? _collectiveMind = default!;
[UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default;
[UISystemDependency] private readonly ChatSystem? _chatSys = default;
[UISystemDependency] private readonly TransformSystem? _transform = default;
Expand All @@ -85,7 +86,8 @@ public sealed class ChatUIController : UIController
{SharedChatSystem.EmotesAltPrefix, ChatSelectChannel.Emotes},
{SharedChatSystem.AdminPrefix, ChatSelectChannel.Admin},
{SharedChatSystem.RadioCommonPrefix, ChatSelectChannel.Radio},
{SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead}
{SharedChatSystem.DeadPrefix, ChatSelectChannel.Dead},
{SharedChatSystem.CollectiveMindPrefix, ChatSelectChannel.CollectiveMind}
};

public static readonly Dictionary<ChatSelectChannel, char> ChannelPrefixes = new()
Expand Down Expand Up @@ -557,9 +559,17 @@ private void UpdateChannelPermissions()
FilterableChannels |= ChatChannel.Admin;
FilterableChannels |= ChatChannel.AdminAlert;
FilterableChannels |= ChatChannel.AdminChat;
FilterableChannels |= ChatChannel.CollectiveMind;
CanSendChannels |= ChatSelectChannel.Admin;
}

// collective mind
if (_collectiveMind != null && _collectiveMind.IsCollectiveMind)
{
FilterableChannels |= ChatChannel.CollectiveMind;
CanSendChannels |= ChatSelectChannel.CollectiveMind;
}

SelectableChannels = CanSendChannels;

// Necessary so that we always have a channel to fall back to.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public sealed partial class ChannelFilterPopup : Popup
ChatChannel.Whisper,
ChatChannel.Emotes,
ChatChannel.Radio,
ChatChannel.CollectiveMind,
ChatChannel.Notifications,
ChatChannel.LOOC,
ChatChannel.OOC,
Expand Down
43 changes: 43 additions & 0 deletions Content.Server/Chat/Commands/CollectiveMindCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Content.Server.Chat.Systems;
using Content.Shared.Administration;
using Robust.Shared.Player;
using Robust.Shared.Console;
using Robust.Shared.Enums;

namespace Content.Server.Chat.Commands
{
[AnyCommand]
internal sealed class CollectiveMindCommand : IConsoleCommand
{
public string Command => "cmsay";
public string Description => "Send chat messages to the collective mind.";
public string Help => "cmsay <text>";

public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (shell.Player is not ICommonSession player)
{
shell.WriteError("This command cannot be run from the server.");
return;
}

if (player.Status != SessionStatus.InGame)
return;

if (player.AttachedEntity is not {} playerEntity)
{
shell.WriteError("You don't have an entity!");
return;
}

if (args.Length < 1)
return;

var message = string.Join(" ", args).Trim();
if (string.IsNullOrEmpty(message))
return;

EntitySystem.Get<ChatSystem>().TrySendInGameICMessage(playerEntity, message, InGameICChatType.CollectiveMind, ChatTransmitRange.Normal);
}
}
}
57 changes: 56 additions & 1 deletion Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Text;
Expand All @@ -15,6 +16,7 @@
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.CollectiveMind;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.Ghost;
Expand Down Expand Up @@ -256,6 +258,9 @@ public void TrySendInGameICMessage(
case InGameICChatType.Emote:
SendEntityEmote(source, message, range, nameOverride, hideLog: hideLog, ignoreActionBlocker: ignoreActionBlocker);
break;
case InGameICChatType.CollectiveMind:
SendCollectiveMindChat(source, message, false);
break;
}
}

Expand Down Expand Up @@ -428,6 +433,55 @@ public void DispatchStationAnnouncement(

#region Private API

public void SendCollectiveMindChat(EntityUid source, string message, bool hideChat)
{
if (!TryComp<CollectiveMindComponent>(source, out var sourseCollectiveMindComp) || !_prototypeManager.TryIndex<RadioChannelPrototype>(sourseCollectiveMindComp.Channel, out var radioChannelProto))
return;

var clients = Filter.Empty();
var mindQuery = EntityQueryEnumerator<CollectiveMindComponent, ActorComponent>();
while (mindQuery.MoveNext(out var uid, out var collectMindComp, out var actorComp))
{
if (collectMindComp.Channel == sourseCollectiveMindComp.Channel)
{
clients.AddPlayer(actorComp.PlayerSession);
}
}

var admins = _adminManager.ActiveAdmins.Select(p => p.Channel);
string messageWrap;
string adminMessageWrap;

messageWrap = Loc.GetString("chat-manager-send-collective-mind-chat-wrap-message",
("message", message),
("channel", sourseCollectiveMindComp.Channel));

adminMessageWrap = Loc.GetString("chat-manager-send-collective-mind-chat-wrap-message-admin",
("source", source),
("message", message),
("channel", sourseCollectiveMindComp.Channel));

_adminLogger.Add(LogType.Chat, LogImpact.Low, $"CollectiveMind chat from {ToPrettyString(source):Player}: {message}");

_chatManager.ChatMessageToManyFiltered(clients,
ChatChannel.CollectiveMind,
message,
messageWrap,
source,
hideChat,
true,
radioChannelProto.Color);

_chatManager.ChatMessageToMany(ChatChannel.CollectiveMind,
message,
adminMessageWrap,
source,
hideChat,
true,
admins,
radioChannelProto.Color);
}

private void SendEntitySpeak(
EntityUid source,
string originalMessage,
Expand Down Expand Up @@ -989,7 +1043,8 @@ public enum InGameICChatType : byte
{
Speak,
Emote,
Whisper
Whisper,
CollectiveMind
}

/// <summary>
Expand Down
9 changes: 7 additions & 2 deletions Content.Shared/Chat/ChatChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,21 @@ public enum ChatChannel : ushort
/// Admin chat
/// </summary>
AdminChat = 1 << 13,

/// <summary>
/// Collective mind channel for entities who have comp.
/// </summary>
CollectiveMind = 1 << 14,

/// <summary>
/// Unspecified.
/// </summary>
Unspecified = 1 << 14,
Unspecified = 1 << 15,

/// <summary>
/// Channels considered to be IC.
/// </summary>
IC = Local | Whisper | Radio | Dead | Emotes | Damage | Visual | Notifications,
IC = Local | Whisper | Radio | Dead | Emotes | Damage | Visual | CollectiveMind | Notifications,

AdminRelated = Admin | AdminAlert | AdminChat,
}
Expand Down
5 changes: 5 additions & 0 deletions Content.Shared/Chat/ChatSelectChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public enum ChatSelectChannel : ushort
/// </summary>
Emotes = ChatChannel.Emotes,

/// <summary>
/// CollectiveMind
/// </summary>
CollectiveMind = ChatChannel.CollectiveMind,

/// <summary>
/// Deadchat
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions Content.Shared/Chat/SharedChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public abstract class SharedChatSystem : EntitySystem
public const char EmotesAltPrefix = '*';
public const char AdminPrefix = ']';
public const char WhisperPrefix = ',';
public const char CollectiveMindPrefix = '+';

public const char DefaultChannelKey = 'h';

[ValidatePrototypeId<RadioChannelPrototype>]
Expand Down
13 changes: 13 additions & 0 deletions Content.Shared/CollectiveMind/CollectiveMindComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Content.Shared.Radio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.CollectiveMind
{
[RegisterComponent, NetworkedComponent]
public sealed partial class CollectiveMindComponent : Component
{
[DataField("channel", required: true)]
public ProtoId<RadioChannelPrototype> Channel;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ objective-condition-abduct-description = (use the Gizmo on a subdued victim, the

abductor-role-greeting = I am a professional combat scientist of a high-tech race. My task is to abduct humans, conduct experiments on them, and return them intact for the purity of the experiment. It is not in my interest to destroy the station, kill, or assist the crew.

roles-antag-abductor-objective = Find the nuke disk and blow up the station.
roles-antag-abductor-objective = Kidnap station crew and perform your experiments on them!
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/chat/managers/chat-manager.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ chat-manager-send-hook-ooc-wrap-message = OOC: [bold](D){$senderName}:[/bold] {$
chat-manager-dead-channel-name = DEAD
chat-manager-admin-channel-name = ADMIN

chat-manager-send-collective-mind-chat-wrap-message = {$channel} collective mind: {$message}
chat-manager-send-collective-mind-chat-wrap-message-admin = {$source} ({$channel} collective mind): {$message}
chat-manager-collective-mind-channel-name = collective mind

chat-manager-rate-limited = You are sending messages too quickly!
chat-manager-rate-limit-admin-announcement = Player { $player } breached chat rate limits. Watch them if this is a regular occurence.

Expand Down
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/chat/ui/chat-box.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ hud-chatbox-select-channel-Whisper = Whisper
hud-chatbox-select-channel-LOOC = LOOC
hud-chatbox-select-channel-OOC = OOC
hud-chatbox-select-channel-Damage = Damage
hud-chatbox-select-channel-CollectiveMind = Collective Mind
hud-chatbox-select-channel-Visual = Actions
hud-chatbox-select-channel-Radio = Radio

Expand All @@ -28,6 +29,7 @@ hud-chatbox-channel-OOC = OOC
hud-chatbox-channel-Radio = Radio
hud-chatbox-channel-Notifications = Notifications
hud-chatbox-channel-Server = Server
hud-chatbox-channel-CollectiveMind = Collective Mind
hud-chatbox-channel-Visual = Actions
hud-chatbox-channel-Damage = Damage
hud-chatbox-channel-Unspecified = Unspecified
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/Mobs/Species/diona.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
id: BaseMobDiona
abstract: true
components:
- type: CollectiveMind
channel: Dionas
- type: HumanoidAppearance
species: Diona
- type: Hunger
Expand Down
7 changes: 7 additions & 0 deletions Resources/Prototypes/radio_channels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,3 +95,10 @@
color: "#f6ce64"
# long range since otherwise it'd defeat the point of a handheld radio independent of telecomms
longRange: true

- type: radioChannel
id: Dionas
name: chat-radio-dionas
keycode: 'd'
color: "#025c0f"
longRange: true
Loading