StructuralStress.cs

Component that tracks structural health of a GameObject and applies damage from sustained overload or impulses, plays a break sound, spawns debris prefab, and destroys the object when health reaches zero. It listens to trigger enter/exit to sum masses on top and applies periodic stress damage in fixed updates.

File Access
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

public sealed class StructuralStress : Component, Component.ITriggerListener
{
	[Property] public float MaxStructuralHealth { get; set; } = 100f;
	[Property] public float CurrentHealth { get; private set; } = 100f;
	[Property] public float MassCapacity { get; set; } = 200f; // Максимальная масса сверху до начала разрушения
	[Property] public bool IsFoundation { get; set; } = false; // Является ли несущей опорой

	[Property] public SoundEvent BreakSound { get; set; }
	[Property] public GameObject DebrisPrefab { get; set; }

	private Rigidbody _rb;
	private float _currentOverloadWeight = 0f;

	protected override void OnStart()
	{
		CurrentHealth = MaxStructuralHealth;
		_rb = Components.Get<Rigidbody>();
	}

	protected override void OnFixedUpdate()
	{
		// Проверяем перегрузку весом сверху
		if ( _currentOverloadWeight > MassCapacity )
		{
			float excess = _currentOverloadWeight - MassCapacity;
			ApplyStressDamage( excess * Time.Delta * 0.5f );
		}
	}

	public void ApplyStressDamage( float damage )
	{
		CurrentHealth -= damage;

		// Визуальная реакция — легкое встряхивание или треск при низком HP
		if ( CurrentHealth <= 0f )
		{
			Collapse();
		}
	}

	public void ApplyImpulseDamage( Vector3 force )
	{
		float impactStrength = force.Length / 100f;
		if ( impactStrength > 5f )
		{
			ApplyStressDamage( impactStrength );
		}
	}

	private void Collapse()
	{
		// Воспроизводим звук разрушения
		if ( BreakSound != null )
		{
			Sound.Play( BreakSound, Transform.Position );
		}

		// Спавним осколки / Debris
		if ( DebrisPrefab != null )
		{
			var debris = DebrisPrefab.Clone( Transform.World );
			debris.Transform.Scale = Transform.Scale;
		}

		Log.Info( $"[StructuralStress] Объект {GameObject.Name} разрушился под напряжением!" );

		// Уничтожаем основной объект
		GameObject.Destroy();
	}

	void ITriggerListener.OnTriggerEnter( Collider other )
	{
		if ( other.Components.Get<Rigidbody>() is Rigidbody otherRb )
		{
			_currentOverloadWeight += otherRb.PhysicsBody.Mass;
		}
	}

	void ITriggerListener.OnTriggerExit( Collider other )
	{
		if ( other.Components.Get<Rigidbody>() is Rigidbody otherRb )
		{
			_currentOverloadWeight = MathF.Max( 0f, _currentOverloadWeight - otherRb.PhysicsBody.Mass );
		}
	}
}