/// <summary> /// Tries to dock with the target grid, otherwise falls back to proximity. /// </summary> public bool TryFTLDock(ShuttleComponent component, EntityUid targetUid) { if (!TryComp <TransformComponent>(component.Owner, out var xform) || !TryComp <TransformComponent>(targetUid, out var targetXform) || targetXform.MapUid == null) { return(false); } var config = GetDockingConfig(component, targetUid); if (config != null) { // Set position xform.Coordinates = config.Coordinates; xform.WorldRotation = config.Angle; // Connect everything foreach (var(dockA, dockB) in config.Docks) { _dockSystem.Dock(dockA, dockB); } return(true); } TryFTLProximity(component, targetUid, xform, targetXform); return(false); }
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 <MetaDataComponent>(uid).EntityLifeStage >= EntityLifeStage.Terminating) { return; } if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physicsComponent)) { return; } Disable(physicsComponent); if (!EntityManager.TryGetComponent(component.Owner, out FixturesComponent? fixturesComponent)) { return; } foreach (var fixture in fixturesComponent.Fixtures.Values) { fixture.Mass = 0f; } }
private bool TrySetupFTL(ShuttleComponent shuttle, [NotNullWhen(true)] out FTLComponent?component) { var uid = shuttle.Owner; component = null; if (HasComp <FTLComponent>(uid)) { _sawmill.Warning($"Tried queuing {ToPrettyString(uid)} which already has HyperspaceComponent?"); return(false); } if (TryComp <FTLDestinationComponent>(uid, out var dest)) { dest.Enabled = false; } _thruster.DisableLinearThrusters(shuttle); _thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North); _thruster.SetAngularThrust(shuttle, false); // TODO: Maybe move this to docking instead? SetDocks(uid, false); component = AddComp <FTLComponent>(uid); // TODO: Need BroadcastGrid to not be bad. SoundSystem.Play(_startupSound.GetSound(), Filter.Empty().AddInRange(Transform(uid).MapPosition, GetSoundRange(component.Owner)), _startupSound.Params); // Make sure the map is setup before we leave to avoid pop-in (e.g. parallax). SetupHyperspace(); return(true); }
/// <summary> /// Sets the shuttle's movement mode. Does minimal revalidation. /// </summary> private void SetShuttleMode(ShuttleMode mode, ShuttleConsoleComponent consoleComponent, ShuttleComponent shuttleComponent, TransformComponent?consoleXform = null) { // Re-validate if (!this.IsPowered(consoleComponent.Owner, EntityManager) || !Resolve(consoleComponent.Owner, ref consoleXform) || !consoleXform.Anchored || consoleXform.GridID != Transform(shuttleComponent.Owner).GridID) { return; } shuttleComponent.Mode = mode; switch (mode) { case ShuttleMode.Strafing: break; case ShuttleMode.Cruise: break; default: throw new ArgumentOutOfRangeException(); } }
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++) { component.LinearThrusters[i] = new List <ThrusterComponent>(); } }
private void HandleShuttleShutdown(EntityUid uid, ShuttleComponent component, ComponentShutdown args) { if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) { return; } Disable(physicsComponent); foreach (var fixture in physicsComponent.Fixtures) { fixture.Mass = 0f; } }
private void OnShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args) { if (!EntityManager.HasComponent <IMapGridComponent>(component.Owner)) { return; } if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physicsComponent)) { return; } if (component.Enabled) { Enable(physicsComponent); } }
/// <summary> /// Moves a shuttle from its current position to the target one. Goes through the hyperspace map while the timer is running. /// </summary> public void FTLTravel(ShuttleComponent component, EntityCoordinates coordinates, float startupTime = DefaultStartupTime, float hyperspaceTime = DefaultTravelTime) { if (!TrySetupFTL(component, out var hyperspace)) { return; } hyperspace.StartupTime = startupTime; hyperspace.TravelTime = hyperspaceTime; hyperspace.Accumulator = hyperspace.StartupTime; hyperspace.TargetCoordinates = coordinates; hyperspace.Dock = false; _console.RefreshShuttleConsoles(); }
public void Toggle(ShuttleComponent component) { if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physicsComponent)) { return; } component.Enabled = !component.Enabled; if (component.Enabled) { Enable(physicsComponent); } else { Disable(physicsComponent); } }
/// <summary> /// Moves a shuttle from its current position to docked on the target one. Goes through the hyperspace map while the timer is running. /// </summary> public void FTLTravel(ShuttleComponent component, EntityUid target, float startupTime = DefaultStartupTime, float hyperspaceTime = DefaultTravelTime, bool dock = false) { if (!TrySetupFTL(component, out var hyperspace)) { return; } hyperspace.State = FTLState.Starting; hyperspace.StartupTime = startupTime; hyperspace.TravelTime = hyperspaceTime; hyperspace.Accumulator = hyperspace.StartupTime; hyperspace.TargetUid = target; hyperspace.Dock = dock; _console.RefreshShuttleConsoles(); }
private void HandleShuttleStartup(EntityUid uid, ShuttleComponent component, ComponentStartup args) { if (!component.Owner.HasComponent <IMapGridComponent>()) { return; } if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) { return; } if (component.Enabled) { Enable(physicsComponent); } if (component.Owner.TryGetComponent(out ShuttleComponent? shuttleComponent)) { RecalculateSpeedMultiplier(shuttleComponent, physicsComponent); } }
private DockingConfig?GetDockingConfig(ShuttleComponent component, EntityUid targetGrid) { var gridDocks = GetDocks(targetGrid); if (gridDocks.Count <= 0) { return(null); } var xformQuery = GetEntityQuery <TransformComponent>(); var targetGridGrid = Comp <IMapGridComponent>(targetGrid); var targetGridXform = xformQuery.GetComponent(targetGrid); var targetGridAngle = targetGridXform.WorldRotation.Reduced(); var targetGridRotation = targetGridAngle.ToVec(); var shuttleDocks = GetDocks(component.Owner); var shuttleAABB = Comp <IMapGridComponent>(component.Owner).Grid.LocalAABB; var validDockConfigs = new List <DockingConfig>(); if (shuttleDocks.Count > 0) { // We'll try all combinations of shuttle docks and see which one is most suitable foreach (var shuttleDock in shuttleDocks) { var shuttleDockXform = xformQuery.GetComponent(shuttleDock.Owner); foreach (var gridDock in gridDocks) { var gridXform = xformQuery.GetComponent(gridDock.Owner); if (!CanDock( shuttleDock, shuttleDockXform, gridDock, gridXform, targetGridRotation, shuttleAABB, targetGridGrid, out var dockedAABB, out var matty, out var targetAngle)) { continue; } // Can't just use the AABB as we want to get bounds as tight as possible. var spawnPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero)); spawnPosition = new EntityCoordinates(targetGridXform.MapUid !.Value, spawnPosition.ToMapPos(EntityManager)); var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetGridAngle, spawnPosition.Position); // Check if there's no intersecting grids (AKA oh god it's docking at cargo). if (_mapManager.FindGridsIntersecting(targetGridXform.MapID, dockedBounds).Any(o => o.GridEntityId != targetGrid)) { continue; } // Alright well the spawn is valid now to check how many we can connect // Get the matrix for each shuttle dock and test it against the grid docks to see // if the connected position / direction matches. var dockedPorts = new List <(DockingComponent DockA, DockingComponent DockB)>() { (shuttleDock, gridDock), }; // TODO: Check shuttle orientation as the tiebreaker. foreach (var other in shuttleDocks) { if (other == shuttleDock) { continue; } foreach (var otherGrid in gridDocks) { if (otherGrid == gridDock) { continue; } if (!CanDock( other, xformQuery.GetComponent(other.Owner), otherGrid, xformQuery.GetComponent(otherGrid.Owner), targetGridRotation, shuttleAABB, targetGridGrid, out var otherDockedAABB, out _, out var otherTargetAngle) || !otherDockedAABB.Equals(dockedAABB) || !targetAngle.Equals(otherTargetAngle)) { continue; } dockedPorts.Add((other, otherGrid)); } } var spawnRotation = shuttleDockXform.LocalRotation + gridXform.LocalRotation + targetGridXform.LocalRotation; validDockConfigs.Add(new DockingConfig() { Docks = dockedPorts, Area = dockedAABB.Value, Coordinates = spawnPosition, Angle = spawnRotation, }); } } } if (validDockConfigs.Count <= 0) { return(null); } // Prioritise by priority docks, then by maximum connected ports, then by most similar angle. validDockConfigs = validDockConfigs .OrderByDescending(x => x.Docks.Any(docks => HasComp <EmergencyDockComponent>(docks.DockB.Owner))) .ThenByDescending(x => x.Docks.Count) .ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList(); var location = validDockConfigs.First(); location.TargetGrid = targetGrid; // TODO: Ideally do a hyperspace warpin, just have it run on like a 10 second timer. return(location); }
private void ToggleShuttleMode(EntityUid user, ShuttleConsoleComponent consoleComponent, ShuttleComponent shuttleComponent, TransformComponent?consoleXform = null) { // Re-validate if (EntityManager.TryGetComponent(consoleComponent.Owner, out ApcPowerReceiverComponent? receiver) && !receiver.Powered) { return; } if (!Resolve(consoleComponent.Owner, ref consoleXform)) { return; } if (!consoleXform.Anchored || consoleXform.GridID != EntityManager.GetComponent <TransformComponent>(shuttleComponent.Owner).GridID) { return; } switch (shuttleComponent.Mode) { case ShuttleMode.Cruise: shuttleComponent.Mode = ShuttleMode.Docking; _popup.PopupEntity(Loc.GetString("shuttle-mode-docking"), consoleComponent.Owner, Filter.Entities(user)); break; case ShuttleMode.Docking: shuttleComponent.Mode = ShuttleMode.Cruise; _popup.PopupEntity(Loc.GetString("shuttle-mode-cruise"), consoleComponent.Owner, Filter.Entities(user)); break; default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Tries to arrive nearby without overlapping with other grids. /// </summary> public bool TryFTLProximity(ShuttleComponent component, EntityUid targetUid, TransformComponent?xform = null, TransformComponent?targetXform = null) { if (!Resolve(targetUid, ref targetXform) || targetXform.MapUid == null || !Resolve(component.Owner, ref xform)) { return(false); } var xformQuery = GetEntityQuery <TransformComponent>(); var shuttleAABB = Comp <IMapGridComponent>(component.Owner).Grid.LocalAABB; Box2?aabb = null; // Spawn nearby. // We essentially expand the Box2 of the target area until nothing else is added then we know it's valid. // Can't just get an AABB of every grid as we may spawn very far away. var targetAABB = _transform.GetWorldMatrix(targetXform, xformQuery) .TransformBox(Comp <IMapGridComponent>(targetUid).Grid.LocalAABB).Enlarged(shuttleAABB.Size.Length); var nearbyGrids = new HashSet <EntityUid>(1) { targetUid }; var iteration = 0; var lastCount = 1; var mapId = targetXform.MapID; while (iteration < 3) { foreach (var grid in _mapManager.FindGridsIntersecting(mapId, targetAABB)) { if (!nearbyGrids.Add(grid.GridEntityId)) { continue; } targetAABB = targetAABB.Union(_transform.GetWorldMatrix(grid.GridEntityId, xformQuery) .TransformBox(Comp <IMapGridComponent>(grid.GridEntityId).Grid.LocalAABB)); } // Can do proximity if (nearbyGrids.Count == lastCount) { break; } targetAABB = targetAABB.Enlarged(shuttleAABB.Size.Length / 2f); iteration++; lastCount = nearbyGrids.Count; // Mishap moment, dense asteroid field or whatever if (iteration != 3) { continue; } foreach (var grid in _mapManager.GetAllGrids()) { // Don't add anymore as it is irrelevant, but that doesn't mean we need to re-do existing work. if (nearbyGrids.Contains(grid.GridEntityId)) { continue; } targetAABB = targetAABB.Union(_transform.GetWorldMatrix(grid.GridEntityId, xformQuery) .TransformBox(Comp <IMapGridComponent>(grid.GridEntityId).Grid.LocalAABB)); } break; } var minRadius = (MathF.Max(targetAABB.Width, targetAABB.Height) + MathF.Max(shuttleAABB.Width, shuttleAABB.Height)) / 2f; var spawnPos = targetAABB.Center + _random.NextVector2(minRadius, minRadius + 64f); if (TryComp <PhysicsComponent>(component.Owner, out var shuttleBody)) { shuttleBody.LinearVelocity = Vector2.Zero; shuttleBody.AngularVelocity = 0f; } xform.Coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos); xform.WorldRotation = _random.NextAngle(); return(true); }