Files
wwdpublic/Content.Server/Chat/Systems/AutoEmoteSystem.cs
Mnemotechnican 92dcd724d4 Refactor Sign Languages and Language Markers (#575)
# Description
This refactors #510 and #553. #553 specifically was reverted and
re-implemented from scratch. As a consequence to all of this, the chat
system was refactored a bit too, hopefully for the best.

Changes:
- InGameICChatType, InGameOOCChatType, ChatTransmitRange were all moved
to shared and made serializable
- Added a method to wrap whisper messages to reduce code duplication in
chat system
- Both WrapPublicMethod and WrapWhisperMessage call the same generic
WrapMessage method, which allows to add speech verbs to whispers and
more. That method is also fully responsible for adding language markers
and deducing speech verbs now.
- Everything related to speech was moved out of LanguagePrototype and
into SpeechOverrideInfo. LanguagePrototype now holds an instance of
that.
- Added AllowRadio, RequireSpeech, ChatTypeOverride,
SpeechVerbOverrides, MessageWrapOverrides to SpeechOverrideInfo, all of
which are used in implementing the sign language.
- Suffered a lot

# TODO
- [X] Cry
- [X] Fix the sign language not displaying properly over the character.
- [X] Find a way to circumvent being unable to speak??

<details><summary><h1>Media</h1></summary><p>


![image](https://github.com/user-attachments/assets/845ec5d3-20aa-4786-bdc8-c39c80e0a4a3)

See below

</p></details>

# Changelog
No cl no fun

---------

Signed-off-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com>
Co-authored-by: Danger Revolution! <142105406+DangerRevolution@users.noreply.github.com>
2024-07-26 13:11:42 -07:00

143 lines
4.8 KiB
C#

using System.Linq;
using Content.Shared.Chat;
using Content.Shared.Chat.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Chat.Systems;
public sealed class AutoEmoteSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AutoEmoteComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<AutoEmoteComponent, EntityUnpausedEvent>(OnUnpaused);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var curTime = _gameTiming.CurTime;
var query = EntityQueryEnumerator<AutoEmoteComponent>();
while (query.MoveNext(out var uid, out var autoEmote))
{
if (autoEmote.NextEmoteTime > curTime)
continue;
foreach (var (key, time) in autoEmote.EmoteTimers)
{
if (time > curTime)
continue;
var autoEmotePrototype = _prototypeManager.Index<AutoEmotePrototype>(key);
ResetTimer(uid, key, autoEmote, autoEmotePrototype);
if (!_random.Prob(autoEmotePrototype.Chance))
continue;
if (autoEmotePrototype.WithChat)
{
_chatSystem.TryEmoteWithChat(uid, autoEmotePrototype.EmoteId, autoEmotePrototype.HiddenFromChatWindow ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
}
else
{
_chatSystem.TryEmoteWithoutChat(uid, autoEmotePrototype.EmoteId);
}
}
}
}
private void OnMapInit(EntityUid uid, AutoEmoteComponent autoEmote, MapInitEvent args)
{
// Start timers
foreach (var autoEmotePrototypeId in autoEmote.Emotes)
{
ResetTimer(uid, autoEmotePrototypeId, autoEmote);
}
}
private void OnUnpaused(EntityUid uid, AutoEmoteComponent autoEmote, ref EntityUnpausedEvent args)
{
foreach (var key in autoEmote.EmoteTimers.Keys)
{
autoEmote.EmoteTimers[key] += args.PausedTime;
}
autoEmote.NextEmoteTime += args.PausedTime;
}
/// <summary>
/// Try to add an emote to the entity, which will be performed at an interval.
/// </summary>
public bool AddEmote(EntityUid uid, string autoEmotePrototypeId, AutoEmoteComponent? autoEmote = null)
{
if (!Resolve(uid, ref autoEmote, logMissing: false))
return false;
DebugTools.Assert(autoEmote.LifeStage <= ComponentLifeStage.Running);
if (autoEmote.Emotes.Contains(autoEmotePrototypeId))
return false;
autoEmote.Emotes.Add(autoEmotePrototypeId);
ResetTimer(uid, autoEmotePrototypeId, autoEmote);
return true;
}
/// <summary>
/// Stop preforming an emote. Note that by default this will queue empty components for removal.
/// </summary>
public bool RemoveEmote(EntityUid uid, string autoEmotePrototypeId, AutoEmoteComponent? autoEmote = null, bool removeEmpty = true)
{
if (!Resolve(uid, ref autoEmote, logMissing: false))
return false;
DebugTools.Assert(_prototypeManager.HasIndex<AutoEmotePrototype>(autoEmotePrototypeId), "Prototype not found. Did you make a typo?");
if (!autoEmote.EmoteTimers.Remove(autoEmotePrototypeId))
return false;
if (autoEmote.EmoteTimers.Count > 0)
autoEmote.NextEmoteTime = autoEmote.EmoteTimers.Values.Min();
else if (removeEmpty)
RemCompDeferred(uid, autoEmote);
else
autoEmote.NextEmoteTime = TimeSpan.MaxValue;
return true;
}
/// <summary>
/// Reset the timer for a specific emote, or return false if it doesn't exist.
/// </summary>
public bool ResetTimer(EntityUid uid, string autoEmotePrototypeId, AutoEmoteComponent? autoEmote = null, AutoEmotePrototype? autoEmotePrototype = null)
{
if (!Resolve(uid, ref autoEmote))
return false;
if (!autoEmote.Emotes.Contains(autoEmotePrototypeId))
return false;
autoEmotePrototype ??= _prototypeManager.Index<AutoEmotePrototype>(autoEmotePrototypeId);
var curTime = _gameTiming.CurTime;
var time = curTime + autoEmotePrototype.Interval;
autoEmote.EmoteTimers[autoEmotePrototypeId] = time;
if (autoEmote.NextEmoteTime > time || autoEmote.NextEmoteTime <= curTime)
autoEmote.NextEmoteTime = time;
return true;
}
}