From 844c56c0167d43bdac9a9033b8aa7a586eb23e41 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Fri, 21 Apr 2023 20:04:20 +1000 Subject: [PATCH] Fix thruster postmapinit (#15623) --- .../Construction/Completions/BuildMachine.cs | 2 +- .../ConstructionSystem.Machine.cs | 33 +- .../Construction/PartExchangerSystem.cs | 2 +- .../Shuttles/Components/ShuttleComponent.cs | 4 +- .../Shuttles/Components/ThrusterComponent.cs | 10 +- .../Shuttles/Systems/ShuttleSystem.cs | 268 ++--- .../Shuttles/Systems/ThrusterSystem.cs | 917 +++++++++--------- 7 files changed, 638 insertions(+), 598 deletions(-) diff --git a/Content.Server/Construction/Completions/BuildMachine.cs b/Content.Server/Construction/Completions/BuildMachine.cs index c37c057321..909fd5ab1b 100644 --- a/Content.Server/Construction/Completions/BuildMachine.cs +++ b/Content.Server/Construction/Completions/BuildMachine.cs @@ -100,7 +100,7 @@ namespace Content.Server.Construction.Completions if (entityManager.TryGetComponent(machine, out MachineComponent? machineComp)) { - constructionSystem.RefreshParts(machineComp); + constructionSystem.RefreshParts(machine, machineComp); } var entChangeEv = new ConstructionChangeEntityEvent(machine, uid); diff --git a/Content.Server/Construction/ConstructionSystem.Machine.cs b/Content.Server/Construction/ConstructionSystem.Machine.cs index 18dd4aef63..62abe9e567 100644 --- a/Content.Server/Construction/ConstructionSystem.Machine.cs +++ b/Content.Server/Construction/ConstructionSystem.Machine.cs @@ -5,6 +5,7 @@ using Content.Shared.Construction.Components; using Content.Shared.Construction.Prototypes; using Content.Shared.Verbs; using Robust.Shared.Containers; +using Robust.Shared.Map.Components; using Robust.Shared.Utility; namespace Content.Server.Construction; @@ -28,8 +29,8 @@ public sealed partial class ConstructionSystem private void OnMachineMapInit(EntityUid uid, MachineComponent component, MapInitEvent args) { - CreateBoardAndStockParts(component); - RefreshParts(component); + CreateBoardAndStockParts(uid, component); + RefreshParts(uid, component); } private void OnMachineExaminableVerb(EntityUid uid, MachineComponent component, GetVerbsEvent args) @@ -99,21 +100,21 @@ public sealed partial class ConstructionSystem return output; } - public void RefreshParts(MachineComponent component) + public void RefreshParts(EntityUid uid, MachineComponent component) { var parts = GetAllParts(component); - EntityManager.EventBus.RaiseLocalEvent(component.Owner, new RefreshPartsEvent + EntityManager.EventBus.RaiseLocalEvent(uid, new RefreshPartsEvent { Parts = parts, PartRatings = GetPartsRatings(parts), }, true); } - public void CreateBoardAndStockParts(MachineComponent component) + private void CreateBoardAndStockParts(EntityUid uid, MachineComponent component) { // Entity might not be initialized yet. - var boardContainer = _container.EnsureContainer(component.Owner, MachineFrameComponent.BoardContainerName); - var partContainer = _container.EnsureContainer(component.Owner, MachineFrameComponent.PartContainerName); + var boardContainer = _container.EnsureContainer(uid, MachineFrameComponent.BoardContainerName); + var partContainer = _container.EnsureContainer(uid, MachineFrameComponent.PartContainerName); if (string.IsNullOrEmpty(component.BoardPrototype)) return; @@ -122,11 +123,11 @@ public sealed partial class ConstructionSystem if (boardContainer.ContainedEntities.Count > 0) return; - var board = EntityManager.SpawnEntity(component.BoardPrototype, Transform(component.Owner).Coordinates); + var board = EntityManager.SpawnEntity(component.BoardPrototype, Transform(uid).Coordinates); if (!component.BoardContainer.Insert(board)) { - throw new Exception($"Couldn't insert board with prototype {component.BoardPrototype} to machine with prototype {MetaData(component.Owner).EntityPrototype?.ID ?? "N/A"}!"); + throw new Exception($"Couldn't insert board with prototype {component.BoardPrototype} to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}!"); } if (!TryComp(board, out var machineBoard)) @@ -134,7 +135,7 @@ public sealed partial class ConstructionSystem throw new Exception($"Entity with prototype {component.BoardPrototype} doesn't have a {nameof(MachineBoardComponent)}!"); } - var xform = Transform(component.Owner); + var xform = Transform(uid); foreach (var (part, amount) in machineBoard.Requirements) { var partProto = _prototypeManager.Index(part); @@ -149,20 +150,20 @@ public sealed partial class ConstructionSystem foreach (var (stackType, amount) in machineBoard.MaterialRequirements) { - var stack = _stackSystem.Spawn(amount, stackType, Transform(component.Owner).Coordinates); + var stack = _stackSystem.Spawn(amount, stackType, Transform(uid).Coordinates); if (!partContainer.Insert(stack)) - throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {MetaData(component.Owner).EntityPrototype?.ID ?? "N/A"}"); + throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); } foreach (var (compName, info) in machineBoard.ComponentRequirements) { for (var i = 0; i < info.Amount; i++) { - var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(component.Owner).Coordinates); + var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(uid).Coordinates); if(!partContainer.Insert(c)) - throw new Exception($"Couldn't insert machine component part with default prototype '{compName}' to machine with prototype {MetaData(component.Owner).EntityPrototype?.ID ?? "N/A"}"); + throw new Exception($"Couldn't insert machine component part with default prototype '{compName}' to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); } } @@ -170,10 +171,10 @@ public sealed partial class ConstructionSystem { for (var i = 0; i < info.Amount; i++) { - var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(component.Owner).Coordinates); + var c = EntityManager.SpawnEntity(info.DefaultPrototype, Transform(uid).Coordinates); if(!partContainer.Insert(c)) - throw new Exception($"Couldn't insert machine component part with default prototype '{tagName}' to machine with prototype {MetaData(component.Owner).EntityPrototype?.ID ?? "N/A"}"); + throw new Exception($"Couldn't insert machine component part with default prototype '{tagName}' to machine with prototype {MetaData(uid).EntityPrototype?.ID ?? "N/A"}"); } } } diff --git a/Content.Server/Construction/PartExchangerSystem.cs b/Content.Server/Construction/PartExchangerSystem.cs index 2ea4cd10f3..cfd97390a5 100644 --- a/Content.Server/Construction/PartExchangerSystem.cs +++ b/Content.Server/Construction/PartExchangerSystem.cs @@ -83,7 +83,7 @@ public sealed class PartExchangerSystem : EntitySystem storage.Storage.Insert(unused.Owner); _storage.Insert(uid, unused.Owner, null, false); } - _construction.RefreshParts(machine); + _construction.RefreshParts(args.Args.Target.Value, machine); args.Handled = true; } diff --git a/Content.Server/Shuttles/Components/ShuttleComponent.cs b/Content.Server/Shuttles/Components/ShuttleComponent.cs index 83a78fc61d..6917eefcf8 100644 --- a/Content.Server/Shuttles/Components/ShuttleComponent.cs +++ b/Content.Server/Shuttles/Components/ShuttleComponent.cs @@ -15,12 +15,12 @@ namespace Content.Server.Shuttles.Components /// /// The thrusters contributing to each direction for impulse. /// - public readonly List[] LinearThrusters = new List[4]; + public readonly List[] LinearThrusters = new List[4]; /// /// The thrusters contributing to the angular impulse of the shuttle. /// - public readonly List AngularThrusters = new(); + public readonly List AngularThrusters = new(); [ViewVariables] public float AngularThrust = 0f; diff --git a/Content.Server/Shuttles/Components/ThrusterComponent.cs b/Content.Server/Shuttles/Components/ThrusterComponent.cs index f4ce81de6a..bc66b89724 100644 --- a/Content.Server/Shuttles/Components/ThrusterComponent.cs +++ b/Content.Server/Shuttles/Components/ThrusterComponent.cs @@ -1,6 +1,7 @@ using Content.Server.Shuttles.Systems; using Content.Shared.Construction.Prototypes; using Content.Shared.Damage; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Shuttles.Components @@ -42,7 +43,8 @@ namespace Content.Server.Shuttles.Components /// public bool IsOn; - [ViewVariables(VVAccess.ReadWrite)] + // Need to serialize this because RefreshParts isn't called on Init and this will break post-mapinit maps! + [ViewVariables(VVAccess.ReadWrite), DataField("thrust")] public float Thrust; [DataField("baseThrust"), ViewVariables(VVAccess.ReadWrite)] @@ -73,6 +75,12 @@ namespace Content.Server.Shuttles.Components public bool Firing = false; + /// + /// Next time we tick damage for anyone colliding. + /// + [ViewVariables(VVAccess.ReadWrite), DataField("nextFire", customTypeSerializer:typeof(TimeOffsetSerializer))] + public TimeSpan NextFire; + [DataField("machinePartThrust", customTypeSerializer: typeof(PrototypeIdSerializer))] public string MachinePartThrust = "Laser"; diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.cs index f1ab8c078d..577e716a68 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.cs @@ -12,165 +12,165 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Random; -namespace Content.Server.Shuttles.Systems +namespace Content.Server.Shuttles.Systems; + +[UsedImplicitly] +public sealed partial class ShuttleSystem : SharedShuttleSystem { - [UsedImplicitly] - public sealed partial class ShuttleSystem : SharedShuttleSystem + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AirlockSystem _airlock = default!; + [Dependency] private readonly DockingSystem _dockSystem = default!; + [Dependency] private readonly DoorSystem _doors = default!; + [Dependency] private readonly FixtureSystem _fixtures = default!; + [Dependency] private readonly MapLoaderSystem _loader = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly ShuttleConsoleSystem _console = default!; + [Dependency] private readonly StunSystem _stuns = default!; + [Dependency] private readonly ThrusterSystem _thruster = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + + private ISawmill _sawmill = default!; + + public const float TileMassMultiplier = 0.5f; + + public const float ShuttleLinearDamping = 0.05f; + public const float ShuttleAngularDamping = 0.05f; + + public override void Initialize() { - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly AirlockSystem _airlock = default!; - [Dependency] private readonly DockingSystem _dockSystem = default!; - [Dependency] private readonly DoorSystem _doors = default!; - [Dependency] private readonly FixtureSystem _fixtures = default!; - [Dependency] private readonly MapLoaderSystem _loader = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly ShuttleConsoleSystem _console = default!; - [Dependency] private readonly StunSystem _stuns = default!; - [Dependency] private readonly ThrusterSystem _thruster = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + base.Initialize(); + _sawmill = Logger.GetSawmill("shuttles"); - private ISawmill _sawmill = default!; + InitializeFTL(); + InitializeGridFills(); + InitializeIFF(); + InitializeImpact(); - public const float TileMassMultiplier = 0.5f; + SubscribeLocalEvent(OnShuttleAdd); + SubscribeLocalEvent(OnShuttleStartup); + SubscribeLocalEvent(OnShuttleShutdown); - public const float ShuttleLinearDamping = 0.05f; - public const float ShuttleAngularDamping = 0.05f; + SubscribeLocalEvent(OnRoundRestart); - public override void Initialize() + SubscribeLocalEvent(OnGridInit); + SubscribeLocalEvent(OnGridFixtureChange); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + UpdateHyperspace(frameTime); + } + + private void OnRoundRestart(RoundRestartCleanupEvent ev) + { + CleanupHyperspace(); + } + + private void OnShuttleAdd(EntityUid uid, ShuttleComponent component, ComponentAdd args) + { + // Easier than doing it in the comp and they don't have constructors. + for (var i = 0; i < component.LinearThrusters.Length; i++) { - base.Initialize(); - _sawmill = Logger.GetSawmill("shuttles"); + component.LinearThrusters[i] = new List(); + } + } - InitializeFTL(); - InitializeGridFills(); - InitializeIFF(); - InitializeImpact(); + private void OnGridFixtureChange(GridFixtureChangeEvent args) + { + // Look this is jank but it's a placeholder until we design it. + if (args.NewFixtures.Count == 0) + return; - SubscribeLocalEvent(OnShuttleAdd); - SubscribeLocalEvent(OnShuttleStartup); - SubscribeLocalEvent(OnShuttleShutdown); + var uid = args.NewFixtures[0].Body.Owner; + var manager = Comp(uid); - SubscribeLocalEvent(OnRoundRestart); - - SubscribeLocalEvent(OnGridInit); - SubscribeLocalEvent(OnGridFixtureChange); + foreach (var fixture in args.NewFixtures) + { + _physics.SetDensity(uid, fixture, TileMassMultiplier, false, manager); + _fixtures.SetRestitution(uid, fixture, 0.1f, false, manager); } - public override void Update(float frameTime) + _fixtures.FixtureUpdate(uid, manager: manager); + } + + private void OnGridInit(GridInitializeEvent ev) + { + if (HasComp(ev.EntityUid)) + return; + + EntityManager.EnsureComponent(ev.EntityUid); + } + + private void OnShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args) + { + if (!EntityManager.HasComponent(uid)) { - base.Update(frameTime); - UpdateHyperspace(frameTime); + return; } - private void OnRoundRestart(RoundRestartCleanupEvent ev) + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) { - CleanupHyperspace(); + return; } - private void OnShuttleAdd(EntityUid uid, ShuttleComponent component, ComponentAdd args) + if (component.Enabled) { - // Easier than doing it in the comp and they don't have constructors. - for (var i = 0; i < component.LinearThrusters.Length; i++) - { - component.LinearThrusters[i] = new List(); - } + Enable(uid, physicsComponent); } + } - private void OnGridFixtureChange(GridFixtureChangeEvent args) + public void Toggle(EntityUid uid, ShuttleComponent component) + { + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + return; + + component.Enabled = !component.Enabled; + + if (component.Enabled) { - // Look this is jank but it's a placeholder until we design it. - if (args.NewFixtures.Count == 0) - return; - - var uid = args.NewFixtures[0].Body.Owner; - var manager = Comp(uid); - - foreach (var fixture in args.NewFixtures) - { - _physics.SetDensity(uid, fixture, TileMassMultiplier, false, manager); - _fixtures.SetRestitution(uid, fixture, 0.1f, false, manager); - } - - _fixtures.FixtureUpdate(uid, manager: manager); + Enable(uid, physicsComponent); } - - private void OnGridInit(GridInitializeEvent ev) + else { - if (HasComp(ev.EntityUid)) - return; - - EntityManager.EnsureComponent(ev.EntityUid); - } - - private void OnShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args) - { - if (!EntityManager.HasComponent(uid)) - { - return; - } - - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) - { - return; - } - - if (component.Enabled) - { - Enable(uid, physicsComponent); - } - } - - public void Toggle(EntityUid uid, ShuttleComponent component) - { - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) - return; - - component.Enabled = !component.Enabled; - - if (component.Enabled) - { - Enable(uid, physicsComponent); - } - else - { - Disable(uid, physicsComponent); - } - } - - private void Enable(EntityUid uid, PhysicsComponent component) - { - FixturesComponent? manager = null; - - _physics.SetBodyType(uid, BodyType.Dynamic, manager: manager, body: component); - _physics.SetBodyStatus(component, BodyStatus.InAir); - _physics.SetFixedRotation(uid, false, manager: manager, body: component); - _physics.SetLinearDamping(component, ShuttleLinearDamping); - _physics.SetAngularDamping(component, ShuttleAngularDamping); - } - - private void Disable(EntityUid uid, PhysicsComponent component) - { - FixturesComponent? manager = null; - - _physics.SetBodyType(uid, BodyType.Static, manager: manager, body: component); - _physics.SetBodyStatus(component, BodyStatus.OnGround); - _physics.SetFixedRotation(uid, true, manager: manager, body: component); - } - - private void OnShuttleShutdown(EntityUid uid, ShuttleComponent component, ComponentShutdown args) - { - // None of the below is necessary for any cleanup if we're just deleting. - if (EntityManager.GetComponent(uid).EntityLifeStage >= EntityLifeStage.Terminating) return; - - if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) - { - return; - } - Disable(uid, physicsComponent); } } + + private void Enable(EntityUid uid, PhysicsComponent component) + { + FixturesComponent? manager = null; + + _physics.SetBodyType(uid, BodyType.Dynamic, manager: manager, body: component); + _physics.SetBodyStatus(component, BodyStatus.InAir); + _physics.SetFixedRotation(uid, false, manager: manager, body: component); + _physics.SetLinearDamping(component, ShuttleLinearDamping); + _physics.SetAngularDamping(component, ShuttleAngularDamping); + } + + private void Disable(EntityUid uid, PhysicsComponent component) + { + FixturesComponent? manager = null; + + _physics.SetBodyType(uid, BodyType.Static, manager: manager, body: component); + _physics.SetBodyStatus(component, BodyStatus.OnGround); + _physics.SetFixedRotation(uid, true, manager: manager, body: component); + } + + private void OnShuttleShutdown(EntityUid uid, ShuttleComponent component, ComponentShutdown args) + { + // None of the below is necessary for any cleanup if we're just deleting. + if (EntityManager.GetComponent(uid).EntityLifeStage >= EntityLifeStage.Terminating) + return; + + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + { + return; + } + + Disable(uid, physicsComponent); + } } diff --git a/Content.Server/Shuttles/Systems/ThrusterSystem.cs b/Content.Server/Shuttles/Systems/ThrusterSystem.cs index 287622aed9..7903392296 100644 --- a/Content.Server/Shuttles/Systems/ThrusterSystem.cs +++ b/Content.Server/Shuttles/Systems/ThrusterSystem.cs @@ -17,496 +17,527 @@ using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; +using Robust.Shared.Timing; using Robust.Shared.Utility; -namespace Content.Server.Shuttles.Systems +namespace Content.Server.Shuttles.Systems; + +public sealed class ThrusterSystem : EntitySystem { - public sealed class ThrusterSystem : EntitySystem + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly AmbientSoundSystem _ambient = default!; + [Dependency] private readonly FixtureSystem _fixtureSystem = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + // Essentially whenever thruster enables we update the shuttle's available impulses which are used for movement. + // This is done for each direction available. + + public const string BurnFixture = "thruster-burn"; + + public override void Initialize() { - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; - [Dependency] private readonly AmbientSoundSystem _ambient = default!; - [Dependency] private readonly FixtureSystem _fixtureSystem = default!; - [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + base.Initialize(); + SubscribeLocalEvent(OnActivateThruster); + SubscribeLocalEvent(OnThrusterInit); + SubscribeLocalEvent(OnThrusterShutdown); + SubscribeLocalEvent(OnThrusterMapInit); + SubscribeLocalEvent(OnPowerChange); + SubscribeLocalEvent(OnAnchorChange); + SubscribeLocalEvent(OnThrusterReAnchor); + SubscribeLocalEvent(OnRotate); + SubscribeLocalEvent(OnIsHotEvent); + SubscribeLocalEvent(OnStartCollide); + SubscribeLocalEvent(OnEndCollide); - // Essentially whenever thruster enables we update the shuttle's available impulses which are used for movement. - // This is done for each direction available. + SubscribeLocalEvent(OnThrusterExamine); - public const string BurnFixture = "thruster-burn"; + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnUpgradeExamine); - private readonly HashSet _activeThrusters = new(); + SubscribeLocalEvent(OnShuttleTileChange); + } - // Used for accumulating burn if someone touches a firing thruster. + private void OnThrusterExamine(EntityUid uid, ThrusterComponent component, ExaminedEvent args) + { + // Powered is already handled by other power components + var enabled = Loc.GetString(component.Enabled ? "thruster-comp-enabled" : "thruster-comp-disabled"); - private float _accumulator; + args.PushMarkup(enabled); - public override void Initialize() + if (component.Type == ThrusterType.Linear && + EntityManager.TryGetComponent(uid, out TransformComponent? xform) && + xform.Anchored) { - base.Initialize(); - SubscribeLocalEvent(OnActivateThruster); - SubscribeLocalEvent(OnThrusterInit); - SubscribeLocalEvent(OnThrusterShutdown); - SubscribeLocalEvent(OnPowerChange); - SubscribeLocalEvent(OnAnchorChange); - SubscribeLocalEvent(OnThrusterReAnchor); - SubscribeLocalEvent(OnRotate); - SubscribeLocalEvent(OnIsHotEvent); - SubscribeLocalEvent(OnStartCollide); - SubscribeLocalEvent(OnEndCollide); + var nozzleDir = Loc.GetString("thruster-comp-nozzle-direction", + ("direction", xform.LocalRotation.Opposite().ToWorldVec().GetDir().ToString().ToLowerInvariant())); - SubscribeLocalEvent(OnThrusterExamine); + args.PushMarkup(nozzleDir); - SubscribeLocalEvent(OnRefreshParts); - SubscribeLocalEvent(OnUpgradeExamine); + var exposed = NozzleExposed(xform); - SubscribeLocalEvent(OnShuttleTileChange); + var nozzleText = Loc.GetString(exposed ? "thruster-comp-nozzle-exposed" : "thruster-comp-nozzle-not-exposed"); + + args.PushMarkup(nozzleText); } + } - private void OnThrusterExamine(EntityUid uid, ThrusterComponent component, ExaminedEvent args) + private void OnIsHotEvent(EntityUid uid, ThrusterComponent component, IsHotEvent args) + { + args.IsHot = component.Type != ThrusterType.Angular && component.IsOn; + } + + private void OnShuttleTileChange(EntityUid uid, ShuttleComponent component, ref TileChangedEvent args) + { + // If the old tile was space but the new one isn't then disable all adjacent thrusters + if (args.NewTile.IsSpace(_tileDefManager) || !args.OldTile.IsSpace(_tileDefManager)) + return; + + var tilePos = args.NewTile.GridIndices; + var grid = _mapManager.GetGrid(uid); + var xformQuery = GetEntityQuery(); + var thrusterQuery = GetEntityQuery(); + + for (var x = -1; x <= 1; x++) { - // Powered is already handled by other power components - var enabled = Loc.GetString(component.Enabled ? "thruster-comp-enabled" : "thruster-comp-disabled"); - - args.PushMarkup(enabled); - - if (component.Type == ThrusterType.Linear && - EntityManager.TryGetComponent(uid, out TransformComponent? xform) && - xform.Anchored) + for (var y = -1; y <= 1; y++) { - var nozzleDir = Loc.GetString("thruster-comp-nozzle-direction", - ("direction", xform.LocalRotation.Opposite().ToWorldVec().GetDir().ToString().ToLowerInvariant())); + if (x != 0 && y != 0) + continue; - args.PushMarkup(nozzleDir); + var checkPos = tilePos + new Vector2i(x, y); + var enumerator = grid.GetAnchoredEntitiesEnumerator(checkPos); - var exposed = NozzleExposed(xform); - - var nozzleText = Loc.GetString(exposed ? "thruster-comp-nozzle-exposed" : "thruster-comp-nozzle-not-exposed"); - - args.PushMarkup(nozzleText); - } - } - - private void OnIsHotEvent(EntityUid uid, ThrusterComponent component, IsHotEvent args) - { - args.IsHot = component.Type != ThrusterType.Angular && component.IsOn; - } - - private void OnShuttleTileChange(EntityUid uid, ShuttleComponent component, ref TileChangedEvent args) - { - // If the old tile was space but the new one isn't then disable all adjacent thrusters - if (args.NewTile.IsSpace(_tileDefManager) || !args.OldTile.IsSpace(_tileDefManager)) return; - - var tilePos = args.NewTile.GridIndices; - var grid = _mapManager.GetGrid(uid); - var xformQuery = GetEntityQuery(); - var thrusterQuery = GetEntityQuery(); - - for (var x = -1; x <= 1; x++) - { - for (var y = -1; y <= 1; y++) + while (enumerator.MoveNext(out var ent)) { - if (x != 0 && y != 0) continue; + if (!thrusterQuery.TryGetComponent(ent.Value, out var thruster) || !thruster.RequireSpace) + continue; - var checkPos = tilePos + new Vector2i(x, y); - var enumerator = grid.GetAnchoredEntitiesEnumerator(checkPos); + // Work out if the thruster is facing this direction + var xform = xformQuery.GetComponent(ent.Value); + var direction = xform.LocalRotation.ToWorldVec(); - while (enumerator.MoveNext(out var ent)) - { - if (!thrusterQuery.TryGetComponent(ent.Value, out var thruster) || !thruster.RequireSpace) continue; + if (new Vector2i((int) direction.X, (int) direction.Y) != new Vector2i(x, y)) + continue; - // Work out if the thruster is facing this direction - var xform = xformQuery.GetComponent(ent.Value); - var direction = xform.LocalRotation.ToWorldVec(); - - if (new Vector2i((int) direction.X, (int) direction.Y) != new Vector2i(x, y)) continue; - - DisableThruster(ent.Value, thruster, xform.GridUid); - } + DisableThruster(ent.Value, thruster, xform.GridUid); } } } + } - private void OnActivateThruster(EntityUid uid, ThrusterComponent component, ActivateInWorldEvent args) + private void OnActivateThruster(EntityUid uid, ThrusterComponent component, ActivateInWorldEvent args) + { + component.Enabled ^= true; + } + + /// + /// If the thruster rotates change the direction where the linear thrust is applied + /// + private void OnRotate(EntityUid uid, ThrusterComponent component, ref MoveEvent args) + { + // TODO: Disable visualizer for old direction + + if (!component.Enabled || + component.Type != ThrusterType.Linear || + !EntityManager.TryGetComponent(uid, out TransformComponent? xform) || + !EntityManager.TryGetComponent(xform.GridUid, out ShuttleComponent? shuttleComponent)) { - component.Enabled ^= true; + return; } - /// - /// If the thruster rotates change the direction where the linear thrust is applied - /// - private void OnRotate(EntityUid uid, ThrusterComponent component, ref MoveEvent args) + var canEnable = CanEnable(uid, component); + + // If it's not on then don't enable it inadvertantly (given we don't have an old rotation) + if (!canEnable && !component.IsOn) + return; + + // Enable it if it was turned off but new tile is valid + if (!component.IsOn && canEnable) { - // TODO: Disable visualizer for old direction - - if (!component.Enabled || - component.Type != ThrusterType.Linear || - !EntityManager.TryGetComponent(uid, out TransformComponent? xform) || - !_mapManager.TryGetGrid(xform.GridUid, out var grid) || - !EntityManager.TryGetComponent(grid.Owner, out ShuttleComponent? shuttleComponent)) - { - return; - } - - var canEnable = CanEnable(uid, component); - - // If it's not on then don't enable it inadvertantly (given we don't have an old rotation) - if (!canEnable && !component.IsOn) return; - - // Enable it if it was turned off but new tile is valid - if (!component.IsOn && canEnable) - { - EnableThruster(uid, component); - return; - } - - // Disable if new tile invalid - if (component.IsOn && !canEnable) - { - DisableThruster(uid, component, xform, args.OldRotation); - return; - } - - var oldDirection = (int) args.OldRotation.GetCardinalDir() / 2; - var direction = (int) args.NewRotation.GetCardinalDir() / 2; - - shuttleComponent.LinearThrust[oldDirection] -= component.Thrust; - DebugTools.Assert(shuttleComponent.LinearThrusters[oldDirection].Contains(component)); - shuttleComponent.LinearThrusters[oldDirection].Remove(component); - - shuttleComponent.LinearThrust[direction] += component.Thrust; - DebugTools.Assert(!shuttleComponent.LinearThrusters[direction].Contains(component)); - shuttleComponent.LinearThrusters[direction].Add(component); + EnableThruster(uid, component); + return; } - private void OnAnchorChange(EntityUid uid, ThrusterComponent component, ref AnchorStateChangedEvent args) + // Disable if new tile invalid + if (component.IsOn && !canEnable) { - if (args.Anchored && CanEnable(uid, component)) - { - EnableThruster(uid, component); - } - else - { - DisableThruster(uid, component); - } + DisableThruster(uid, component, xform, args.OldRotation); + return; } - private void OnThrusterReAnchor(EntityUid uid, ThrusterComponent component, ref ReAnchorEvent args) + var oldDirection = (int) args.OldRotation.GetCardinalDir() / 2; + var direction = (int) args.NewRotation.GetCardinalDir() / 2; + + shuttleComponent.LinearThrust[oldDirection] -= component.Thrust; + DebugTools.Assert(shuttleComponent.LinearThrusters[oldDirection].Contains(uid)); + shuttleComponent.LinearThrusters[oldDirection].Remove(uid); + + shuttleComponent.LinearThrust[direction] += component.Thrust; + DebugTools.Assert(!shuttleComponent.LinearThrusters[direction].Contains(uid)); + shuttleComponent.LinearThrusters[direction].Add(uid); + } + + private void OnAnchorChange(EntityUid uid, ThrusterComponent component, ref AnchorStateChangedEvent args) + { + if (args.Anchored && CanEnable(uid, component)) { - DisableThruster(uid, component, args.OldGrid); - - if (CanEnable(uid, component)) - EnableThruster(uid, component); + EnableThruster(uid, component); } - - private void OnThrusterInit(EntityUid uid, ThrusterComponent component, ComponentInit args) - { - _ambient.SetAmbience(uid, false); - - if (!component.Enabled) - { - return; - } - - if (CanEnable(uid, component)) - { - EnableThruster(uid, component); - } - } - - private void OnThrusterShutdown(EntityUid uid, ThrusterComponent component, ComponentShutdown args) + else { DisableThruster(uid, component); } + } - private void OnPowerChange(EntityUid uid, ThrusterComponent component, ref PowerChangedEvent args) + private void OnThrusterReAnchor(EntityUid uid, ThrusterComponent component, ref ReAnchorEvent args) + { + DisableThruster(uid, component, args.OldGrid); + + if (CanEnable(uid, component)) + EnableThruster(uid, component); + } + + private void OnThrusterMapInit(EntityUid uid, ThrusterComponent component, MapInitEvent args) + { + if (component.NextFire < _timing.CurTime) + component.NextFire = _timing.CurTime; + } + + private void OnThrusterInit(EntityUid uid, ThrusterComponent component, ComponentInit args) + { + _ambient.SetAmbience(uid, false); + + if (!component.Enabled) { - if (args.Powered && CanEnable(uid, component)) - { - EnableThruster(uid, component); - } - else - { - DisableThruster(uid, component); - } + return; } - /// - /// Tries to enable the thruster and turn it on. If it's already enabled it does nothing. - /// - public void EnableThruster(EntityUid uid, ThrusterComponent component, TransformComponent? xform = null) + if (CanEnable(uid, component)) { - if (component.IsOn || - !Resolve(uid, ref xform) || - !_mapManager.TryGetGrid(xform.GridUid, out var grid)) return; - - component.IsOn = true; - - if (!EntityManager.TryGetComponent(grid.Owner, out ShuttleComponent? shuttleComponent)) return; - - // Logger.DebugS("thruster", $"Enabled thruster {uid}"); - - switch (component.Type) - { - case ThrusterType.Linear: - var direction = (int) xform.LocalRotation.GetCardinalDir() / 2; - - shuttleComponent.LinearThrust[direction] += component.Thrust; - DebugTools.Assert(!shuttleComponent.LinearThrusters[direction].Contains(component)); - shuttleComponent.LinearThrusters[direction].Add(component); - - // Don't just add / remove the fixture whenever the thruster fires because perf - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent) && - component.BurnPoly.Count > 0) - { - var shape = new PolygonShape(); - shape.SetVertices(component.BurnPoly); - _fixtureSystem.TryCreateFixture(uid, shape, BurnFixture, hard: false, collisionLayer: (int) CollisionGroup.FullTileMask); - } - - break; - case ThrusterType.Angular: - shuttleComponent.AngularThrust += component.Thrust; - DebugTools.Assert(!shuttleComponent.AngularThrusters.Contains(component)); - shuttleComponent.AngularThrusters.Add(component); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) - { - _appearance.SetData(uid, ThrusterVisualState.State, true, appearance); - } - - if (EntityManager.TryGetComponent(uid, out PointLightComponent? pointLightComponent)) - { - pointLightComponent.Enabled = true; - } - - _ambient.SetAmbience(uid, true); - } - - public void DisableThruster(EntityUid uid, ThrusterComponent component, TransformComponent? xform = null, Angle? angle = null) - { - if (!Resolve(uid, ref xform)) return; - DisableThruster(uid, component, xform.GridUid, xform); - } - - /// - /// Tries to disable the thruster. - /// - public void DisableThruster(EntityUid uid, ThrusterComponent component, EntityUid? gridId, TransformComponent? xform = null, Angle? angle = null) - { - if (!component.IsOn || - !Resolve(uid, ref xform) || - !_mapManager.TryGetGrid(gridId, out var grid)) return; - - component.IsOn = false; - - if (!EntityManager.TryGetComponent(grid.Owner, out ShuttleComponent? shuttleComponent)) return; - - // Logger.DebugS("thruster", $"Disabled thruster {uid}"); - - switch (component.Type) - { - case ThrusterType.Linear: - angle ??= xform.LocalRotation; - var direction = (int) angle.Value.GetCardinalDir() / 2; - - shuttleComponent.LinearThrust[direction] -= component.Thrust; - DebugTools.Assert(shuttleComponent.LinearThrusters[direction].Contains(component)); - shuttleComponent.LinearThrusters[direction].Remove(component); - break; - case ThrusterType.Angular: - shuttleComponent.AngularThrust -= component.Thrust; - DebugTools.Assert(shuttleComponent.AngularThrusters.Contains(component)); - shuttleComponent.AngularThrusters.Remove(component); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) - { - _appearance.SetData(uid, ThrusterVisualState.State, false, appearance); - } - - if (EntityManager.TryGetComponent(uid, out PointLightComponent? pointLightComponent)) - { - pointLightComponent.Enabled = false; - } - - _ambient.SetAmbience(uid, false); - - if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) - { - _fixtureSystem.DestroyFixture(uid, BurnFixture, body: physicsComponent); - } - - _activeThrusters.Remove(component); - component.Colliding.Clear(); - } - - public bool CanEnable(EntityUid uid, ThrusterComponent component) - { - if (!component.Enabled) return false; - if (component.LifeStage > ComponentLifeStage.Running) return false; - - var xform = Transform(uid); - - if (!xform.Anchored ||!this.IsPowered(uid, EntityManager)) - { - return false; - } - - if (!component.RequireSpace) - return true; - - return NozzleExposed(xform); - } - - private bool NozzleExposed(TransformComponent xform) - { - if (xform.GridUid == null) - return true; - - var (x, y) = xform.LocalPosition + xform.LocalRotation.Opposite().ToWorldVec(); - var tile = _mapManager.GetGrid(xform.GridUid.Value).GetTileRef(new Vector2i((int) Math.Floor(x), (int) Math.Floor(y))); - - return tile.Tile.IsSpace(); - } - - #region Burning - - public override void Update(float frameTime) - { - base.Update(frameTime); - - _accumulator += frameTime; - - if (_accumulator < 1) return; - - _accumulator -= 1; - - foreach (var comp in _activeThrusters.ToArray()) - { - MetaDataComponent? metaData = null; - - if (!comp.Firing || comp.Damage == null || Paused(comp.Owner, metaData) || Deleted(comp.Owner, metaData)) continue; - - DebugTools.Assert(comp.Colliding.Count > 0); - - foreach (var uid in comp.Colliding.ToArray()) - { - _damageable.TryChangeDamage(uid, comp.Damage); - } - } - } - - private void OnStartCollide(EntityUid uid, ThrusterComponent component, ref StartCollideEvent args) - { - if (args.OurFixture.ID != BurnFixture) return; - - _activeThrusters.Add(component); - component.Colliding.Add((args.OtherFixture.Body).Owner); - } - - private void OnEndCollide(EntityUid uid, ThrusterComponent component, ref EndCollideEvent args) - { - if (args.OurFixture.ID != BurnFixture) return; - - component.Colliding.Remove((args.OtherFixture.Body).Owner); - - if (component.Colliding.Count == 0) - { - _activeThrusters.Remove(component); - } - } - - /// - /// Considers a thrust direction as being active. - /// - public void EnableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) - { - if ((component.ThrustDirections & direction) != 0x0) return; - - component.ThrustDirections |= direction; - - var index = GetFlagIndex(direction); - - foreach (var comp in component.LinearThrusters[index]) - { - comp.Firing = true; - _appearance.SetData(comp.Owner, ThrusterVisualState.Thrusting, true); - } - } - - /// - /// Disables a thrust direction. - /// - public void DisableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) - { - if ((component.ThrustDirections & direction) == 0x0) return; - - component.ThrustDirections &= ~direction; - - var index = GetFlagIndex(direction); - - foreach (var comp in component.LinearThrusters[index]) - { - comp.Firing = false; - _appearance.SetData(comp.Owner, ThrusterVisualState.Thrusting, false); - } - } - - public void DisableLinearThrusters(ShuttleComponent component) - { - foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) - { - DisableLinearThrustDirection(component, dir); - } - - DebugTools.Assert(component.ThrustDirections == DirectionFlag.None); - } - - public void SetAngularThrust(ShuttleComponent component, bool on) - { - if (on) - { - foreach (var comp in component.AngularThrusters) - { - comp.Firing = true; - _appearance.SetData(comp.Owner, ThrusterVisualState.Thrusting, true); - } - } - else - { - foreach (var comp in component.AngularThrusters) - { - comp.Firing = false; - _appearance.SetData(comp.Owner, ThrusterVisualState.Thrusting, false); - } - } - } - - private void OnRefreshParts(EntityUid uid, ThrusterComponent component, RefreshPartsEvent args) - { - var thrustRating = args.PartRatings[component.MachinePartThrust]; - - component.Thrust = component.BaseThrust * MathF.Pow(component.PartRatingThrustMultiplier, thrustRating - 1); - } - - private void OnUpgradeExamine(EntityUid uid, ThrusterComponent component, UpgradeExamineEvent args) - { - args.AddPercentageUpgrade("thruster-comp-upgrade-thrust", component.Thrust / component.BaseThrust); - } - - #endregion - - private int GetFlagIndex(DirectionFlag flag) - { - return (int) Math.Log2((int) flag); + EnableThruster(uid, component); } } + + private void OnThrusterShutdown(EntityUid uid, ThrusterComponent component, ComponentShutdown args) + { + DisableThruster(uid, component); + } + + private void OnPowerChange(EntityUid uid, ThrusterComponent component, ref PowerChangedEvent args) + { + if (args.Powered && CanEnable(uid, component)) + { + EnableThruster(uid, component); + } + else + { + DisableThruster(uid, component); + } + } + + /// + /// Tries to enable the thruster and turn it on. If it's already enabled it does nothing. + /// + public void EnableThruster(EntityUid uid, ThrusterComponent component, TransformComponent? xform = null) + { + if (component.IsOn || + !Resolve(uid, ref xform)) + { + return; + } + + component.IsOn = true; + + if (!EntityManager.TryGetComponent(xform.GridUid, out ShuttleComponent? shuttleComponent)) + return; + + // Logger.DebugS("thruster", $"Enabled thruster {uid}"); + + switch (component.Type) + { + case ThrusterType.Linear: + var direction = (int) xform.LocalRotation.GetCardinalDir() / 2; + + shuttleComponent.LinearThrust[direction] += component.Thrust; + DebugTools.Assert(!shuttleComponent.LinearThrusters[direction].Contains(uid)); + shuttleComponent.LinearThrusters[direction].Add(uid); + + // Don't just add / remove the fixture whenever the thruster fires because perf + if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent) && + component.BurnPoly.Count > 0) + { + var shape = new PolygonShape(); + shape.SetVertices(component.BurnPoly); + _fixtureSystem.TryCreateFixture(uid, shape, BurnFixture, hard: false, collisionLayer: (int) CollisionGroup.FullTileMask, body: physicsComponent); + } + + break; + case ThrusterType.Angular: + shuttleComponent.AngularThrust += component.Thrust; + DebugTools.Assert(!shuttleComponent.AngularThrusters.Contains(uid)); + shuttleComponent.AngularThrusters.Add(uid); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + { + _appearance.SetData(uid, ThrusterVisualState.State, true, appearance); + } + + if (EntityManager.TryGetComponent(uid, out PointLightComponent? pointLightComponent)) + { + pointLightComponent.Enabled = true; + } + + _ambient.SetAmbience(uid, true); + } + + public void DisableThruster(EntityUid uid, ThrusterComponent component, TransformComponent? xform = null, Angle? angle = null) + { + if (!Resolve(uid, ref xform)) return; + DisableThruster(uid, component, xform.GridUid, xform); + } + + /// + /// Tries to disable the thruster. + /// + public void DisableThruster(EntityUid uid, ThrusterComponent component, EntityUid? gridId, TransformComponent? xform = null, Angle? angle = null) + { + if (!component.IsOn || + !Resolve(uid, ref xform)) + { + return; + } + + component.IsOn = false; + + if (!EntityManager.TryGetComponent(gridId, out ShuttleComponent? shuttleComponent)) + return; + + // Logger.DebugS("thruster", $"Disabled thruster {uid}"); + + switch (component.Type) + { + case ThrusterType.Linear: + angle ??= xform.LocalRotation; + var direction = (int) angle.Value.GetCardinalDir() / 2; + + shuttleComponent.LinearThrust[direction] -= component.Thrust; + DebugTools.Assert(shuttleComponent.LinearThrusters[direction].Contains(uid)); + shuttleComponent.LinearThrusters[direction].Remove(uid); + break; + case ThrusterType.Angular: + shuttleComponent.AngularThrust -= component.Thrust; + DebugTools.Assert(shuttleComponent.AngularThrusters.Contains(uid)); + shuttleComponent.AngularThrusters.Remove(uid); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearance)) + { + _appearance.SetData(uid, ThrusterVisualState.State, false, appearance); + } + + if (EntityManager.TryGetComponent(uid, out PointLightComponent? pointLightComponent)) + { + pointLightComponent.Enabled = false; + } + + _ambient.SetAmbience(uid, false); + + if (EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + { + _fixtureSystem.DestroyFixture(uid, BurnFixture, body: physicsComponent); + } + + component.Colliding.Clear(); + } + + public bool CanEnable(EntityUid uid, ThrusterComponent component) + { + if (!component.Enabled) + return false; + + if (component.LifeStage > ComponentLifeStage.Running) + return false; + + var xform = Transform(uid); + + if (!xform.Anchored ||!this.IsPowered(uid, EntityManager)) + { + return false; + } + + if (!component.RequireSpace) + return true; + + return NozzleExposed(xform); + } + + private bool NozzleExposed(TransformComponent xform) + { + if (xform.GridUid == null) + return true; + + var (x, y) = xform.LocalPosition + xform.LocalRotation.Opposite().ToWorldVec(); + var tile = _mapManager.GetGrid(xform.GridUid.Value).GetTileRef(new Vector2i((int) Math.Floor(x), (int) Math.Floor(y))); + + return tile.Tile.IsSpace(); + } + + #region Burning + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var curTime = _timing.CurTime; + + while (query.MoveNext(out var comp)) + { + if (!comp.Firing || comp.Colliding.Count == 0 || comp.Damage == null || comp.NextFire < curTime) + continue; + + comp.NextFire += TimeSpan.FromSeconds(1); + + foreach (var uid in comp.Colliding.ToArray()) + { + _damageable.TryChangeDamage(uid, comp.Damage); + } + } + } + + private void OnStartCollide(EntityUid uid, ThrusterComponent component, ref StartCollideEvent args) + { + if (args.OurFixture.ID != BurnFixture) + return; + + component.Colliding.Add(args.OtherEntity); + } + + private void OnEndCollide(EntityUid uid, ThrusterComponent component, ref EndCollideEvent args) + { + if (args.OurFixture.ID != BurnFixture) + return; + + component.Colliding.Remove(args.OtherEntity); + } + + /// + /// Considers a thrust direction as being active. + /// + public void EnableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) + { + if ((component.ThrustDirections & direction) != 0x0) + return; + + component.ThrustDirections |= direction; + + var index = GetFlagIndex(direction); + var appearanceQuery = GetEntityQuery(); + var thrusterQuery = GetEntityQuery(); + + foreach (var uid in component.LinearThrusters[index]) + { + if (!thrusterQuery.TryGetComponent(uid, out var comp)) + continue; + + comp.Firing = true; + appearanceQuery.TryGetComponent(uid, out var appearance); + _appearance.SetData(uid, ThrusterVisualState.Thrusting, true, appearance); + } + } + + /// + /// Disables a thrust direction. + /// + public void DisableLinearThrustDirection(ShuttleComponent component, DirectionFlag direction) + { + if ((component.ThrustDirections & direction) == 0x0) + return; + + component.ThrustDirections &= ~direction; + + var index = GetFlagIndex(direction); + var appearanceQuery = GetEntityQuery(); + var thrusterQuery = GetEntityQuery(); + + foreach (var uid in component.LinearThrusters[index]) + { + if (!thrusterQuery.TryGetComponent(uid, out var comp)) + continue; + + appearanceQuery.TryGetComponent(uid, out var appearance); + comp.Firing = false; + _appearance.SetData(uid, ThrusterVisualState.Thrusting, false, appearance); + } + } + + public void DisableLinearThrusters(ShuttleComponent component) + { + foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag))) + { + DisableLinearThrustDirection(component, dir); + } + + DebugTools.Assert(component.ThrustDirections == DirectionFlag.None); + } + + public void SetAngularThrust(ShuttleComponent component, bool on) + { + var appearanceQuery = GetEntityQuery(); + var thrusterQuery = GetEntityQuery(); + + if (on) + { + foreach (var uid in component.AngularThrusters) + { + if (!thrusterQuery.TryGetComponent(uid, out var comp)) + continue; + + appearanceQuery.TryGetComponent(uid, out var appearance); + comp.Firing = true; + _appearance.SetData(uid, ThrusterVisualState.Thrusting, true, appearance); + } + } + else + { + foreach (var uid in component.AngularThrusters) + { + if (!thrusterQuery.TryGetComponent(uid, out var comp)) + continue; + + appearanceQuery.TryGetComponent(uid, out var appearance); + comp.Firing = false; + _appearance.SetData(uid, ThrusterVisualState.Thrusting, false, appearance); + } + } + } + + private void OnRefreshParts(EntityUid uid, ThrusterComponent component, RefreshPartsEvent args) + { + var thrustRating = args.PartRatings[component.MachinePartThrust]; + + component.Thrust = component.BaseThrust * MathF.Pow(component.PartRatingThrustMultiplier, thrustRating - 1); + } + + private void OnUpgradeExamine(EntityUid uid, ThrusterComponent component, UpgradeExamineEvent args) + { + args.AddPercentageUpgrade("thruster-comp-upgrade-thrust", component.Thrust / component.BaseThrust); + } + + #endregion + + private int GetFlagIndex(DirectionFlag flag) + { + return (int) Math.Log2((int) flag); + } }