]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Cleanup TipsSystem, add TippyOnTrigger (#41477)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Tue, 18 Nov 2025 18:09:49 +0000 (19:09 +0100)
committerGitHub <noreply@github.com>
Tue, 18 Nov 2025 18:09:49 +0000 (18:09 +0000)
* tippy on trigger and refactor

* optional parameter for command

Content.Client/Tips/TipsSystem.cs [new file with mode: 0644]
Content.Server/Administration/Commands/TippyCommand.cs [new file with mode: 0644]
Content.Server/Tips/TipsSystem.cs
Content.Shared/Tips/SharedTipsSystem.cs [new file with mode: 0644]
Content.Shared/Tips/TippyEvent.cs
Content.Shared/Trigger/Components/Effects/TippyOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/TippyOnTriggerSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/commands/tippy-command.ftl
Resources/engineCommandPerms.yml

diff --git a/Content.Client/Tips/TipsSystem.cs b/Content.Client/Tips/TipsSystem.cs
new file mode 100644 (file)
index 0000000..00fc084
--- /dev/null
@@ -0,0 +1,5 @@
+using Content.Shared.Tips;
+
+namespace Content.Client.Tips;
+
+public sealed class TipsSystem : SharedTipsSystem;
diff --git a/Content.Server/Administration/Commands/TippyCommand.cs b/Content.Server/Administration/Commands/TippyCommand.cs
new file mode 100644 (file)
index 0000000..b79c160
--- /dev/null
@@ -0,0 +1,103 @@
+using Content.Shared.Administration;
+using Content.Shared.Tips;
+using Robust.Server.Player;
+using Robust.Shared.Console;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Fun)]
+public sealed class TippyCommand : LocalizedEntityCommands
+{
+    [Dependency] private readonly SharedTipsSystem _tips = default!;
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+    [Dependency] private readonly IPlayerManager _player = default!;
+
+    public override string Command => "tippy";
+
+    public override void Execute(IConsoleShell shell, string argStr, string[] args)
+    {
+        if (args.Length < 2)
+        {
+            shell.WriteLine(Loc.GetString("cmd-tippy-help"));
+            return;
+        }
+
+        ICommonSession? targetSession = null;
+        if (args[0] != "all")
+        {
+            if (!_player.TryGetSessionByUsername(args[0], out targetSession))
+            {
+                shell.WriteLine(Loc.GetString("cmd-tippy-error-no-user"));
+                return;
+            }
+        }
+
+        var msg = args[1];
+
+        EntProtoId? prototype = null;
+        if (args.Length > 2)
+        {
+            if (args[2] == "null")
+                prototype = null;
+            else if (!_prototype.HasIndex<EntityPrototype>(args[2]))
+            {
+                shell.WriteError(Loc.GetString("cmd-tippy-error-no-prototype", ("proto", args[2])));
+                return;
+            }
+            else
+                prototype = args[2];
+        }
+
+        var speakTime = _tips.GetSpeechTime(msg);
+        var slideTime = 3f;
+        var waddleInterval = 0.5f;
+
+        if (args.Length > 3 && float.TryParse(args[3], out var parsedSpeakTime))
+            speakTime = parsedSpeakTime;
+
+        if (args.Length > 4 && float.TryParse(args[4], out var parsedSlideTime))
+            slideTime = parsedSlideTime;
+
+        if (args.Length > 5 && float.TryParse(args[5], out var parsedWaddleInterval))
+            waddleInterval = parsedWaddleInterval;
+
+        if (targetSession != null) // send to specified player
+            _tips.SendTippy(targetSession, msg, prototype, speakTime, slideTime, waddleInterval);
+        else // send to everyone
+            _tips.SendTippy(msg, prototype, speakTime, slideTime, waddleInterval);
+    }
+
+    public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+    {
+        return args.Length switch
+        {
+            1 => CompletionResult.FromHintOptions(
+                CompletionHelper.SessionNames(players: _player),
+                Loc.GetString("cmd-tippy-auto-1")),
+            2 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-2")),
+            3 => CompletionResult.FromHintOptions(
+                CompletionHelper.PrototypeIdsLimited<EntityPrototype>(args[2], _prototype),
+                Loc.GetString("cmd-tippy-auto-3")),
+            4 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-4")),
+            5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
+            6 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-6")),
+            _ => CompletionResult.Empty
+        };
+    }
+}
+
+[AdminCommand(AdminFlags.Fun)]
+public sealed class TipCommand : LocalizedEntityCommands
+{
+    [Dependency] private readonly SharedTipsSystem _tips = default!;
+
+    public override string Command => "tip";
+
+    public override void Execute(IConsoleShell shell, string argStr, string[] args)
+    {
+        _tips.AnnounceRandomTip();
+        _tips.RecalculateNextTipTime();
+    }
+}
index cd7f7b52f548f25c5813b58843c964e9e6740dc0..406ee76ca6b2bccd0a1ba3eb88585154699b2b21 100644 (file)
@@ -4,10 +4,7 @@ using Content.Shared.CCVar;
 using Content.Shared.Chat;
 using Content.Shared.Dataset;
 using Content.Shared.Tips;
