From d3f4d690d6334c0bb61350dafa49ab9c89f487b2 Mon Sep 17 00:00:00 2001 From: arthu241243 Date: Tue, 30 Jun 2026 21:46:31 +0300 Subject: [PATCH 1/4] Elevator fix --- .../ComplexElevatorSystem.Debug.cs | 78 ++++ .../ComplexElevator/ComplexElevatorSystem.cs | 409 ++++++++++++------ .../_Scp/ComplexElevator/ElevatorCommands.cs | 6 +- 3 files changed, 370 insertions(+), 123 deletions(-) create mode 100644 Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.Debug.cs diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.Debug.cs b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.Debug.cs new file mode 100644 index 00000000000..44b1c31715c --- /dev/null +++ b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.Debug.cs @@ -0,0 +1,78 @@ +using Content.Server.Administration.Managers; +using Content.Shared._Scp.ComplexElevator; +using Content.Shared.Database; +using Content.Shared.Verbs; +using Robust.Server.GameObjects; +using Robust.Shared.Player; + +namespace Content.Server._Scp.ComplexElevator; + +public sealed partial class ComplexElevatorSystem +{ + [Dependency] private readonly IAdminManager _adminManager = default!; + + private void InitializeDebug() + { + SubscribeLocalEvent>(AddAdminElevatorVerbs); + SubscribeLocalEvent>(AddAdminPointVerbs); + } + + private void AddAdminElevatorVerbs(EntityUid uid, ComplexElevatorComponent comp, GetVerbsEvent args) + { + if (!TryComp(args.User, out ActorComponent? actor) || !_adminManager.IsAdmin(actor.PlayerSession)) + return; + + foreach (var floor in comp.Floors) + { + var targetFloor = floor; + args.Verbs.Add(new Verb + { + Text = $"Send to: {targetFloor}", + Category = VerbCategory.Debug, + Act = () => MoveToFloor((uid, comp), targetFloor), + Impact = LogImpact.Medium + }); + } + } + + private void AddAdminPointVerbs(EntityUid uid, ElevatorPointComponent comp, GetVerbsEvent args) + { + if (!TryComp(args.User, out ActorComponent? actor) || !_adminManager.IsAdmin(actor.PlayerSession)) + return; + + foreach (var (elevatorUid, elevatorComp) in _elevatorIndex) + { + if (!TryComp(elevatorComp, out var elevator)) + continue; + + if (!elevator.Floors.Contains(comp.FloorId)) + continue; + + var elUid = elevatorComp; + var floorId = comp.FloorId; + + args.Verbs.Add(new Verb + { + Text = $"[Elevator: {elevator.ElevatorId}] Send here", + Category = VerbCategory.Debug, + Act = () => MoveToFloor((elUid, elevator), floorId), + Impact = LogImpact.Medium + }); + + args.Verbs.Add(new Verb + { + Text = $"[Elevator: {elevator.ElevatorId}] Teleport here instantly", + Category = VerbCategory.Debug, + Act = () => + { + ClearGasInTargetArea(elUid, floorId); + KillEntitiesInTargetArea((elUid, elevator), floorId); + elevator.CurrentFloor = floorId; + Dirty(elUid, elevator); + TeleportToFloor(elUid, floorId, elevator); + }, + Impact = LogImpact.High + }); + } + } +} diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs index 5d42b2e7f4c..62c217086bd 100644 --- a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs +++ b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs @@ -1,47 +1,189 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; -using Content.Shared.Timing; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Doors.Systems; +using Content.Shared._Scp.ComplexElevator; +using Robust.Shared.GameObjects; +using Content.Shared.Buckle.Components; using Content.Shared.Damage; using Content.Shared.Damage.Systems; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; -using Robust.Shared.Physics.Components; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Timing; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Map; -using Robust.Shared.Maths; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics.Components; using Robust.Shared.Timing; -using Content.Server.Doors.Systems; namespace Content.Server._Scp.ComplexElevator; -public sealed class ComplexElevatorSystem : EntitySystem +public sealed partial class ComplexElevatorSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly UseDelaySystem _useDelay = default!; [Dependency] private readonly AudioSystem _audio = default!; [Dependency] private readonly PointLightSystem _pointLight = default!; - + [Dependency] private readonly SharedMapSystem _mapSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; private static readonly Color ElevatorButtonGreen = Color.FromHex("#00FF00"); private static readonly Color ElevatorButtonYellow = Color.FromHex("#FFFF00"); private static readonly Color ElevatorButtonRed = Color.FromHex("#FF0000"); + private readonly Dictionary _elevatorIndex = new(); + private readonly Dictionary _pointIndex = new(); + public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnElevatorStartup); + SubscribeLocalEvent(OnElevatorShutdown); + SubscribeLocalEvent(OnPointStartup); + SubscribeLocalEvent(OnPointShutdown); + SubscribeLocalEvent(OnButtonInteract); SubscribeLocalEvent(OnButtonActivate); + + InitializeDebug(); + } + + private void OnElevatorStartup(EntityUid uid, ComplexElevatorComponent comp, ComponentStartup args) + { + if (!string.IsNullOrEmpty(comp.ElevatorId)) + _elevatorIndex[comp.ElevatorId] = uid; + } + + private void OnElevatorShutdown(EntityUid uid, ComplexElevatorComponent comp, ComponentShutdown args) + { + if (!string.IsNullOrEmpty(comp.ElevatorId) && _elevatorIndex.TryGetValue(comp.ElevatorId, out var existing) && existing == uid) + _elevatorIndex.Remove(comp.ElevatorId); + } + + private void OnPointStartup(EntityUid uid, ElevatorPointComponent comp, ComponentStartup args) + { + if (!string.IsNullOrEmpty(comp.FloorId)) + _pointIndex[comp.FloorId] = uid; + } + + private void OnPointShutdown(EntityUid uid, ElevatorPointComponent comp, ComponentShutdown args) + { + if (!string.IsNullOrEmpty(comp.FloorId) && _pointIndex.TryGetValue(comp.FloorId, out var existing) && existing == uid) + _pointIndex.Remove(comp.FloorId); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var toRemove = new List(); + + while (query.MoveNext(out var uid, out var moving, out var elevator)) + { + if (moving.MovementStartTime == null) + { + moving.MovementStartTime = _timing.CurTime; + Dirty(uid, moving); + continue; + } + + var elapsed = _timing.CurTime - moving.MovementStartTime.Value; + + if (moving.Phase == ElevatorMovementPhase.DoorClosing) + { + if (elapsed >= elevator.DoorCloseDelay) + { + if (!CanCloseDoorsForFloor(elevator.ElevatorId, elevator.CurrentFloor)) + { + toRemove.Add(uid); + continue; + } + + TryCloseDoorsForFloor(elevator.ElevatorId, elevator.CurrentFloor); + + moving.Phase = ElevatorMovementPhase.WaitingForSend; + moving.MovementStartTime = _timing.CurTime; + Dirty(uid, moving); + } + } + else if (moving.Phase == ElevatorMovementPhase.WaitingForSend) + { + if (elapsed >= elevator.SendDelay) + { + if (elevator.UseIntermediateFloor) + { + ClearGasInTargetArea(uid, elevator.IntermediateFloorId); + KillEntitiesInTargetArea((uid, elevator), elevator.IntermediateFloorId); + elevator.CurrentFloor = elevator.IntermediateFloorId; + Dirty(uid, elevator); + + TeleportToFloor(uid, elevator.IntermediateFloorId, elevator); + + _audio.PlayPvs(elevator.TravelSound, uid); + + moving.Phase = ElevatorMovementPhase.Travelling; + moving.MovementStartTime = _timing.CurTime; + Dirty(uid, moving); + } + else + { + ClearGasInTargetArea(uid, moving.TargetFloor); + KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor); + elevator.CurrentFloor = moving.TargetFloor; + Dirty(uid, elevator); + + TeleportToFloor(uid, moving.TargetFloor, elevator); + + OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); + _audio.PlayPvs(elevator.ArrivalSound, uid); + + toRemove.Add(uid); + } + } + } + else if (moving.Phase == ElevatorMovementPhase.Travelling) + { + if (elapsed >= elevator.IntermediateDelay) + { + ClearGasInTargetArea(uid, moving.TargetFloor); + KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor); + elevator.CurrentFloor = moving.TargetFloor; + Dirty(uid, elevator); + + TeleportToFloor(uid, moving.TargetFloor, elevator); + + OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); + _audio.PlayPvs(elevator.ArrivalSound, uid); + + toRemove.Add(uid); + } + } + } + + foreach (var uid in toRemove) + { + RemComp(uid); + if (TryComp(uid, out var elevator)) + { + UpdateButtonLights((uid, elevator)); + } + } } private TimeSpan GetButtonUseDelay(Entity elevator, ElevatorButtonComponent button) { - return elevator.Comp.SendDelay + elevator.Comp.IntermediateDelay + TimeSpan.FromSeconds(1); + return elevator.Comp.DoorCloseDelay + elevator.Comp.SendDelay + elevator.Comp.IntermediateDelay + TimeSpan.FromSeconds(1); } private void SetButtonDelay(EntityUid button, Entity elevator) @@ -52,95 +194,87 @@ private void SetButtonDelay(EntityUid button, Entity e } } - private void StartMovement(Entity ent, string targetFloor) + private void TeleportToFloor(EntityUid uid, string floorId, ComplexElevatorComponent elevatorComp) { - KillEntitiesInTargetArea(ent, ent.Comp.IntermediateFloorId); - ent.Comp.CurrentFloor = ent.Comp.IntermediateFloorId; - TeleportToFloor(ent, ent.Comp.IntermediateFloorId); - - _audio.PlayPvs(ent.Comp.TravelSound, ent); - Timer.Spawn(ent.Comp.IntermediateDelay, () => + if (!TryFindPoint(floorId, out var point)) { - if (!Exists(ent)) - return; - - KillEntitiesInTargetArea(ent, targetFloor); - ent.Comp.CurrentFloor = targetFloor; - TeleportToFloor(ent, targetFloor); - OpenDoorsForFloor(ent.Comp.ElevatorId, targetFloor); + Log.Warning($"Could not find ElevatorPoint for floor {floorId} for elevator {elevatorComp.ElevatorId}"); + return; + } - _audio.PlayPvs(ent.Comp.ArrivalSound, ent); + var pointTransform = Transform(point.Value.Owner); + var elevatorTransform = Transform(uid); - ent.Comp.IsMoving = false; - UpdateButtonLights(ent); - }); - } + var aabb = _lookup.GetWorldAABB(uid, elevatorTransform); + var intersectingEntities = _lookup.GetEntitiesIntersecting(elevatorTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sundries); - private void TeleportToFloor(EntityUid uid, string floorId) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var pointUid, out var pointComp)) + var entitiesToTeleport = new List<(EntityUid, Vector2)>(); + foreach (var entUid in intersectingEntities) { - if (pointComp.FloorId != floorId) + if (entUid == uid || IsBlacklisted(entUid)) continue; - var pointTransform = Transform(pointUid); - var elevatorTransform = Transform(uid); + if (TryComp(entUid, out var buckle) && buckle.Buckled) + continue; - var aabb = _lookup.GetWorldAABB(uid, elevatorTransform); - var intersectingEntities = _lookup.GetEntitiesIntersecting(elevatorTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sensors); + var entTransform = Transform(entUid); + var relativePos = _transform.GetWorldPosition(entTransform) - _transform.GetWorldPosition(elevatorTransform); + entitiesToTeleport.Add((entUid, relativePos)); - var entitiesToTeleport = new List<(EntityUid, Vector2)>(); - foreach (var entUid in intersectingEntities) + if (TryComp(entUid, out var pullable) && pullable.BeingPulled) { - if (entUid == uid || HasComp(entUid)) - continue; - - var entTransform = Transform(entUid); - var relativePos = entTransform.LocalPosition - elevatorTransform.LocalPosition; - entitiesToTeleport.Add((entUid, relativePos)); + _pulling.TryStopPull(entUid, pullable); } - - _transform.SetCoordinates(uid, pointTransform.Coordinates); - - var newElevatorTransform = Transform(uid); - - foreach (var (entUid, relativePos) in entitiesToTeleport) + if (TryComp(entUid, out var puller) && puller.Pulling != null) { - var newCoordinates = new EntityCoordinates(newElevatorTransform.ParentUid, newElevatorTransform.LocalPosition + relativePos); - _transform.SetCoordinates(entUid, newCoordinates); + if (TryComp(puller.Pulling.Value, out var subjectPulling)) + { + _pulling.TryStopPull(puller.Pulling.Value, subjectPulling); + } } + } + + var targetWorldPos = _transform.GetWorldPosition(pointTransform); + var destParent = pointTransform.GridUid ?? pointTransform.MapUid; + + if (destParent == null) + return; - break; + foreach (var (entUid, relativePos) in entitiesToTeleport) + { + var entWorldPos = targetWorldPos + relativePos; + var destCoords = new EntityCoordinates(destParent.Value, entWorldPos - _transform.GetWorldPosition(destParent.Value)); + _transform.SetCoordinates(entUid, destCoords); } + + _transform.SetCoordinates(uid, pointTransform.Coordinates); } private void HandleButtonPress(Entity button, Entity elevator) { - if (elevator.Comp.IsMoving) + if (HasComp(elevator)) return; + + switch (button.Comp.ButtonType) { - switch (button.Comp.ButtonType) - { - case ElevatorButtonType.CallButton: - MoveToFloor(elevator, button.Comp.Floor); - break; - case ElevatorButtonType.SendElevatorUp: - MoveUp(elevator); - break; - case ElevatorButtonType.SendElevatorDown: - MoveDown(elevator); - break; - } - SetButtonDelay(button, elevator); + case ElevatorButtonType.CallButton: + MoveToFloor(elevator, button.Comp.Floor); + break; + case ElevatorButtonType.SendElevatorUp: + MoveUp(elevator); + break; + case ElevatorButtonType.SendElevatorDown: + MoveDown(elevator); + break; } + SetButtonDelay(button, elevator); } private void OnButtonInteract(Entity ent, ref InteractHandEvent args) { if (!TryFindElevator(ent.Comp.ElevatorId, out var elevator)) return; - + HandleButtonPress(ent, elevator.Value); args.Handled = true; } @@ -156,53 +290,43 @@ private void OnButtonActivate(Entity ent, ref ActivateI private bool TryFindElevator(string elevatorId, [NotNullWhen(true)] out Entity? ent) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + if (_elevatorIndex.TryGetValue(elevatorId, out var uid) && TryComp(uid, out var comp)) { - if (comp.ElevatorId == elevatorId) - { - ent = (uid, comp); - return true; - } + ent = (uid, comp); + return true; } + + ent = null; + return false; + } + + private bool TryFindPoint(string floorId, [NotNullWhen(true)] out Entity? ent) + { + if (_pointIndex.TryGetValue(floorId, out var uid) && TryComp(uid, out var comp)) + { + ent = (uid, comp); + return true; + } + ent = null; return false; } public void MoveToFloor(Entity ent, string targetFloor) { - if (ent.Comp.IsMoving || !ent.Comp.Floors.Contains(targetFloor) || ent.Comp.CurrentFloor == targetFloor) + if (HasComp(ent) || !ent.Comp.Floors.Contains(targetFloor) || ent.Comp.CurrentFloor == targetFloor) return; if (!CanCloseDoorsForFloor(ent.Comp.ElevatorId, ent.Comp.CurrentFloor)) return; - ent.Comp.IsMoving = true; - UpdateButtonLights(ent); - - Timer.Spawn(ent.Comp.SendDelay, () => - { - if (!Exists(ent) || !ent.Comp.IsMoving) - return; + var moving = EnsureComp(ent); + moving.TargetFloor = targetFloor; + moving.MovementStartTime = _timing.CurTime; + moving.Phase = ElevatorMovementPhase.DoorClosing; + Dirty(ent, moving); - StartMovement(ent, targetFloor); - }); - - Timer.Spawn(ent.Comp.DoorCloseDelay, () => - { - if (!Exists(ent) || !ent.Comp.IsMoving) - return; - - if (!CanCloseDoorsForFloor(ent.Comp.ElevatorId, ent.Comp.CurrentFloor)) - { - ent.Comp.IsMoving = false; - UpdateButtonLights(ent); - } - else - { - TryCloseDoorsForFloor(ent.Comp.ElevatorId, ent.Comp.CurrentFloor); - } - }); + UpdateButtonLights(ent); } public void MoveUp(Entity ent) @@ -221,7 +345,7 @@ public void MoveDown(Entity ent) private string? GetNextFloor(Entity ent, bool up) { - if (ent.Comp.IsMoving || ent.Comp.Floors.Count == 0) + if (HasComp(ent) || ent.Comp.Floors.Count == 0) return null; var currentIndex = ent.Comp.Floors.IndexOf(ent.Comp.CurrentFloor); @@ -317,34 +441,79 @@ private bool IsDoorBlocked(EntityUid doorUid, float range) return false; } + private void ClearGasInTargetArea(EntityUid elevator, string floorId) + { + if (!TryFindPoint(floorId, out var point)) + return; + + var pointTransform = Transform(point.Value.Owner); + if (pointTransform.GridUid == null) + return; + + if (!TryComp(pointTransform.GridUid, out var grid)) + return; + + var aabb = _lookup.GetWorldAABB(elevator, pointTransform); + var invMatrix = _transform.GetInvWorldMatrix(pointTransform.GridUid.Value); + var localAabb = invMatrix.TransformBox(aabb); + + var tileEnumerator = _mapSystem.GetLocalTilesEnumerator(pointTransform.GridUid.Value, grid, localAabb); + while (tileEnumerator.MoveNext(out var tileRef)) + { + var gas = _atmos.GetTileMixture(pointTransform.GridUid, null, tileRef.GridIndices, excite: true); + if (gas != null) + { + gas.Clear(); + } + } + } + private void KillEntitiesInTargetArea(Entity elevator, string floorId) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var pointUid, out var pointComp)) + if (!elevator.Comp.CrushEntitiesOnArrival) + return; + + if (!TryFindPoint(floorId, out var point)) + return; + + var pointTransform = Transform(point.Value.Owner); + var aabb = _lookup.GetWorldAABB(elevator.Owner, pointTransform); + var intersectingEntities = _lookup.GetEntitiesIntersecting(pointTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sundries); + + foreach (var entUid in intersectingEntities) { - if (pointComp.FloorId != floorId) + if (entUid == elevator.Owner || IsBlacklisted(entUid)) continue; - var pointTransform = Transform(pointUid); - - var aabb = _lookup.GetWorldAABB(elevator.Owner, pointTransform); - var intersectingEntities = _lookup.GetEntitiesIntersecting(pointTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sensors); + var damage = new DamageSpecifier(); + damage.DamageDict["Blunt"] = elevator.Comp.CrushDamage; + _damageable.TryChangeDamage(entUid, damage, true); + } + } - foreach (var entUid in intersectingEntities) + private bool IsBlacklisted(EntityUid entity) + { + foreach (var comp in EntityManager.GetComponents(entity)) + { + var compName = _componentFactory.GetRegistration(comp).Name; + + if (compName == "ElevatorPoint" || + compName == "ElevatorButton" || + compName == "ElevatorDoor" || + compName == "Marker" || + compName.EndsWith("Spawner") || + compName.Contains("SpawnPoint")) { - if (entUid == elevator.Owner) - continue; - - var damage = new DamageSpecifier(); - damage.DamageDict["Blunt"] = 2000; - _damageable.TryChangeDamage(entUid, damage, true); + return true; } - break; } + + return false; } private void UpdateButtonLights(Entity elevator) { + var isMoving = HasComp(elevator); var query = EntityQueryEnumerator(); while (query.MoveNext(out var buttonUid, out var buttonComp, out var light)) { @@ -354,7 +523,7 @@ private void UpdateButtonLights(Entity elevator) Color color = ElevatorButtonRed; if (buttonComp.ButtonType == ElevatorButtonType.CallButton) { - if (elevator.Comp.IsMoving) + if (isMoving) color = ElevatorButtonYellow; else if (buttonComp.Floor == elevator.Comp.CurrentFloor) color = ElevatorButtonGreen; @@ -365,6 +534,4 @@ private void UpdateButtonLights(Entity elevator) _pointLight.SetColor(buttonUid, color, light); } } -} - - +} \ No newline at end of file diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorCommands.cs b/Content.Server/_Scp/ComplexElevator/ElevatorCommands.cs index 62e5f66d5ae..7abb81493c2 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorCommands.cs +++ b/Content.Server/_Scp/ComplexElevator/ElevatorCommands.cs @@ -1,5 +1,4 @@ -using System.Linq; -using Content.Server._Scp.ComplexElevator; +using Content.Shared._Scp.ComplexElevator; using Content.Shared.Administration; using Robust.Shared.Console; using Robust.Shared.GameObjects; @@ -82,6 +81,7 @@ private void HandleAdd(IConsoleShell shell, string[] args, Entity Date: Wed, 1 Jul 2026 15:54:07 +0300 Subject: [PATCH 2/4] Elevator-2.1 --- .../ComplexElevatorComponent.cs | 30 +++++++++++++------ .../ElevatorButtonComponent.cs | 5 +++- .../ComplexElevator/ElevatorDoorComponent.cs | 7 +++-- .../ElevatorMovingComponent.cs | 25 ++++++++++++++++ .../ComplexElevator/ElevatorPointComponent.cs | 5 ++-- 5 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs b/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs index 56c7adf0e6c..2eedc06a999 100644 --- a/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs +++ b/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs @@ -1,25 +1,27 @@ -using System.Collections.Generic; using Robust.Shared.Audio; -using Robust.Shared.GameObjects; -using Robust.Shared.Maths; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; -namespace Content.Server._Scp.ComplexElevator; +namespace Content.Shared._Scp.ComplexElevator; -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ComplexElevatorComponent : Component { [DataField] public string ElevatorId = string.Empty; - [DataField] + [DataField, AutoNetworkedField] public string CurrentFloor = "IntermediateFloor"; - [DataField] + [DataField, AutoNetworkedField] public List Floors = new(); [DataField] public string IntermediateFloorId = "IntermediateFloor"; + [DataField] + public bool UseIntermediateFloor = true; + [DataField] public TimeSpan SendDelay = TimeSpan.FromSeconds(1); @@ -41,5 +43,15 @@ public sealed partial class ComplexElevatorComponent : Component [DataField] public float DoorBlockCheckRange = 0.6f; - public bool IsMoving = false; -} \ No newline at end of file + [DataField] + public bool TeleportBuckled = true; + + [DataField] + public bool TeleportPulled = true; + + [DataField] + public bool CrushEntitiesOnArrival = true; + + [DataField] + public float CrushDamage = 2000f; +} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs index 1d0a178f644..bd6c356b9c9 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs +++ b/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs @@ -1,4 +1,7 @@ -namespace Content.Server._Scp.ComplexElevator; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.ComplexElevator; [RegisterComponent] public sealed partial class ElevatorButtonComponent : Component diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs index c0bce80dafc..852a3103daf 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs +++ b/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs @@ -1,4 +1,7 @@ -namespace Content.Server._Scp.ComplexElevator; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.ComplexElevator; [RegisterComponent] public sealed partial class ElevatorDoorComponent : Component @@ -8,4 +11,4 @@ public sealed partial class ElevatorDoorComponent : Component [DataField] public string Floor = ""; -} \ No newline at end of file +} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs new file mode 100644 index 00000000000..152d498ee8f --- /dev/null +++ b/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs @@ -0,0 +1,25 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.ComplexElevator; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class ElevatorMovingComponent : Component +{ + [AutoNetworkedField] + public string TargetFloor = string.Empty; + + [AutoNetworkedField, AutoPausedField] + public TimeSpan? MovementStartTime; + + [AutoNetworkedField] + public ElevatorMovementPhase Phase = ElevatorMovementPhase.DoorClosing; +} + +[Serializable, NetSerializable] +public enum ElevatorMovementPhase : byte +{ + DoorClosing, + WaitingForSend, + Travelling +} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs index cedc841aea2..2a0c793e85e 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs +++ b/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs @@ -1,10 +1,11 @@ using Robust.Shared.GameStates; +using Robust.Shared.Serialization; -namespace Content.Server._Scp.ComplexElevator; +namespace Content.Shared._Scp.ComplexElevator; [RegisterComponent] public sealed partial class ElevatorPointComponent : Component { [DataField] public string FloorId = ""; -} \ No newline at end of file +} From 4922cb2e6b4c4e64eb432a0bf371e92d0c739d6d Mon Sep 17 00:00:00 2001 From: arthu241243 Date: Thu, 2 Jul 2026 20:02:34 +0300 Subject: [PATCH 3/4] Elevator 3.0 --- .../_Scp/ComplexElevator/ComplexElevatorSystem.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs index 62c217086bd..27b2feee610 100644 --- a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs +++ b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs @@ -94,7 +94,6 @@ public override void Update(float frameTime) if (moving.MovementStartTime == null) { moving.MovementStartTime = _timing.CurTime; - Dirty(uid, moving); continue; } @@ -123,12 +122,13 @@ public override void Update(float frameTime) { if (elevator.UseIntermediateFloor) { - ClearGasInTargetArea(uid, elevator.IntermediateFloorId); - KillEntitiesInTargetArea((uid, elevator), elevator.IntermediateFloorId); - elevator.CurrentFloor = elevator.IntermediateFloorId; - Dirty(uid, elevator); + if (TeleportToFloor(uid, elevator.IntermediateFloorId, elevator)) + { + ClearGasInTargetArea(uid, elevator.IntermediateFloorId); + KillEntitiesInTargetArea((uid, elevator), elevator.IntermediateFloorId); - TeleportToFloor(uid, elevator.IntermediateFloorId, elevator); + elevator.CurrentFloor = elevator.IntermediateFloorId; + Dirty(uid, elevator); _audio.PlayPvs(elevator.TravelSound, uid); @@ -324,7 +324,6 @@ public void MoveToFloor(Entity ent, string targetFloor moving.TargetFloor = targetFloor; moving.MovementStartTime = _timing.CurTime; moving.Phase = ElevatorMovementPhase.DoorClosing; - Dirty(ent, moving); UpdateButtonLights(ent); } From 84706cd6fb583453b0d3450c71f3ba90f08cb0ac Mon Sep 17 00:00:00 2001 From: arthu241243 Date: Thu, 2 Jul 2026 20:03:16 +0300 Subject: [PATCH 4/4] Elevator 3.0 --- .../ComplexElevator/ComplexElevatorSystem.cs | 155 +++++++++++------- .../ComplexElevator/ElevatorDoorComponent.cs | 14 -- .../ElevatorMovingComponent.cs | 13 +- .../ComplexElevatorComponent.cs | 3 + .../ElevatorButtonComponent.cs | 11 +- .../ComplexElevator/ElevatorDoorComponent.cs | 15 ++ .../ComplexElevator/ElevatorPointComponent.cs | 7 +- 7 files changed, 130 insertions(+), 88 deletions(-) delete mode 100644 Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs rename {Content.Server => Content.Shared}/_Scp/ComplexElevator/ComplexElevatorComponent.cs (94%) rename {Content.Server => Content.Shared}/_Scp/ComplexElevator/ElevatorButtonComponent.cs (64%) create mode 100644 Content.Shared/_Scp/ComplexElevator/ElevatorDoorComponent.cs rename {Content.Server => Content.Shared}/_Scp/ComplexElevator/ElevatorPointComponent.cs (50%) diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs index 27b2feee610..a2512a30dfa 100644 --- a/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs +++ b/Content.Server/_Scp/ComplexElevator/ComplexElevatorSystem.cs @@ -1,9 +1,13 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Numerics; using Content.Server.Atmos.EntitySystems; using Content.Server.Doors.Systems; using Content.Shared._Scp.ComplexElevator; using Robust.Shared.GameObjects; +using ElevatorButtonComponent = Content.Shared._Scp.ComplexElevator.ElevatorButtonComponent; +using ElevatorPointComponent = Content.Shared._Scp.ComplexElevator.ElevatorPointComponent; +using ElevatorDoorComponent = Content.Shared._Scp.ComplexElevator.ElevatorDoorComponent; using Content.Shared.Buckle.Components; using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -113,7 +117,6 @@ public override void Update(float frameTime) moving.Phase = ElevatorMovementPhase.WaitingForSend; moving.MovementStartTime = _timing.CurTime; - Dirty(uid, moving); } } else if (moving.Phase == ElevatorMovementPhase.WaitingForSend) @@ -122,33 +125,41 @@ public override void Update(float frameTime) { if (elevator.UseIntermediateFloor) { - if (TeleportToFloor(uid, elevator.IntermediateFloorId, elevator)) - { - ClearGasInTargetArea(uid, elevator.IntermediateFloorId); - KillEntitiesInTargetArea((uid, elevator), elevator.IntermediateFloorId); + var toTeleport = CollectEntitiesOnElevator(uid); + var exempt = new HashSet(toTeleport.Select(t => t.Item1)); + + ClearGasInTargetArea(uid, elevator.IntermediateFloorId); + KillEntitiesInTargetArea((uid, elevator), elevator.IntermediateFloorId, exempt); + if (TeleportToFloor(uid, elevator.IntermediateFloorId, elevator, toTeleport)) + { elevator.CurrentFloor = elevator.IntermediateFloorId; Dirty(uid, elevator); - _audio.PlayPvs(elevator.TravelSound, uid); + _audio.PlayPvs(elevator.TravelSound, uid); - moving.Phase = ElevatorMovementPhase.Travelling; - moving.MovementStartTime = _timing.CurTime; - Dirty(uid, moving); + moving.Phase = ElevatorMovementPhase.Travelling; + moving.MovementStartTime = _timing.CurTime; + } } else { + var toTeleport = CollectEntitiesOnElevator(uid); + var exempt = new HashSet(toTeleport.Select(t => t.Item1)); + ClearGasInTargetArea(uid, moving.TargetFloor); - KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor); - elevator.CurrentFloor = moving.TargetFloor; - Dirty(uid, elevator); + KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor, exempt); - TeleportToFloor(uid, moving.TargetFloor, elevator); + if (TeleportToFloor(uid, moving.TargetFloor, elevator, toTeleport)) + { + elevator.CurrentFloor = moving.TargetFloor; + Dirty(uid, elevator); - OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); - _audio.PlayPvs(elevator.ArrivalSound, uid); + OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); + _audio.PlayPvs(elevator.ArrivalSound, uid); - toRemove.Add(uid); + toRemove.Add(uid); + } } } } @@ -156,27 +167,32 @@ public override void Update(float frameTime) { if (elapsed >= elevator.IntermediateDelay) { + var toTeleport = CollectEntitiesOnElevator(uid); + var exempt = new HashSet(toTeleport.Select(t => t.Item1)); + ClearGasInTargetArea(uid, moving.TargetFloor); - KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor); - elevator.CurrentFloor = moving.TargetFloor; - Dirty(uid, elevator); + KillEntitiesInTargetArea((uid, elevator), moving.TargetFloor, exempt); - TeleportToFloor(uid, moving.TargetFloor, elevator); + if (TeleportToFloor(uid, moving.TargetFloor, elevator, toTeleport)) + { + elevator.CurrentFloor = moving.TargetFloor; + Dirty(uid, elevator); - OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); - _audio.PlayPvs(elevator.ArrivalSound, uid); + OpenDoorsForFloor(elevator.ElevatorId, moving.TargetFloor); + _audio.PlayPvs(elevator.ArrivalSound, uid); - toRemove.Add(uid); + toRemove.Add(uid); + } } } - } - foreach (var uid in toRemove) - { - RemComp(uid); - if (TryComp(uid, out var elevator)) + foreach (var movingUid in toRemove) { - UpdateButtonLights((uid, elevator)); + RemComp(movingUid); + if (TryComp(movingUid, out var movingElevator)) + { + UpdateButtonLights((movingUid, movingElevator)); + } } } } @@ -194,42 +210,46 @@ private void SetButtonDelay(EntityUid button, Entity e } } - private void TeleportToFloor(EntityUid uid, string floorId, ComplexElevatorComponent elevatorComp) + private bool TeleportToFloor(EntityUid uid, string floorId, ComplexElevatorComponent elevatorComp, List<(EntityUid, Vector2)>? precomputed = null) { if (!TryFindPoint(floorId, out var point)) { Log.Warning($"Could not find ElevatorPoint for floor {floorId} for elevator {elevatorComp.ElevatorId}"); - return; + return false; } var pointTransform = Transform(point.Value.Owner); var elevatorTransform = Transform(uid); - var aabb = _lookup.GetWorldAABB(uid, elevatorTransform); - var intersectingEntities = _lookup.GetEntitiesIntersecting(elevatorTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sundries); + var entitiesToTeleport = precomputed ?? new List<(EntityUid, Vector2)>(); - var entitiesToTeleport = new List<(EntityUid, Vector2)>(); - foreach (var entUid in intersectingEntities) + if (precomputed == null) { - if (entUid == uid || IsBlacklisted(entUid)) - continue; + var aabb = _lookup.GetWorldAABB(uid, elevatorTransform); + var intersectingEntities = _lookup.GetEntitiesIntersecting(elevatorTransform.MapID, aabb, LookupFlags.Static | LookupFlags.Dynamic | LookupFlags.Sundries); - if (TryComp(entUid, out var buckle) && buckle.Buckled) - continue; + foreach (var entUid in intersectingEntities) + { + if (entUid == uid || IsBlacklisted(entUid)) + continue; - var entTransform = Transform(entUid); - var relativePos = _transform.GetWorldPosition(entTransform) - _transform.GetWorldPosition(elevatorTransform); - entitiesToTeleport.Add((entUid, relativePos)); + if (TryComp(entUid, out var buckle) && buckle.Buckled) + continue; - if (TryComp(entUid, out var pullable) && pullable.BeingPulled) - { - _pulling.TryStopPull(entUid, pullable); - } - if (TryComp(entUid, out var puller) && puller.Pulling != null) - { - if (TryComp(puller.Pulling.Value, out var subjectPulling)) + var entTransform = Transform(entUid); + var relativePos = _transform.GetWorldPosition(entTransform) - _transform.GetWorldPosition(elevatorTransform); + entitiesToTeleport.Add((entUid, relativePos)); + + if (TryComp(entUid, out var pullable) && pullable.BeingPulled) + { + _pulling.TryStopPull(entUid, pullable); + } + if (TryComp(entUid, out var puller) && puller.Pulling != null) { - _pulling.TryStopPull(puller.Pulling.Value, subjectPulling); + if (TryComp(puller.Pulling.Value, out var subjectPulling)) + { + _pulling.TryStopPull(puller.Pulling.Value, subjectPulling); + } } } } @@ -238,7 +258,7 @@ private void TeleportToFloor(EntityUid uid, string floorId, ComplexElevatorCompo var destParent = pointTransform.GridUid ?? pointTransform.MapUid; if (destParent == null) - return; + return false; foreach (var (entUid, relativePos) in entitiesToTeleport) { @@ -248,6 +268,30 @@ private void TeleportToFloor(EntityUid uid, string floorId, ComplexElevatorCompo } _transform.SetCoordinates(uid, pointTransform.Coordinates); + return true; + } + + private List<(EntityUid, Vector2)> CollectEntitiesOnElevator(EntityUid uid) + { + var elevatorTransform = Transform(uid); + var aabb = _lookup.GetWorldAABB(uid, elevatorTransform); + var intersecting = _lookup.GetEntitiesIntersecting(elevatorTransform.MapID, aabb, LookupFlags.Static | LookupFlags.Dynamic | LookupFlags.Sundries); + + var entities = new List<(EntityUid, Vector2)>(); + foreach (var entUid in intersecting) + { + if (entUid == uid || IsBlacklisted(entUid)) + continue; + + if (TryComp(entUid, out var buckle) && buckle.Buckled) + continue; + + var entTransform = Transform(entUid); + var relativePos = _transform.GetWorldPosition(entTransform) - _transform.GetWorldPosition(elevatorTransform); + entities.Add((entUid, relativePos)); + } + + return entities; } private void HandleButtonPress(Entity button, Entity elevator) @@ -467,7 +511,7 @@ private void ClearGasInTargetArea(EntityUid elevator, string floorId) } } - private void KillEntitiesInTargetArea(Entity elevator, string floorId) + private void KillEntitiesInTargetArea(Entity elevator, string floorId, HashSet? exempt = null) { if (!elevator.Comp.CrushEntitiesOnArrival) return; @@ -477,11 +521,12 @@ private void KillEntitiesInTargetArea(Entity elevator, var pointTransform = Transform(point.Value.Owner); var aabb = _lookup.GetWorldAABB(elevator.Owner, pointTransform); - var intersectingEntities = _lookup.GetEntitiesIntersecting(pointTransform.MapID, aabb, LookupFlags.Dynamic | LookupFlags.Sundries); + + var intersectingEntities = _lookup.GetEntitiesIntersecting(pointTransform.MapID, aabb, LookupFlags.Static | LookupFlags.Dynamic | LookupFlags.Sundries); foreach (var entUid in intersectingEntities) { - if (entUid == elevator.Owner || IsBlacklisted(entUid)) + if (entUid == elevator.Owner || IsBlacklisted(entUid) || (exempt != null && exempt.Contains(entUid))) continue; var damage = new DamageSpecifier(); @@ -533,4 +578,4 @@ private void UpdateButtonLights(Entity elevator) _pointLight.SetColor(buttonUid, color, light); } } -} \ No newline at end of file +} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs deleted file mode 100644 index 852a3103daf..00000000000 --- a/Content.Server/_Scp/ComplexElevator/ElevatorDoorComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared._Scp.ComplexElevator; - -[RegisterComponent] -public sealed partial class ElevatorDoorComponent : Component -{ - [DataField] - public string ElevatorId = ""; - - [DataField] - public string Floor = ""; -} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs b/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs index 152d498ee8f..328fbd573ba 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs +++ b/Content.Server/_Scp/ComplexElevator/ElevatorMovingComponent.cs @@ -1,22 +1,13 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; +namespace Content.Server._Scp.ComplexElevator; -namespace Content.Shared._Scp.ComplexElevator; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +[RegisterComponent] public sealed partial class ElevatorMovingComponent : Component { - [AutoNetworkedField] public string TargetFloor = string.Empty; - - [AutoNetworkedField, AutoPausedField] public TimeSpan? MovementStartTime; - - [AutoNetworkedField] public ElevatorMovementPhase Phase = ElevatorMovementPhase.DoorClosing; } -[Serializable, NetSerializable] public enum ElevatorMovementPhase : byte { DoorClosing, diff --git a/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs b/Content.Shared/_Scp/ComplexElevator/ComplexElevatorComponent.cs similarity index 94% rename from Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs rename to Content.Shared/_Scp/ComplexElevator/ComplexElevatorComponent.cs index 2eedc06a999..7ea9b073d19 100644 --- a/Content.Server/_Scp/ComplexElevator/ComplexElevatorComponent.cs +++ b/Content.Shared/_Scp/ComplexElevator/ComplexElevatorComponent.cs @@ -1,5 +1,8 @@ +using System.Collections.Generic; using Robust.Shared.Audio; +using Robust.Shared.GameObjects; using Robust.Shared.GameStates; +using Robust.Shared.Maths; using Robust.Shared.Serialization; namespace Content.Shared._Scp.ComplexElevator; diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs b/Content.Shared/_Scp/ComplexElevator/ElevatorButtonComponent.cs similarity index 64% rename from Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs rename to Content.Shared/_Scp/ComplexElevator/ElevatorButtonComponent.cs index bd6c356b9c9..ff60df111d5 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorButtonComponent.cs +++ b/Content.Shared/_Scp/ComplexElevator/ElevatorButtonComponent.cs @@ -1,22 +1,23 @@ +using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared._Scp.ComplexElevator; -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ElevatorButtonComponent : Component { - [DataField] + [DataField, AutoNetworkedField] public string ElevatorId = string.Empty; - [DataField] + [DataField, AutoNetworkedField] public ElevatorButtonType ButtonType = ElevatorButtonType.CallButton; - [DataField] + [DataField, AutoNetworkedField] public string Floor = string.Empty; - } +[Serializable, NetSerializable] public enum ElevatorButtonType { CallButton, diff --git a/Content.Shared/_Scp/ComplexElevator/ElevatorDoorComponent.cs b/Content.Shared/_Scp/ComplexElevator/ElevatorDoorComponent.cs new file mode 100644 index 00000000000..19889e411ee --- /dev/null +++ b/Content.Shared/_Scp/ComplexElevator/ElevatorDoorComponent.cs @@ -0,0 +1,15 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared._Scp.ComplexElevator; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ElevatorDoorComponent : Component +{ + [DataField, AutoNetworkedField] + public string ElevatorId = string.Empty; + + [DataField, AutoNetworkedField] + public string Floor = string.Empty; +} diff --git a/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs b/Content.Shared/_Scp/ComplexElevator/ElevatorPointComponent.cs similarity index 50% rename from Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs rename to Content.Shared/_Scp/ComplexElevator/ElevatorPointComponent.cs index 2a0c793e85e..ce28183a598 100644 --- a/Content.Server/_Scp/ComplexElevator/ElevatorPointComponent.cs +++ b/Content.Shared/_Scp/ComplexElevator/ElevatorPointComponent.cs @@ -1,11 +1,12 @@ +using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared._Scp.ComplexElevator; -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ElevatorPointComponent : Component { - [DataField] - public string FloorId = ""; + [DataField, AutoNetworkedField] + public string FloorId = string.Empty; }