VoiceLines/VoiceLinePlayer.cs

Static helper that plays localized voice lines and shows subtitles. It looks up a VoiceLine resource, plays its localized sound either positioned at a caller GameObject or in 2D, estimates subtitle duration from word count, and posts the subtitle to UI.SubtitleBus.

File Access
using System;
using Sandbox;

namespace BrickJam.VoiceLines;

/// <summary>
/// Plays localized voice lines and surfaces their subtitles. Scene-System port of the legacy
/// <c>VoiceLinePlayer</c> entity, reduced to a static helper (the legacy per-line tracking +
/// ConCmds had no in-game callers). Call <see cref="Play"/> from anywhere.
/// </summary>
public static class VoiceLinePlayer
{
	/// <summary>
	/// Play a voice line by resource path. If <paramref name="caller"/> is valid the sound is
	/// positional, otherwise it plays 2D. The localized subtitle is pushed to the HUD.
	/// </summary>
	public static void Play( GameObject caller, string path )
	{
		if ( !ResourceLibrary.TryGet<VoiceLine>( path, out var voiceLine ) || !voiceLine.IsValidVoiceLine )
		{
			Log.Warning( $"Could not find a valid voice line \"{path}\"" );
			return;
		}

		var sound = voiceLine.LocalizedSound;

		if ( caller.IsValid() )
			Sound.Play( sound, caller.WorldPosition );
		else
			Sound.Play( sound );

		// No public API for a SoundEvent's length, so size the subtitle to the text (reading speed):
		// ~2.5 words/sec, clamped to a sane on-screen window.
		var subtitle = voiceLine.LocalizedSubtitle;
		var words = subtitle?.Split( ' ', StringSplitOptions.RemoveEmptyEntries ).Length ?? 0;
		var duration = Math.Clamp( 1.5f + words * 0.4f, 3f, 10f );

		UI.SubtitleBus.Post( caller.IsValid() ? caller.Name : "", subtitle, duration );
	}
}