Grid/Base/PrisonBreakableBlock.cs
using Sandbox;
using SWB.Base;
using Sandbox.Sboku;
using System.Numerics;

public sealed class PrisonBreakableBlock : Component
{
    [Property] public int MaxHealth { get; set; } = 20;
    [Property] public float DestroyDelay { get; set; } = 0f;
    [Property] public DebrisVisualPool DebrisPool { get; set; }
    [Property] public GameObject DustPrefab { get; set; }
    [Property] public Material BlockMaterial { get; set; }
    [Property] public float ShatterWorldSize { get; set; } = 64f;

    private int health;
    private bool destroyed;

    protected override void OnStart()
    {
        health = MaxHealth;
    }

    [Rpc.Host]
    public void RpcHit(int damage, Vector3 hitWorldPos)
    {
        if (destroyed)
            return;

        if (damage <= 0)
            damage = 1;

        health -= damage;

        if (health > 0)
            return;

        destroyed = true;
        DestroyBlock(hitWorldPos);
        RpcDestroyBlock(hitWorldPos);
    }

    [Rpc.Broadcast]
    private void RpcDestroyBlock(Vector3 hitWorldPos)
    {
        if (destroyed && Networking.IsHost)
            return;

        destroyed = true;
        DestroyBlock(hitWorldPos);
    }

    private void DestroyBlock(Vector3 hitWorldPos)
    {
        if (DustPrefab.IsValid())
        {
            var dust = DustPrefab.Clone(hitWorldPos + Vector3.Up * 16f);
            dust.DestroyAsync(1.5f);
        }

        if (DebrisPool.IsValid())
        {
            DebrisPool.SpawnColumnDebris(
                GameObject.Transform.Position,
                2,
                ShatterWorldSize,
                0,
                BlockMaterial
            );
        }

        if (DestroyDelay > 0f)
        {
            GameObject.DestroyAsync(DestroyDelay);
            return;
        }

        GameObject.Destroy();
    }
}