-using Robust.Server.GameObjects;
-using Robust.Server.Player;
 using Robust.Shared.Configuration;
-using Robust.Shared.Console;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
@@ -15,10 +12,7 @@ using Robust.Shared.Timing;
 
 namespace Content.Server.Tips;
 
-/// <summary>
-///     Handles periodically displaying gameplay tips to all players ingame.
-/// </summary>
-public sealed class TipsSystem : EntitySystem
+public sealed class TipsSystem : SharedTipsSystem
 {
     [Dependency] private readonly IChatManager _chat = default!;
     [Dependency] private readonly IPrototypeManager _prototype = default!;
@@ -26,8 +20,6 @@ public sealed class TipsSystem : EntitySystem
     [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly GameTicker _ticker = default!;
-    [Dependency] private readonly IConsoleHost _conHost = default!;
-    [Dependency] private readonly IPlayerManager _playerManager = default!;
 
     private bool _tipsEnabled;
     private float _tipTimeOutOfRound;
@@ -35,16 +27,6 @@ public sealed class TipsSystem : EntitySystem
     private string _tipsDataset = "";
     private float _tipTippyChance;
 
-    /// <summary>
-    /// Always adds this time to a speech message. This is so really short message stay around for a bit.
-    /// </summary>
-    private const float SpeechBuffer = 3f;
-
-    /// <summary>
-    /// Expected reading speed.
-    /// </summary>
-    private const float Wpm = 180f;
-
     [ViewVariables(VVAccess.ReadWrite)]
     private TimeSpan _nextTipTime = TimeSpan.Zero;
 
@@ -53,110 +35,45 @@ public sealed class TipsSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<GameRunLevelChangedEvent>(OnGameRunLevelChanged);
-        Subs.CVar(_cfg, CCVars.TipFrequencyOutOfRound, SetOutOfRound, true);
-        Subs.CVar(_cfg, CCVars.TipFrequencyInRound, SetInRound, true);
         Subs.CVar(_cfg, CCVars.TipsEnabled, SetEnabled, true);
-        Subs.CVar(_cfg, CCVars.TipsDataset, SetDataset, true);
-        Subs.CVar(_cfg, CCVars.TipsTippyChance, SetTippyChance, true);
+        Subs.CVar(_cfg, CCVars.TipFrequencyOutOfRound, value => _tipTimeOutOfRound = value, true);
+        Subs.CVar(_cfg, CCVars.TipFrequencyInRound, value => _tipTimeInRound = value, true);
+        Subs.CVar(_cfg, CCVars.TipsDataset, value => _tipsDataset = value, true);
+        Subs.CVar(_cfg, CCVars.TipsTippyChance, value => _tipTippyChance = value, true);
 
         RecalculateNextTipTime();
-        _conHost.RegisterCommand("tippy", Loc.GetString("cmd-tippy-desc"), Loc.GetString("cmd-tippy-help"), SendTippy, SendTippyHelper);
-        _conHost.RegisterCommand("tip", Loc.GetString("cmd-tip-desc"), "tip", SendTip);
     }
 
-    private CompletionResult SendTippyHelper(IConsoleShell shell, string[] args)
+    private void OnGameRunLevelChanged(GameRunLevelChangedEvent ev)
     {
-        return args.Length switch
+        // reset for lobby -> inround
+        // reset for inround -> post but not post -> lobby
+        if (ev.New == GameRunLevel.InRound || ev.Old == GameRunLevel.InRound)
         {
-            1 => CompletionResult.FromHintOptions(
-                CompletionHelper.SessionNames(players: _playerManager),
-                Loc.GetString("cmd-tippy-auto-1")),
-            2 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-2")),
-            3 => CompletionResult.FromHintOptions(
-                CompletionHelper.PrototypeIdsLimited<EntityPrototype>(args[2], _prototype),
-                Loc.GetString("cmd-tippy-auto-3")),
-            4 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-4")),
-            5 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-5")),
-            6 => CompletionResult.FromHint(Loc.GetString("cmd-tippy-auto-6")),
-            _ => CompletionResult.Empty
-        };
+            RecalculateNextTipTime();
+        }
     }
 
