diff --git a/Content.Client/Prayer/PrayerSystem.cs b/Content.Client/Prayer/PrayerSystem.cs new file mode 100644 index 0000000000..30f4a647da --- /dev/null +++ b/Content.Client/Prayer/PrayerSystem.cs @@ -0,0 +1,17 @@ +using Content.Shared.Administration; +using Content.Shared.Prayer; + +namespace Content.Client.Prayer; +/// +/// System to handle subtle messages and praying +/// +public sealed class PrayerSystem : SharedPrayerSystem +{ + protected override void OnPrayerTextMessage(PrayerTextMessage message, EntitySessionEventArgs eventArgs) + { + var bwoinkMessage = new SharedBwoinkSystem.BwoinkTextMessage(eventArgs.SenderSession.UserId, + eventArgs.SenderSession.UserId, message.Text); + + RaiseNetworkEvent(bwoinkMessage); + } +} diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index a03dd94513..6d36c52d1a 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -10,6 +10,7 @@ using Content.Server.Ghost.Roles; using Content.Server.Mind.Commands; using Content.Server.Mind.Components; using Content.Server.Players; +using Content.Server.Prayer; using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Administration; @@ -45,6 +46,7 @@ namespace Content.Server.Administration.Systems [Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly PrayerSystem _prayerSystem = default!; private readonly Dictionary _openSolutionUis = new(); @@ -80,6 +82,21 @@ namespace Content.Server.Administration.Systems verb.Impact = LogImpact.Low; args.Verbs.Add(verb); + // Subtle Messages + Verb prayerVerb = new(); + prayerVerb.Text = Loc.GetString("prayer-verbs-subtle-message"); + prayerVerb.Category = VerbCategory.Admin; + prayerVerb.IconTexture = "/Textures/Interface/pray.svg.png"; + prayerVerb.Act = () => + { + _quickDialog.OpenDialog(player, "Subtle Message", "Message", "Popup Message", (string message, string popupMessage) => + { + _prayerSystem.SendSubtleMessage(targetActor.PlayerSession, message, popupMessage == "" ? Loc.GetString("prayer-popup-subtle-default") : popupMessage); + }); + }; + prayerVerb.Impact = LogImpact.Low; + args.Verbs.Add(prayerVerb); + // Freeze var frozen = HasComp(args.Target); args.Verbs.Add(new Verb diff --git a/Content.Server/Prayer/PrayableComponent.cs b/Content.Server/Prayer/PrayableComponent.cs new file mode 100644 index 0000000000..b174bb8c28 --- /dev/null +++ b/Content.Server/Prayer/PrayableComponent.cs @@ -0,0 +1,17 @@ +namespace Content.Server.Prayer +{ + /// + /// Allows an entity to be prayed on in the context menu + /// + [RegisterComponent] + public sealed class PrayableComponent : Component + { + /// + /// If bible users are only allowed to use this prayable entity + /// + [DataField("bibleUserOnly")] + [ViewVariables(VVAccess.ReadWrite)] + public bool BibleUserOnly; + } +} + diff --git a/Content.Server/Prayer/PrayerSystem.cs b/Content.Server/Prayer/PrayerSystem.cs new file mode 100644 index 0000000000..2b12f37dd9 --- /dev/null +++ b/Content.Server/Prayer/PrayerSystem.cs @@ -0,0 +1,95 @@ +using Content.Server.Administration; +using Content.Server.Bible.Components; +using Content.Server.Chat.Managers; +using Content.Server.Popups; +using Content.Shared.Database; +using Content.Shared.Popups; +using Robust.Server.Player; +using Robust.Shared.Player; +using Content.Shared.Chat; +using Content.Shared.Prayer; +using Content.Shared.Verbs; +using Robust.Server.GameObjects; + +namespace Content.Server.Prayer; +/// +/// System to handle subtle messages and praying +/// +public sealed class PrayerSystem : SharedPrayerSystem +{ + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly QuickDialogSystem _quickDialog = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(AddPrayVerb); + } + + private void AddPrayVerb(EntityUid uid, PrayableComponent comp, GetVerbsEvent args) + { + // if it doesn't have an actor and we can't reach it then don't add the verb + if (!EntityManager.TryGetComponent(args.User, out var actor)) + return; + + if (!args.CanAccess) + return; + + ActivationVerb prayerVerb = new(); + prayerVerb.Text = Loc.GetString("prayer-verbs-pray"); + prayerVerb.IconTexture = "/Textures/Interface/pray.svg.png"; + prayerVerb.Act = () => + { + if (comp.BibleUserOnly && !EntityManager.TryGetComponent(args.User, out var bibleUser)) + { + _popupSystem.PopupEntity(Loc.GetString("prayer-popup-notify-locked"), uid, Filter.Empty().AddPlayer(actor.PlayerSession), PopupType.Large); + return; + } + + _quickDialog.OpenDialog(actor.PlayerSession, "Pray", "Message", (string message) => + { + Pray(actor.PlayerSession, message); + }); + }; + prayerVerb.Impact = LogImpact.Low; + args.Verbs.Add(prayerVerb); + } + + /// + /// Subtly messages a player by giving them a popup and a chat message. + /// + /// The IPlayerSession that you want to send the message to + /// The main message sent to the player via the chatbox + /// The popup to notify the player, also prepended to the messageString + public void SendSubtleMessage(IPlayerSession target, string messageString, string popupMessage) + { + if (target.AttachedEntity == null) + return; + + _popupSystem.PopupEntity(popupMessage, target.AttachedEntity.Value, Filter.Empty().AddPlayer(target), PopupType.Large); + _chatManager.ChatMessageToOne(ChatChannel.Local, messageString, popupMessage + " \"{0}\"", EntityUid.Invalid, false, target.ConnectedClient); + } + + /// + /// Sends a message to the admin channel with a message and username + /// + /// The IPlayerSession who sent the original message + /// Message to be sent to the admin chat + /// + /// You may be wondering, "Why the admin chat, specifically? Nobody even reads it!" + /// Exactly. + /// + public void Pray(IPlayerSession sender, string message) + { + if (sender.AttachedEntity == null) + return; + + _popupSystem.PopupEntity(Loc.GetString("prayer-popup-notify-sent"), sender.AttachedEntity.Value, Filter.Empty().AddPlayer(sender), PopupType.Medium); + + var networkMessage = new PrayerTextMessage(Loc.GetString("prayer-chat-notify", ("message", message))); + + RaiseNetworkEvent(networkMessage, Filter.Empty().AddPlayer(sender)); + } +} diff --git a/Content.Shared/Prayer/SharedPrayerSystem.cs b/Content.Shared/Prayer/SharedPrayerSystem.cs new file mode 100644 index 0000000000..fafdbfe043 --- /dev/null +++ b/Content.Shared/Prayer/SharedPrayerSystem.cs @@ -0,0 +1,30 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Prayer; + +/// +/// Shared system for handling Prayers +/// +public abstract class SharedPrayerSystem : EntitySystem +{ + public override void Initialize() + { + SubscribeNetworkEvent(OnPrayerTextMessage); + } + + protected virtual void OnPrayerTextMessage(PrayerTextMessage message, EntitySessionEventArgs eventArgs) + { + // Specific side code in target. + } + + [Serializable, NetSerializable] + public sealed class PrayerTextMessage : EntityEventArgs + { + public string Text { get; } + + public PrayerTextMessage(string text) + { + Text = text; + } + } +} diff --git a/Resources/Locale/en-US/prayers/prayers.ftl b/Resources/Locale/en-US/prayers/prayers.ftl new file mode 100644 index 0000000000..09e9759e03 --- /dev/null +++ b/Resources/Locale/en-US/prayers/prayers.ftl @@ -0,0 +1,7 @@ +prayer-verbs-subtle-message = Subtle Message +prayer-verbs-pray = Pray +prayer-chat-notify = PRAYER: {$message} + +prayer-popup-subtle-default = You hear a voice in your head... +prayer-popup-notify-sent = Your message has been sent to the gods... +prayer-popup-notify-locked = You don't feel worthy enough... diff --git a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml index 0b48fdf2c4..f948e85685 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml @@ -18,6 +18,8 @@ damageOnUntrainedUse: ## What a non-chaplain takes when attempting to heal someone groups: Burn: 10 + - type: Prayable + bibleUserOnly: true - type: Summonable specialItem: SpawnPointGhostRemilia - type: ItemCooldown diff --git a/Resources/Prototypes/Entities/Structures/Furniture/altar.yml b/Resources/Prototypes/Entities/Structures/Furniture/altar.yml index 061ef7cd39..77dc6a85b0 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/altar.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/altar.yml @@ -8,6 +8,7 @@ - type: Anchorable - type: Transform noRot: true + - type: Prayable - type: Damageable damageContainer: Inorganic damageModifierSet: Metallic diff --git a/Resources/Textures/Interface/pray.svg b/Resources/Textures/Interface/pray.svg new file mode 100644 index 0000000000..37023f12cd --- /dev/null +++ b/Resources/Textures/Interface/pray.svg @@ -0,0 +1,7 @@ + + + Layer 1 + + + + diff --git a/Resources/Textures/Interface/pray.svg.png b/Resources/Textures/Interface/pray.svg.png new file mode 100644 index 0000000000..c2a8df8266 Binary files /dev/null and b/Resources/Textures/Interface/pray.svg.png differ diff --git a/Resources/Textures/Interface/pray.svg.png.yml b/Resources/Textures/Interface/pray.svg.png.yml new file mode 100644 index 0000000000..dabd6601f7 --- /dev/null +++ b/Resources/Textures/Interface/pray.svg.png.yml @@ -0,0 +1,2 @@ +sample: + filter: true