Skip to content

Commit

Permalink
Speech verbs & conditional markup modification (#18980)
Browse files Browse the repository at this point in the history
  • Loading branch information
mirrorcult authored Aug 15, 2023
1 parent 5742c4e commit 7db8c78
Show file tree
Hide file tree
Showing 27 changed files with 252 additions and 7 deletions.
9 changes: 7 additions & 2 deletions Content.Server/Chat/Systems/ChatSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,13 @@ private void SendEntitySpeak(
}

name = FormattedMessage.EscapeText(name);
var wrappedMessage = Loc.GetString("chat-manager-entity-say-wrap-message",
("entityName", name), ("message", FormattedMessage.EscapeText(message)));
var speech = GetSpeechVerb(source, message);
var wrappedMessage = Loc.GetString(speech.Bold ? "chat-manager-entity-say-bold-wrap-message" : "chat-manager-entity-say-wrap-message",
("entityName", name),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("message", FormattedMessage.EscapeText(message)));

SendInVoiceRange(ChatChannel.Local, message, wrappedMessage, source, range);

Expand Down
16 changes: 15 additions & 1 deletion Content.Server/Radio/EntitySystems/RadioSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Robust.Shared.Map;
using Content.Shared.Radio.Components;
using Content.Server.Power.Components;
using Robust.Shared.Random;

namespace Content.Server.Radio.EntitySystems;

Expand All @@ -25,7 +26,9 @@ public sealed class RadioSystem : EntitySystem
[Dependency] private readonly INetManager _netMan = default!;
[Dependency] private readonly IReplayRecordingManager _replay = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly ChatSystem _chat = default!;

// set used to prevent radio feedback loops.
private readonly HashSet<string> _messages = new();
Expand Down Expand Up @@ -69,11 +72,22 @@ public void SendRadioMessage(EntityUid messageSource, string message, RadioChann

name = FormattedMessage.EscapeText(name);

var speech = _chat.GetSpeechVerb(messageSource, message);

var wrappedMessage = Loc.GetString(speech.Bold ? "chat-radio-message-wrap-bold" : "chat-radio-message-wrap",
("color", channel.Color),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("channel", $"\\[{channel.LocalizedName}\\]"),
("name", name),
("message", FormattedMessage.EscapeText(message)));

// most radios are relayed to chat, so lets parse the chat message beforehand
var chat = new ChatMessage(
ChatChannel.Radio,
message,
Loc.GetString("chat-radio-message-wrap", ("color", channel.Color), ("channel", $"\\[{channel.LocalizedName}\\]"), ("name", name), ("message", FormattedMessage.EscapeText(message))),
wrappedMessage,
EntityUid.Invalid);
var chatMsg = new MsgChatMessage { Message = chat };
var ev = new RadioReceiveEvent(message, messageSource, channel, chatMsg);
Expand Down
28 changes: 28 additions & 0 deletions Content.Shared/Chat/SharedChatSystem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Content.Shared.Popups;
using Content.Shared.Radio;
using Content.Shared.Speech;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

Expand All @@ -26,6 +27,9 @@ public abstract class SharedChatSystem : EntitySystem

public static string DefaultChannelPrefix = $"{RadioChannelPrefix}{DefaultChannelKey}";

[ValidatePrototypeId<SpeechVerbPrototype>]
public const string DefaultSpeechVerb = "Default";

[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;

Expand Down Expand Up @@ -63,6 +67,30 @@ public override void Shutdown()
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
}

/// <summary>
/// Attempts to find an applicable <see cref="SpeechVerbPrototype"/> for a speaking entity's message.
/// If one is not found, returns <see cref="DefaultSpeechVerb"/>.
/// </summary>
public SpeechVerbPrototype GetSpeechVerb(EntityUid source, string message, SpeechComponent? speech = null)
{
if (!Resolve(source, ref speech, false))
return _prototypeManager.Index<SpeechVerbPrototype>(DefaultSpeechVerb);

// check for a suffix-applicable speech verb
SpeechVerbPrototype? current = null;
foreach (var (str, id) in speech.SuffixSpeechVerbs)
{
var proto = _prototypeManager.Index<SpeechVerbPrototype>(id);
if (message.EndsWith(Loc.GetString(str)) && proto.Priority >= (current?.Priority ?? 0))
{
current = proto;
}
}

// if no applicable suffix verb return the normal one used by the entity
return current ?? _prototypeManager.Index<SpeechVerbPrototype>(speech.SpeechVerb);
}

/// <summary>
/// Attempts to resolve radio prefixes in chat messages (e.g., remove a leading ":e" and resolve the requested
/// channel. Returns true if a radio message was attempted, even if the channel is invalid.
Expand Down
23 changes: 22 additions & 1 deletion Content.Shared/Speech/SpeechComponent.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Collections.Specialized;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;

namespace Content.Shared.Speech
{
/// <summary>
/// Component required for entities to be able to speak. (TODO: Entities can speak fine without this, this only forbids them speak if they have it and enabled is false.)
/// Contains the option to let entities make noise when speaking, datafields for the sounds in question, and relevant AudioParams.
/// Contains the option to let entities make noise when speaking, change speech verbs, datafields for the sounds in question, and relevant AudioParams.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class SpeechComponent : Component
Expand All @@ -20,6 +22,25 @@ public sealed class SpeechComponent : Component
[DataField("speechSounds", customTypeSerializer:typeof(PrototypeIdSerializer<SpeechSoundsPrototype>))]
public string? SpeechSounds;

/// <summary>
/// What speech verb prototype should be used by default for displaying this entity's messages?
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("speechVerb", customTypeSerializer:typeof(PrototypeIdSerializer<SpeechVerbPrototype>))]
public string SpeechVerb = "Default";

/// <summary>
/// A mapping from chat suffixes loc strings to speech verb prototypes that should be conditionally used.
/// For things like '?' changing to 'asks' or '!!' making text bold and changing to 'yells'. Can be overridden if necessary.
/// </summary>
[DataField("suffixSpeechVerbs", customTypeSerializer:typeof(PrototypeIdValueDictionarySerializer<string, SpeechVerbPrototype>))]
public Dictionary<string, string> SuffixSpeechVerbs = new()
{
{ "chat-speech-verb-suffix-exclamation-strong", "DefaultExclamationStrong" },
{ "chat-speech-verb-suffix-exclamation", "DefaultExclamation" },
{ "chat-speech-verb-suffix-question", "DefaultQuestion" },
};

[DataField("audioParams")]
public AudioParams AudioParams = AudioParams.Default.WithVolume(6f).WithRolloffFactor(4.5f);

Expand Down
47 changes: 47 additions & 0 deletions Content.Shared/Speech/SpeechVerbPrototype.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Shared.Speech;

/// <summary>
/// Handles replacing speech verbs and other conditional chat modifications like bolding or font type depending
/// on punctuation or by directly overriding the prototype.
/// </summary>
[Prototype("speechVerb")]
public sealed class SpeechVerbPrototype : IPrototype
{
[IdDataField] public string ID { get; } = default!;

/// <summary>
/// Loc strings to be passed to the chat wrapper. 'says', 'states', etc.
/// Picks one at random if there are multiple.
/// </summary>
[DataField("speechVerbStrings", required: true)]
public List<string> SpeechVerbStrings = default!;

/// <summary>
/// Should use of this speech verb bold the corresponding message?
/// </summary>
[DataField("bold")]
public bool Bold = false;

/// <summary>
/// What font size should be used for the message contents?
/// </summary>
[DataField("fontSize")]
public int FontSize = 12;

/// <summary>
/// What font prototype ID should be used for the message contents?
/// </summary>
/// font proto is client only so cant lint this lol sorry
[DataField("fontId")]
public string FontId = "Default";

/// <summary>
/// If multiple applicable speech verb protos are found (i.e. through speech suffixes) this will determine
/// which one is picked. Higher = more priority.
/// </summary>
[DataField("priority")]
public int Priority = 0;
}
34 changes: 33 additions & 1 deletion Resources/Locale/en-US/chat/managers/chat-manager.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ chat-manager-whisper-headset-on-message = You can't whisper on the radio!
chat-manager-server-wrap-message = [bold]{$message}[/bold]
chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12]
{$message}[/bold][/font]
chat-manager-entity-say-wrap-message = [bold]{$entityName}[/bold] says, "{$message}"
chat-manager-entity-say-wrap-message = [bold]{$entityName}[/bold] {$verb}, [font={$fontType} size={$fontSize}]"{$message}"[/font]
chat-manager-entity-say-bold-wrap-message = [bold]{$entityName}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]"{$message}"[/bold][/font]
chat-manager-entity-whisper-wrap-message = [font size=11][italic]{$entityName} whispers, "{$message}"[/italic][/font]
chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic]Someone whispers, "{$message}"[/italic][/font]
Expand All @@ -45,3 +46,34 @@ chat-manager-send-hook-ooc-wrap-message = OOC: [bold](D){$senderName}:[/bold] {$
chat-manager-dead-channel-name = DEAD
chat-manager-admin-channel-name = ADMIN
## Speech verbs for chat

chat-speech-verb-suffix-exclamation = !
chat-speech-verb-suffix-exclamation-strong = !!
chat-speech-verb-suffix-question = ?
chat-speech-verb-default = says
chat-speech-verb-exclamation = shouts
chat-speech-verb-exclamation-strong = yells
chat-speech-verb-question = asks
chat-speech-verb-insect-1 = chitters
chat-speech-verb-insect-2 = flutters
chat-speech-verb-slime = chirps
chat-speech-verb-robotic = states
chat-speech-verb-reptilian = hisses
chat-speech-verb-skeleton = rattles
chat-speech-verb-small-mob = squeaks
chat-speech-verb-large-mob = roars
chat-speech-verb-monkey = chimpers
chat-speech-verb-cluwne-1 = giggles
chat-speech-verb-cluwne-2 = guffaws
chat-speech-verb-cluwne-3 = laughs
3 changes: 2 additions & 1 deletion Resources/Locale/en-US/headset/headset-component.ftl
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Chat window radio wrap (prefix and postfix)
chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] says, "{$message}"[/color]
chat-radio-message-wrap = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}]"{$message}"[/font][/color]
chat-radio-message-wrap-bold = [color={$color}]{$channel} [bold]{$name}[/bold] {$verb}, [font={$fontType} size={$fontSize}][bold]"{$message}"[/bold][/font][/color]
examine-headset-default-channel = Use {$prefix} for the default channel ([color={$color}]{$channel}[/color]).
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Parts/skeleton.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- type: Input
context: "human"
- type: Speech
speechVerb: Skeleton
- type: SkeletonAccent
- type: Actions
- type: Vocal
Expand Down
5 changes: 5 additions & 0 deletions Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- type: Physics
- type: Speech
speechSounds: Squeak
speechVerb: SmallMob
- type: Fixtures
fixtures:
fix1:
Expand Down Expand Up @@ -844,6 +845,7 @@
False: {visible: false}
- type: Speech
speechSounds: Monkey
speechVerb: Monkey
- type: Body
prototype: Primate
requiredLegs: 1 # TODO: More than 1 leg
Expand Down Expand Up @@ -903,6 +905,7 @@
- type: GhostTakeoverAvailable
- type: Speech
speechSounds: Squeak
speechVerb: SmallMob
- type: Sprite
drawdepth: SmallMobs
sprite: Mobs/Animals/mouse.rsi
Expand Down Expand Up @@ -1462,6 +1465,7 @@
False: {visible: false}
- type: Speech
speechSounds: Monkey
speechVerb: Monkey
- type: Body
prototype: Primate
requiredLegs: 1 # TODO: More than 1 leg
Expand Down Expand Up @@ -2240,6 +2244,7 @@
description: ghost-role-information-hamster-description
- type: GhostTakeoverAvailable
- type: Speech
speechVerb: SmallMob
speechSounds: Squeak
- type: Sprite
drawdepth: SmallMobs
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/NPCs/behonker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
factions:
- SimpleHostile
- type: Speech
speechVerb: Cluwne
- type: CombatMode
- type: MobMover
- type: InputMover
Expand Down
2 changes: 2 additions & 0 deletions Resources/Prototypes/Entities/Mobs/NPCs/human.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,5 @@
description: A polymorphed unfortunate.
components:
- type: Cluwne
- type: Speech
speechVerb: Cluwne
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@
sprite: Mobs/Silicon/Bots/medibot.rsi
state: medibot
- type: Speech
speechVerb: Robotic
- type: HTN
rootTask:
task: MedibotCompound
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Player/dragon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
factions:
- Dragon
- type: Speech
speechVerb: LargeMob
- type: CombatMode
- type: MobMover
- type: InputMover
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Player/guardian.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
- type: Internals
- type: Examiner
- type: Speech
speechVerb: Robotic
- type: TypingIndicator
proto: guardian
- type: Pullable
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Species/arachnid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
sprite: Mobs/Effects/brute_damage.rsi
color: "#162581"
- type: Speech
speechVerb: Insect
speechSounds: Arachnid
- type: Vocal
sounds:
Expand Down
6 changes: 5 additions & 1 deletion Resources/Prototypes/Entities/Mobs/Species/moth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
- type: Damageable
damageContainer: Biological
damageModifierSet: Moth
- type: Speech
speechVerb: Insect
- type: TypingIndicator
proto: moth
- type: Butcherable
butcheringType: Spike
spawned:
Expand Down Expand Up @@ -117,4 +121,4 @@
description: A dummy moth meant to be used in character setup.
components:
- type: HumanoidAppearance
species: Moth
species: Moth
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Species/reptilian.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
- type: LizardAccent
- type: Speech
speechSounds: Lizard
speechVerb: Reptilian
- type: Vocal
sounds:
Male: UnisexReptilian
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Species/skeleton.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
60: 0.9
80: 0.7
- type: Speech
speechVerb: Skeleton
- type: Vocal
sounds:
Male: Skeleton
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Mobs/Species/slime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- type: HumanoidAppearance
species: SlimePerson
- type: Speech
speechVerb: Slime
speechSounds: Slime
- type: Vocal
sounds:
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Entities/Objects/Devices/radio.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
channels:
- Handheld
- type: Speech
speechVerb: Robotic
- type: Sprite
sprite: Objects/Devices/communication.rsi
layers:
Expand Down
Loading

0 comments on commit 7db8c78

Please sign in to comment.