-    private void SendTip(IConsoleShell shell, string argstr, string[] args)
+    private void SetEnabled(bool value)
     {
-        AnnounceRandomTip();
-        RecalculateNextTipTime();
+        _tipsEnabled = value;
+
+        if (_nextTipTime != TimeSpan.Zero)
+            RecalculateNextTipTime();
     }
 
-    private void SendTippy(IConsoleShell shell, string argstr, string[] args)
+    public override void RecalculateNextTipTime()
     {
-        if (args.Length < 2)
-        {
-            shell.WriteLine(Loc.GetString("cmd-tippy-help"));
-            return;
-        }
-
-        ActorComponent? actor = null;
-        if (args[0] != "all")
+        if (_ticker.RunLevel == GameRunLevel.InRound)
         {
-            ICommonSession? session;
-            if (args.Length > 0)
-            {
-                // Get player entity
-                if (!_playerManager.TryGetSessionByUsername(args[0], out session))
-                {
-                    shell.WriteLine(Loc.GetString("cmd-tippy-error-no-user"));
-                    return;
-                }
-            }
-            else
-            {
-                session = shell.Player;
-            }
-
-            if (session?.AttachedEntity is not { } user)
-            {
-                shell.WriteLine(Loc.GetString("cmd-tippy-error-no-user"));
-                return;
-            }
-
-            if (!TryComp(user, out actor))
-            {
-                shell.WriteError(Loc.GetString("cmd-tippy-error-no-user"));
-                return;
-            }
+            _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeInRound);
         }
-
-        var ev = new TippyEvent(args[1]);
-
-        if (args.Length > 2)
+        else
         {
-            ev.Proto = args[2];
-            if (!_prototype.HasIndex<EntityPrototype>(args[2]))
-            {
-                shell.WriteError(Loc.GetString("cmd-tippy-error-no-prototype", ("proto", args[2])));
-                return;
-            }
+            _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeOutOfRound);
         }
-
-        if (args.Length > 3)
-            ev.SpeakTime = float.Parse(args[3]);
-        else
-            ev.SpeakTime = GetSpeechTime(ev.Msg);
-
-        if (args.Length > 4)
-            ev.SlideTime = float.Parse(args[4]);
-
-        if (args.Length > 5)
-            ev.WaddleInterval = float.Parse(args[5]);
-
-        if (actor != null)
-            RaiseNetworkEvent(ev, actor.PlayerSession);
-        else
-            RaiseNetworkEvent(ev);
     }
 
-
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
@@ -171,41 +88,30 @@ public sealed class TipsSystem : EntitySystem
         }
     }
 
-    private void SetOutOfRound(float value)
+    public override void SendTippy(
+        string message,
+        EntProtoId? prototype = null,
+        float speakTime = 5f,
+        float slideTime = 3f,
+        float waddleInterval = 0.5f)
     {
-        _tipTimeOutOfRound = value;
+        var ev = new TippyEvent(message, prototype, speakTime, slideTime, waddleInterval);
+        RaiseNetworkEvent(ev);
     }
 
