Grid/Component/DoorComponent.cs
using Sandbox;
using Sandbox.Grid.Base;
using System;
using System.Numerics;
using System.Security.AccessControl;

public sealed class DoorComponent : GridActivatableBase, Component.INetworkSpawn
{
    [Property] public float OpenHeight { get; set; } = 120f;
    [Property] public float OpenSpeed { get; set; } = 240f;
    [Property] public float CloseSpeed { get; set; } = 120f;
    [Property] public float HoldOpenTime { get; set; } = 2f;
    [Property] public float ClosedZOffset { get; set; } = 0f;
    [Property] public float ProximityCheckInterval { get; set; } = 0.2f;

    public enum DoorState
    {
        Closed,
        Opening,
        Open,
        Closing
    }

    [Sync] public DoorState State { get; private set; } = DoorState.Closed;

    private Vector3 closedPos;
    private Vector3 openPos;
    private float openTimer;
    private bool initialized;
    private float nextProximityCheck;

    private Collider doorCollider;

    protected override void OnStart()
    {
        base.OnStart();
        doorCollider = Components.Get<Collider>();
    }

    void Component.INetworkSpawn.OnNetworkSpawn(Connection connection)
    {
        if (initialized)
            return;

        initialized = true;

        if (!IsProxy)
            State = DoorState.Closed;

        closedPos = Transform.Position + Vector3.Up * ClosedZOffset;
        openPos = closedPos + Vector3.Up * OpenHeight;

        Transform.Position = State == DoorState.Open ? openPos : closedPos;
        UpdateCollider();
    }

    protected override void OnUpdate()
    {
        if (IsProxy || !initialized)
            return;

        switch (State)
        {
            case DoorState.Opening:
                Transform.Position = MoveTowards(
                    Transform.Position,
                    openPos,
                    OpenSpeed * Time.Delta
                );

                if (Transform.Position.Distance(openPos) < 0.5f)
                {
                    Transform.Position = openPos;
                    State = DoorState.Open;
                    openTimer = 0f;
                    UpdateCollider();
                }
                break;

            case DoorState.Open:
                openTimer += Time.Delta;

                if (openTimer >= HoldOpenTime && Time.Now >= nextProximityCheck)
                {
                    nextProximityCheck = Time.Now + ProximityCheckInterval;

                    if (!IsAnyPlayerNearDoorVolume())
                    {
                        State = DoorState.Closing;
                        UpdateCollider();
                    }
                }
                break;

            case DoorState.Closing:

                // 🔒 LAAJENNETTU TURVATARKISTUS
                if (Time.Now >= nextProximityCheck)
                {
                    nextProximityCheck = Time.Now + ProximityCheckInterval;

                    if (IsAnyPlayerNearDoorVolume())
                    {
                        State = DoorState.Open;
                        openTimer = 0f;
                        UpdateCollider();
                        return;
                    }
                }

                Transform.Position = MoveTowards(
                    Transform.Position,
                    closedPos,
                    CloseSpeed * Time.Delta
                );

                if (Transform.Position.Distance(closedPos) < 0.5f)
                {
                    Transform.Position = closedPos;
                    State = DoorState.Closed;
                    UpdateCollider();
                }
                break;
        }
    }

    public override void Activate()
    {
        if (!IsValid || GameObject is null || Scene is null)
            return;

        if (Networking.IsClient && IsProxy)
            return;

        if (State == DoorState.Closed || State == DoorState.Closing)
        {
            State = DoorState.Opening;
            UpdateCollider();
        }
    }

    public override void Deactivate()
    {
        if (!IsValid || GameObject is null || Scene is null)
            return;

        if (Networking.IsClient && IsProxy)
            return;

        if (State != DoorState.Open)
            return;

        if (IsAnyPlayerNearDoorVolume())
            return;

        State = DoorState.Closing;
        UpdateCollider();
    }

    private void UpdateCollider()
    {
        if (doorCollider == null)
            return;

        if (State == DoorState.Open || State == DoorState.Opening || State == DoorState.Closing)
        {
            doorCollider?.Enabled = true;
            doorCollider?.Tags.Add("passable");
        }
        else
        {
            doorCollider?.Enabled = true;
            doorCollider?.Tags.Remove("passable");
        }
    }

    // 🔑 LAAJENNETTU TARKISTUS
    private bool IsAnyPlayerNearDoorVolume()
    {
        Vector3 doorPos = Transform.Position;

        const float rangeXY = 64f;
        const float rangeZ = 96f;

        foreach (var controller in Scene.GetAllComponents<CharacterController>())
        {
            Vector3 p = controller.Transform.Position;

            if (MathF.Abs(p.x - doorPos.x) < rangeXY &&
                 MathF.Abs(p.y - doorPos.y) < rangeXY &&
                 p.z < doorPos.z + rangeZ)
            {
                return true;
            }
        }

        return false;
    }

    private static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDelta)
    {
        var delta = target - current;
        var dist = delta.Length;

        if (dist <= maxDelta || dist == 0f)
            return target;

        return current + delta / dist * maxDelta;
    }

    public bool CanPassThrough()
    {
        return State == DoorState.Open;
    }
}