예제 #1
0
    /// <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);
    }
예제 #2
0
        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;
            }
        }
예제 #3
0
    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);
    }
예제 #4
0
        /// <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();
            }
        }
예제 #5
0
 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>();
     }
 }
예제 #6
0
        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;
            }
        }
예제 #7
0
        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);
            }
        }
예제 #8
0
    /// <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();
    }
예제 #9
0
        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);
            }
        }
예제 #10
0
    /// <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();
    }
예제 #11
0
        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);
            }
        }
예제 #12
0
    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);
    }
예제 #13
0
        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();
            }
        }
예제 #14
0
    /// <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);
    }