-    private void SetInRound(float value)
+    public override void SendTippy(
+        ICommonSession session,
+        string message,
+        EntProtoId? prototype = null,
+        float speakTime = 5f,
+        float slideTime = 3f,
+        float waddleInterval = 0.5f)
     {
-        _tipTimeInRound = value;
+        var ev = new TippyEvent(message, prototype, speakTime, slideTime, waddleInterval);
+        RaiseNetworkEvent(ev, session);
     }
 
-    private void SetEnabled(bool value)
-    {
-        _tipsEnabled = value;
-
-        if (_nextTipTime != TimeSpan.Zero)
-            RecalculateNextTipTime();
-    }
-
-    private void SetDataset(string value)
-    {
-        _tipsDataset = value;
-    }
-
-    private void SetTippyChance(float value)
-    {
-        _tipTippyChance = value;
-    }
-
-    public static float GetSpeechTime(string text)
-    {
-        var wordCount = (float)text.Split().Length;
-        return SpeechBuffer + wordCount * (60f / Wpm);
-    }
-
-    private void AnnounceRandomTip()
+    public override void AnnounceRandomTip()
     {
         if (!_prototype.TryIndex<LocalizedDatasetPrototype>(_tipsDataset, out var tips))
             return;
@@ -215,35 +121,20 @@ public sealed class TipsSystem : EntitySystem
 
         if (_random.Prob(_tipTippyChance))
         {
-            var ev = new TippyEvent(msg);
-            ev.SpeakTime = GetSpeechTime(msg);
-            RaiseNetworkEvent(ev);
-        } else
-        {
-            _chat.ChatMessageToManyFiltered(Filter.Broadcast(), ChatChannel.OOC, tip, msg,
-            EntityUid.Invalid, false, false, Color.MediumPurple);
-        }
-    }
-
-    private void RecalculateNextTipTime()
-    {
-        if (_ticker.RunLevel == GameRunLevel.InRound)
-        {
-            _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeInRound);
+            var speakTime = GetSpeechTime(msg);
+            SendTippy(msg, speakTime: speakTime);
         }
         else
         {
-            _nextTipTime = _timing.CurTime + TimeSpan.FromSeconds(_tipTimeOutOfRound);
-        }
-    }
-
-    private void OnGameRunLevelChanged(GameRunLevelChangedEvent ev)
-    {
-        // reset for lobby -> inround
-        // reset for inround -> post but not post -> lobby
-        if (ev.New == GameRunLevel.InRound || ev.Old == GameRunLevel.InRound)
-        {
-            RecalculateNextTipTime();
+            _chat.ChatMessageToManyFiltered(
+                Filter.Broadcast(),
+                ChatChannel.OOC,
+                tip,
+                msg,
+                EntityUid.Invalid,
+                false,
+                false,
+                Color.MediumPurple);
         }
     }
 }
