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;
}
}