Grid/Component/TrapComponent.cs
using Sandbox;
using Sandbox.Grid.Base;
using Sandbox.Sboku;
using SWB.Demo;
using SWB.Shared;
using System.Collections.Generic;
using System.Numerics;
using System.Security.AccessControl;

public sealed class TrapComponent : GridActivatableBase
{

    [Property]
    public ScreenShake TrapScreenShake { get; set; } = new ScreenShake
    {
        Duration = 0.35f,
        Size = 1.2f,
        Rotation = 2.5f,
        Delay = 0.02f
    };
    [Property] public string ClosedModelPath { get; set; }
    [Property] public string ActiveModelPath { get; set; }

    [Property] public float Damage { get; set; } = 25f;
    [Property] public float ActiveTime { get; set; } = 1.5f;
    [Property] public float DamageInterval { get; set; } = 0.5f;

    private ModelRenderer model;
    private BoxCollider trigger;

    private bool active;
    private TimeSince timeSinceActivated;

    private GridMap GridMap;

    private Dictionary<GameObject, TimeSince> damageCooldown = new();

    public bool IsActive { get { return active; } }
    protected override void OnStart()
    {
        base.OnStart();

        GridMap = Scene.GetComponentInChildren<GridSpawner>()?.Map;

        model = Components.Get<ModelRenderer>();
        trigger = Components.GetOrCreate<BoxCollider>();
        trigger.Tags.Add("passable");
        trigger.IsTrigger = true;
        trigger.Enabled = false;
        trigger.OnTriggerEnter += OnTrigger;



        Deactivate();
    }

    protected override void OnUpdate()
    {
        if (!active)
            return;

        if (timeSinceActivated >= ActiveTime)
        {
            Deactivate();
        }
    }



    public override void Activate()
    {
        if (active)
            return;

        active = true;
        timeSinceActivated = 0;
        damageCooldown.Clear();

        if (model != null && !string.IsNullOrEmpty(ActiveModelPath))
            model.Model = Model.Load(ActiveModelPath);

        trigger.Enabled = true;

        //Log.Info($"Trap activated at {GridX},{GridY}");
    }

    public override void Deactivate()
    {
        active = false;

        if (model != null && !string.IsNullOrEmpty(ClosedModelPath))
            model.Model = Model.Load(ClosedModelPath);

        trigger.Enabled = false;

        //Log.($"Trap deactivated at {GridX},{GridY}");
    }

    private void OnTrigger(Collider other)
    {
        if (!active)
            return;

        var go = other.GameObject;

        var player = go.GetComponentInParent<DemoPlayer>();
        if (player != null && player.Health > 0 && !OnCooldown(go))
        {
            player.TakeDamage(CreateDamageInfo());

            player.ShakeScreen(TrapScreenShake);
            return;
        }

        // 🔹 Botti
        var bot = go.GetComponentInParent<BotAdapter>();
        if (bot != null && bot.IsAlive && !OnCooldown(go))
        {
            bot.TakeDamage(CreateDamageInfo());
            return;
        }

    }

    private bool OnCooldown(GameObject go)
    {
        if (damageCooldown.TryGetValue(go, out var since) && since < DamageInterval)
            return true;

        damageCooldown[go] = 0;
        return false;
    }

    private SWB.Shared.DamageInfo CreateDamageInfo()
    {
        return new SWB.Shared.DamageInfo
        {
            Damage = Damage,
            AttackerId = GameObject.Id,
            Origin = GameObject.WorldPosition,
            Force = Vector3.Up * 200f,
            Tags = new[] { "trap" },
            Inflictor = "trap"
        };
    }
}