diff --git a/Content.Shared/Tips/SharedTipsSystem.cs b/Content.Shared/Tips/SharedTipsSystem.cs
new file mode 100644 (file)
index 0000000..1973aae
--- /dev/null
@@ -0,0 +1,74 @@
+using Content.Shared.CCVar;
+using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Tips;
+
+/// <summary>
+/// Handles periodically displaying gameplay tips to all players ingame.
+/// </summary>
+public abstract class SharedTipsSystem : EntitySystem
+{
+    /// <summary>
+    /// Always adds this time to a speech message. This is so really short message stay around for a bit.
+    /// </summary>
+    private const float SpeechBuffer = 3f;
+
+    /// <summary>
+    /// Expected reading speed.
+    /// </summary>
+    private const float Wpm = 180f;
+
+    /// <summary>
+    /// Send a tippy message to all clients.
+    /// </summary>
+    /// <param name="message">The text to show in the speech bubble.</param>
+    /// <param name="prototype">The entity to show. Defaults to tippy.</param>
+    /// <param name="speakTime">The time the speech bubble is shown, in seconds.</param>
+    /// <param name="slideTime">The time the entity takes to walk onto the screen, in seconds.</param>
+    /// <param name="waddleInterval">The time between waddle animation steps, in seconds.</param>
+    public virtual void SendTippy(
+        string message,
+        EntProtoId? prototype = null,
+        float speakTime = 5f,
+        float slideTime = 3f,
+        float waddleInterval = 0.5f)
+    { }
+
+    /// <summary>
+    /// Send a tippy message to the given player session.
+    /// </summary>
+    /// <param name="session">The player session to send the message to.</param>
+    /// <param name="message">The text to show in the speech bubble.</param>
+    /// <param name="prototype">The entity to show. Defaults to tippy.</param>
+    /// <param name="speakTime">The time the speech bubble is shown, in seconds.</param>
+    /// <param name="slideTime">The time the entity takes to walk onto the screen, in seconds.</param>
+    /// <param name="waddleInterval">The time between waddle animation steps, in seconds.</param>
+    public virtual void SendTippy(
+        ICommonSession session,
+        string message,
+        EntProtoId? prototype = null,
+        float speakTime = 5f,
+        float slideTime = 3f,
+        float waddleInterval = 0.5f)
+    { }
+
+    /// <summary>
+    /// Send a random tippy message from the dataset given in <see cref="CCVars.TipsDataset"/>.
+    /// </summary>
+    public virtual void AnnounceRandomTip() { }
+
+    /// <summary>
+    /// Set a random time stamp for the next automatic game tip.
+    /// </summary>
+    public virtual void RecalculateNextTipTime() { }
+
+    /// <summary>
+    /// Calculate the recommended speak time for a given message.
+    /// </summary>
+    public float GetSpeechTime(string text)
+    {
+        var wordCount = (float)text.Split().Length;
+        return SpeechBuffer + wordCount * (60f / Wpm);
+    }
+}
index e5dc0b7f35b22e8485a097b02ed1884d9fdbb928..c6b028aa312d0c356ce34ec57ea5f3cd3b39da35 100644 (file)
@@ -1,21 +1,37 @@
 
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Tips;
 
+/// <summary>
+/// Networked event that makes a client show a message on their screen using tippy or another protoype.
+/// </summary>
 [Serializable, NetSerializable]
-public sealed class TippyEvent : EntityEventArgs
+public sealed class TippyEvent(string msg, EntProtoId? proto, float speakTime, float slideTime, float waddleInterval) : EntityEventArgs
 {
-    public TippyEvent(string msg)
-    {
-        Msg = msg;
-    }
+    /// <summary>
+    /// The text to show in the speech bubble.
+    /// </summary>
+    public string Msg = msg;
 
-    public string Msg;
-    public string? Proto;
+    /// <summary>
+    /// The entity to show. Defaults to tippy.
+    /// </summary>
+    public EntProtoId? Proto = proto;
 
-    // TODO: Why are these defaults even here, have the caller specify. This get overriden only most of the time.
-    public float SpeakTime = 5;
-    public float SlideTime = 3;
-    public float WaddleInterval = 0.5f;
+    /// <summary>
+    /// The time the speech bubble is shown, in seconds.
+    /// </summary>
+    public float SpeakTime = speakTime;
+
+    /// <summary>
+    /// The time the entity takes to walk onto the screen, in seconds.
+    /// </summary>
+    public float SlideTime = slideTime;
+
+    /// <summary>
+    /// The time between waddle animation steps, in seconds.
+    /// </summary>
+    public float WaddleInterval = waddleInterval;
 }
