Code/PisadaGigante.cs

Component attached to a prefab that converts character Step events and landing events into a camera shake and optional landing sound. It computes distance attenuation and amplifies shake for landings.

Native Interop
namespace SBH;

using Sandbox;

/// <summary>
/// [SBHBase] PISADA DE GIGANTE (2026-08-10, para el Siren Head — y todo
/// coloso que venga). Colgado en el prefab, convierte cada evento Step del
/// personaje en un TEMBLOR de pantalla con caída por distancia, y el
/// ATERRIZAJE de un salto/caída en un pisotón más fuerte todavía (con su
/// sonido propio, porque el aterrizaje no es un evento pintado).
///
/// El sonido de las pisadas normales va como siempre: pintando los Step en
/// el editor con el Dato apuntando al .sound del personaje (p. ej.
/// sounds/sirenhead/pisada_gigante.sound).
/// </summary>
public sealed class PisadaGigante : Component
{
	[Property, Description( "Trauma de temblor por pisada (0.1 apenas se siente, 0.5 sacude fuerte) — se atenúa con la distancia" )]
	public float FuerzaTemblor { get; set; } = 0.35f;

	[Property, Description( "Distancia a la que el temblor deja de sentirse (el sonido tiene su propia distancia en el .sound)" )]
	public float RadioTemblor { get; set; } = 2500f;

	[Property, Description( "Multiplicador del temblor al ATERRIZAR de un salto o caída" )]
	public float MultAterrizaje { get; set; } = 2.5f;

	[Property, Description( "Sonido del aterrizaje (opcional): suena al caer aunque no haya evento pintado" )]
	public SoundEvent SonidoAterrizaje { get; set; }

	/// <summary> Lo llama el despachador de eventos en cada Step del personaje. </summary>
	public void AlPaso( Vector3 punto ) => Sacudir( punto, FuerzaTemblor );

	/// <summary> Lo llaman SaltoNextbot y la posesión al tocar el piso tras un vuelo. </summary>
	public void AlAterrizar()
	{
		if ( SonidoAterrizaje != null )
			Sound.Play( SonidoAterrizaje, WorldPosition );

		Sacudir( WorldPosition, FuerzaTemblor * MultAterrizaje );
	}

	void Sacudir( Vector3 punto, float fuerza )
	{
		var cam = Scene?.Camera;
		if ( !cam.IsValid() ) return;

		var distancia = cam.WorldPosition.Distance( punto );
		if ( distancia >= RadioTemblor ) return;

		// Atenuación cuadrática: de cerca sacude entero, de lejos un cosquilleo
		var cerca = 1f - (distancia / RadioTemblor);
		TemblorCamara.Sacudir( Scene, fuerza * cerca * cerca );
	}
}