Grid/Destruction/DebrisVisualPoolcs.cs
using Sandbox;
using System;
using System.Collections.Generic;
using static Sandbox.Services.Inventory;

namespace Sandbox.Sboku;

public sealed class DebrisVisualPool : Component
{
    private sealed class ColumnItem
    {
        public GameObject Go;
        public ModelRenderer Renderer;

        public Vector3 StartPos;
        public Vector3 EndPos;
        public float Time;
    }

    private readonly Queue<ColumnItem> columnPool = new();
    private readonly List<ColumnItem> activeColumns = new();

    [Property] public int MaxColumns { get; set; } = 8;

    [Property] public Model DebrisModel { get; set; }
    [Property] public Material BaseMaterial { get; set; }

    [Property] public int MaxInstances { get; set; } = 256;
    [Property] public float Lifetime { get; set; } = 1.5f;

    // 🔑 Yksi debris-instanssi
    private sealed class DebrisItem
    {
        public ModelRenderer Renderer;
        public Vector3 Velocity;
        public float SpawnTime;
    }

    private readonly Queue<DebrisItem> pool = new();
    private readonly List<DebrisItem> active = new();

    protected override void OnStart()
    {
        //Debris
        for (int i = 0; i < MaxInstances; i++)
        {
            var go = new GameObject(true, "debris_instance");
            go.SetParent(GameObject, false);

            var mr = go.Components.Create<ModelRenderer>();
            mr.Model = DebrisModel;
            mr.MaterialOverride = BaseMaterial;
            mr.Enabled = false;

            pool.Enqueue(new DebrisItem
            {
                Renderer = mr
            });
        }

        //Pylväät
        for (int i = 0; i < MaxColumns; i++)
        {
            var go = new GameObject(true, "falling_column");
            go.SetParent(GameObject, false);

            var mr = go.Components.Create<ModelRenderer>();
            mr.Model = Model.Load("models/dev/box.vmdl");
            mr.Enabled = false;
           

            columnPool.Enqueue(new ColumnItem
            {
                Go = go,
                Renderer = mr
            });
        }
    }


  
    private const float fallDuration = 0.6f;
    int verticalSlices = 3;
    int horizontalSlices = 1;

    public void SpawnColumnDebris(
        Vector3 columnBaseWorld,
        int heightInCells,
        float cellSize,
        int hitCellZ,
        Material wallMaterial
    )
    {
        if (pool.Count == 0)
            return;

        const float modelUnitScale = 0.02f;

        float widthWorld = cellSize;

        int fallingCells = heightInCells - hitCellZ - 1;
        if (fallingCells <= 0)
            return;

        float totalHeightWorld = fallingCells * cellSize;
        float sliceHeight = totalHeightWorld / verticalSlices;
        float sliceWidth = widthWorld / horizontalSlices;

        float startZ = columnBaseWorld.z-cellSize/2; //+ (hitCellZ) * cellSize;

        for (int z = 0; z < verticalSlices; z++)
        {
            for (int xy = 0; xy < horizontalSlices; xy++)
            {
                if (pool.Count == 0)
                    return;

                var item = pool.Dequeue();
                var go = item.Renderer.GameObject;

                item.Renderer.MaterialOverride = wallMaterial ?? BaseMaterial;
                item.Renderer.Enabled = true;

                float h = sliceHeight * Game.Random.Float(0.85f, 1.15f);
                float w = sliceWidth * Game.Random.Float(0.75f, 1.1f);

                go.Transform.Scale = new Vector3(
                    w * modelUnitScale,
                    w * modelUnitScale,
                    h * modelUnitScale
                );

                Vector3 sideOffset =
                    Vector3.Random.WithZ(0).Normal *
                    Game.Random.Float(0f, sliceWidth * 0.35f);

                float zPos =
                    startZ +
                    (z + 0.5f) / verticalSlices * totalHeightWorld
                    - h * 0.5f;

                go.Transform.Position =
                    new Vector3(columnBaseWorld.x, columnBaseWorld.y, zPos)
                    + sideOffset;

                go.Transform.Rotation = Rotation.Identity;

                item.Velocity =
                    Vector3.Down * Game.Random.Float(140f, 240f) +
                    sideOffset.Normal * Game.Random.Float(40f, 90f);

                item.SpawnTime = Time.Now;
                active.Add(item);
            }
        }
    }





    protected override void OnUpdate()
    {


        // 🔽 Falling columns
        for (int i = activeColumns.Count - 1; i >= 0; i--)
        {
            var item = activeColumns[i];

            item.Time += Time.Delta;
            float t = item.Time / fallDuration;

            if (t >= 1f)
            {
                item.Renderer.Enabled = false;
                activeColumns.RemoveAt(i);
                columnPool.Enqueue(item);
                continue;
            }
            
            float eased = t * t; // raskas tunne
            item.Go.WorldPosition =
                Vector3.Lerp(item.StartPos, item.EndPos, eased);
        }












        for (int i = active.Count - 1; i >= 0; i--)
        {
            var item = active[i];
            var go = item.Renderer.GameObject;

            float age = Time.Now - item.SpawnTime;

            if (age > Lifetime)
            {
                item.Renderer.Enabled = false;
                active.RemoveAt(i);
                pool.Enqueue(item);
                continue;
            }

            // 🔽 Raskaampi painovoima
            item.Velocity += Vector3.Down * 1800f * Time.Delta;

            // 🧲 Ilmanvastus (tämä tekee "painon")
            item.Velocity *= MathF.Pow(0.15f, Time.Delta);

            go.Transform.Position += item.Velocity * Time.Delta;

            // 🔄 Pyöriminen riippuu nopeudesta
            float spin = item.Velocity.Length * 0.02f;
            go.Transform.Rotation *= Rotation.FromYaw(spin * Time.Delta);
        }
    }

}