diff --git a/Content.Shared/Trigger/Components/Effects/TippyOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/TippyOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..9476d9e
--- /dev/null
@@ -0,0 +1,66 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+/// <summary>
+/// Sends a tippy message to either the entity or all players when triggered.
+/// If TargetUser is true the user will receive the message.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class TippyOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <summary>
+    /// Unlocalized message text to send to the player(s).
+    /// Intended only for admeme purposes. For anything else you should use <see cref="LocMessage"/> instead.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public string Message = string.Empty;
+
+    /// <summary>
+    /// Localized message text to send to the player(s).
+    /// This has priority over <see cref="Message"/>.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public LocId? LocMessage;
+
+    /// <summary>
+    /// If true the message will be send to all players.
+    /// If false it will be send to the user or owning entity, depending on <see cref="BaseXOnTriggerComponent.TargetUser"/>.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool SendToAll;
+
+    /// <summary>
+    /// The entity prototype to show to the client.
+    /// Will default to tippy if null.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntProtoId? Prototype;
+
+    /// <summary>
+    /// Use the prototype of the entity owning this component?
+    /// Will take priority over <see cref="Prototype"/>.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool UseOwnerPrototype;
+
+    /// <summary>
+    /// The time the speech bubble is shown, in seconds.
+    /// Will be calculated automatically from the message length if null.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float? SpeakTime;
+
+    /// <summary>
+    /// The time the entity takes to walk onto the screen, in seconds.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float SlideTime = 3f;
+
+    /// <summary>
+    /// The time between waddle animation steps, in seconds.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public float WaddleInterval = 0.5f;
+}
diff --git a/Content.Shared/Trigger/Systems/TippyOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/TippyOnTriggerSystem.cs
new file mode 100644 (file)
index 0000000..10c5549
--- /dev/null
@@ -0,0 +1,49 @@
+using Content.Shared.Tips;
+using Content.Shared.Trigger.Components.Effects;
+using Robust.Shared.Player;
+
+namespace Content.Shared.Trigger.Systems;
+
+public sealed class TippyOnTriggerSystem : EntitySystem
+{
+    [Dependency] private readonly SharedTipsSystem _tips = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<TippyOnTriggerComponent, TriggerEvent>(OnTrigger);
+    }
+
+    private void OnTrigger(Entity<TippyOnTriggerComponent> ent, ref TriggerEvent args)
+    {
+        if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
+            return;
+
+        var msg = ent.Comp.Message;
+        var prototype = ent.Comp.Prototype;
+
+        if (ent.Comp.LocMessage != null)
+            msg = Loc.GetString(ent.Comp.LocMessage.Value);
+
+        if (ent.Comp.UseOwnerPrototype)
+            prototype = Prototype(ent)?.ID;
+
+        var speakTime = ent.Comp.SpeakTime ?? _tips.GetSpeechTime(msg);
+
+        if (ent.Comp.SendToAll)
+        {
+            _tips.SendTippy(msg, prototype, speakTime, ent.Comp.SlideTime, ent.Comp.WaddleInterval);
+        }
+        else
+        {
+            var target = ent.Comp.TargetUser ? args.User : ent.Owner;
+            if (!TryComp<ActorComponent>(target, out var actor))
+                return;
+
+            _tips.SendTippy(actor.PlayerSession, msg, prototype, speakTime, ent.Comp.SlideTime, ent.Comp.WaddleInterval);
+        }
+
+        args.Handled = true;
+    }
+}
index 6b9a95a1dd8ea74fc37e7a079595fe7295636286..f814506009b96b5dec9c6a302360ecd3a42c24fb 100644 (file)
@@ -1,5 +1,5 @@
 cmd-tippy-desc = Broadcast a message as Tippy the clown.
-cmd-tippy-help = tippy <user | all> <message> [entity prototype] [speak time] [slide time] [waddle interval]
+cmd-tippy-help = tippy <user | all> <message> [entity prototype | null] [speak time] [slide time] [waddle interval]
 cmd-tippy-auto-1 = <user | all>
 cmd-tippy-auto-2 = message
 cmd-tippy-auto-3 = entity prototype
index 194d05d13dac11ca2700f7eea24f23aaff8f4c9b..4520a1da2d69f53c9b225ba2f2cd3f0e2fd553a4 100644 (file)
   Commands:
   - listplayers
 
-- Flags: FUN
-  Commands:
-  - tippy
-  - tip
-
 - Flags: SERVER
   Commands:
   - delete