Skip to content

Commit

Permalink
Let ghosts sometimes make certain devices say creepy things (#34368)
Browse files Browse the repository at this point in the history
* Add SpookySpeaker component/system

* Shuffle Boo action targets before trying to activate them

* Add SpookySpeaker to vending machines

* Fix chatcode eating messages starting with "..."

* Add SpookySpeaker to recycler

* Oops

* Decrease speak probability for vending machines

* Add spooky speaker to arcade machines
  • Loading branch information
Tayrtahn authored Jan 11, 2025
1 parent 5deab7d commit c6e4d19
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 1 deletion.
37 changes: 37 additions & 0 deletions Content.Server/Ghost/Components/SpookySpeakerComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using Content.Shared.Dataset;
using Robust.Shared.Prototypes;

namespace Content.Server.Ghost.Components;

/// <summary>
/// Causes this entity to react to ghost player using the "Boo!" action by speaking
/// a randomly chosen message from a specified set.
/// </summary>
[RegisterComponent, AutoGenerateComponentPause]
public sealed partial class SpookySpeakerComponent : Component
{
/// <summary>
/// ProtoId of the LocalizedDataset to use for messages.
/// </summary>
[DataField(required: true)]
public ProtoId<LocalizedDatasetPrototype> MessageSet;

/// <summary>
/// Probability that this entity will speak if activated by a Boo action.
/// This is so whole banks of entities don't trigger at the same time.
/// </summary>
[DataField]
public float SpeakChance = 0.5f;

/// <summary>
/// Minimum time that must pass after speaking before this entity can speak again.
/// </summary>
[DataField]
public TimeSpan Cooldown = TimeSpan.FromSeconds(30);

/// <summary>
/// Time when the cooldown will have elapsed and the entity can speak again.
/// </summary>
[DataField, AutoPausedField]
public TimeSpan NextSpeakTime;
}
6 changes: 5 additions & 1 deletion Content.Server/Ghost/GhostSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.Ghost
Expand Down Expand Up @@ -63,6 +64,7 @@ public sealed class GhostSystem : SharedGhostSystem
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly IRobustRandom _random = default!;

private EntityQuery<GhostComponent> _ghostQuery;
private EntityQuery<PhysicsComponent> _physicsQuery;
Expand Down Expand Up @@ -127,7 +129,9 @@ private void OnActionPerform(EntityUid uid, GhostComponent component, BooActionE
if (args.Handled)
return;

var entities = _lookup.GetEntitiesInRange(args.Performer, component.BooRadius);
var entities = _lookup.GetEntitiesInRange(args.Performer, component.BooRadius).ToList();
// Shuffle the possible targets so we don't favor any particular entities
_random.Shuffle(entities);

var booCounter = 0;
foreach (var ent in entities)
Expand Down
51 changes: 51 additions & 0 deletions Content.Server/Ghost/SpookySpeakerSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Content.Server.Chat.Systems;
using Content.Server.Ghost.Components;
using Content.Shared.Random.Helpers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;

namespace Content.Server.Ghost;

public sealed class SpookySpeakerSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ChatSystem _chat = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<SpookySpeakerComponent, GhostBooEvent>(OnGhostBoo);
}

private void OnGhostBoo(Entity<SpookySpeakerComponent> entity, ref GhostBooEvent args)
{
// Only activate sometimes, so groups don't all trigger together
if (!_random.Prob(entity.Comp.SpeakChance))
return;

var curTime = _timing.CurTime;
// Enforce a delay between messages to prevent spam
if (curTime < entity.Comp.NextSpeakTime)
return;

if (!_proto.TryIndex(entity.Comp.MessageSet, out var messages))
return;

// Grab a random localized message from the set
var message = _random.Pick(messages);
// Chatcode moment: messages starting with '.' are considered radio messages unless prefixed with '>'
// So this is a stupid trick to make the "...Oooo"-style messages work.
message = '>' + message;
// Say the message
_chat.TrySendInGameICMessage(entity, message, InGameICChatType.Speak, hideChat: true);

// Set the delay for the next message
entity.Comp.NextSpeakTime = curTime + entity.Comp.Cooldown;

args.Handled = true;
}
}
18 changes: 18 additions & 0 deletions Resources/Locale/en-US/ghost/spooky-speaker.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
spooky-speaker-generic-1 = ...ooOoooOOoooo...
spooky-speaker-generic-2 = ...can anyone hear me...?
spooky-speaker-generic-3 = ...join us...
spooky-speaker-generic-4 = ...come play with us...
spooky-speaker-generic-5 = KkkhhkhKhhkhkKk
spooky-speaker-generic-6 = Khhggkkghkk
spooky-speaker-generic-7 = khhkkkkKkhkkHk
spooky-speaker-generic-8 = ...
spooky-speaker-generic-9 = ...h-h-hello...?
spooky-speaker-generic-10 = Bzzzt
spooky-speaker-generic-11 = Weh
spooky-speaker-generic-12 = TREMBLE, MORTALS!
spooky-speaker-generic-13 = 4444444444
spooky-speaker-generic-14 = ...I found you...
spooky-speaker-recycler-1 = I HUNGER
spooky-speaker-recycler-2 = MORE! GIVE ME MORE!
spooky-speaker-recycler-3 = FEED ME
11 changes: 11 additions & 0 deletions Resources/Prototypes/Datasets/spooky_speakers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
- type: localizedDataset
id: SpookySpeakerMessagesGeneric
values:
prefix: spooky-speaker-generic-
count: 14

- type: localizedDataset
id: SpookySpeakerMessagesRecycler
values:
prefix: spooky-speaker-recycler-
count: 3
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
- type: Speech
speechVerb: Robotic
speechSounds: Vending
- type: SpookySpeaker
messageSet: SpookySpeakerMessagesGeneric
speakChance: 0.2
- type: Anchorable
- type: Pullable
- type: StaticPrice
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,10 @@
interactFailureString: petting-failure-generic
interactSuccessSound:
path: /Audio/Items/drill_hit.ogg
- type: SpookySpeaker
speakChance: 0.1
cooldown: 120
messageSet: SpookySpeakerMessagesRecycler
- type: Speech
speechVerb: Robotic
speechSounds: SyndieBorg
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@
- type: Speech
speechVerb: Robotic
speechSounds: Vending
- type: SpookySpeaker
messageSet: SpookySpeakerMessagesGeneric
speakChance: 0.2
- type: IntrinsicRadioReceiver
- type: ActiveRadio
channels:
Expand Down

0 comments on commit c6e4d19

Please sign in to comment.