Example #1
0
        public static void SpawnCargoShip(bool checkGravity)
        {
            Init(  );
            Wrapper.GameAction(() =>
            {
                if (ExtenderOptions.IsDebugging)
                {
                    Essentials.Log.Info("Spawn cargo ship");
                }
                // Select a spawn group to spawn
                MySpawnGroupDefinition spawnGroup = PickRandomSpawnGroup( );
                if (spawnGroup == null)
                {
                    return;
                }

                spawnGroup.ReloadPrefabs( );

                double spawnDistance    = NEUTRAL_SHIP_SPAWN_DISTANCE;
                Vector3D playerPosition = Vector3D.Zero;
                bool isWorldLimited     = MyEntities.IsWorldLimited( );
                int numPlayers          = 0;

                if (isWorldLimited)
                {
                    spawnDistance = Math.Min(spawnDistance, MyEntities.WorldSafeHalfExtent( ) - spawnGroup.SpawnRadius);
                }
                else
                {
                    // In infinite worlds players can be thousands of kilometers away, so spawn ship around random player
                    // so cargo ships will be spawned around every player at some time
                    var players = MySession.Static.Players.GetOnlinePlayers( );
                    // In DS there can be no players connected
                    numPlayers = Math.Max(0, players.Count - 1);
                    int randomPlayerPosition = MyUtils.GetRandomInt(0, numPlayers);
                    int i = 0;
                    foreach (var player in players)
                    {
                        if (i == randomPlayerPosition)
                        {
                            if (player.Character != null)
                            {
                                playerPosition = player.GetPosition( );
                            }
                            break;
                        }
                        i++;
                    }
                }

                if (spawnDistance < 0.0f)
                {
                    if (ExtenderOptions.IsDebugging)
                    {
                        Essentials.Log.Info("Not enough space in the world to spawn such a huge spawn group!");
                    }
                    return;
                }

                double forbiddenRadius = NEUTRAL_SHIP_FORBIDDEN_RADIUS;
                BoundingBoxD spawnBox;
                if (isWorldLimited)
                {
                    spawnBox = new BoundingBoxD(new Vector3D(playerPosition - spawnDistance), new Vector3D(playerPosition + spawnDistance));
                }
                else
                {
                    // We need to extend bouding box so cargo ships aren't spawned near other players
                    GetSafeBoundingBoxForPlayers(playerPosition, spawnDistance, out spawnBox);
                    // Forbidden radius is sphere around all players in box.
                    // Bounding box is generated from players positions so their distance to center shall be same for all players
                    forbiddenRadius += spawnBox.HalfExtents.Max( ) - NEUTRAL_SHIP_FORBIDDEN_RADIUS;
                }
                // Get the direction to the center and deviate it randomly
                Vector3D?origin = MyUtils.GetRandomBorderPosition(ref spawnBox);

                origin = MyEntities.FindFreePlace(origin.Value, spawnGroup.SpawnRadius);
                if (!origin.HasValue)
                {
                    if (ExtenderOptions.IsDebugging)
                    {
                        Essentials.Log.Info("Couldn't find free place for cargo spawn");
                    }
                    return;
                }

                // Radius in arc units of the forbidden sphere in the center, when viewed from origin
                float centerArcRadius = (float)Math.Atan(forbiddenRadius / (origin.Value - spawnBox.Center).Length( ));

                // Generate direction with elevation from centerArcRadius radians to (cAR + N_S_D_S) radians
                Vector3D direction = -Vector3D.Normalize(origin.Value);
                float theta        = MyUtils.GetRandomFloat(centerArcRadius, centerArcRadius + NEUTRAL_SHIP_DIRECTION_SPREAD);
                float phi          = MyUtils.GetRandomRadian( );
                Vector3D cosVec    = Vector3D.CalculatePerpendicularVector(direction);
                Vector3D sinVec    = Vector3D.Cross(direction, cosVec);
                cosVec            *= (Math.Sin(theta) * Math.Cos(phi));
                sinVec            *= (Math.Sin(theta) * Math.Sin(phi));
                direction          = direction * Math.Cos(theta) + cosVec + sinVec;

                Vector3D destination = Vector3D.Zero;
                RayD ray             = new RayD(origin.Value, direction);
                double?intersection  = ray.Intersects(spawnBox);
                Vector3D directionMult;
                if (!intersection.HasValue || intersection.Value < NEUTRAL_SHIP_MINIMAL_ROUTE_LENGTH)
                {
                    directionMult = direction * NEUTRAL_SHIP_MINIMAL_ROUTE_LENGTH;
                }
                else
                {
                    directionMult = direction * intersection.Value;
                }
                destination = origin.Value + directionMult;

                Vector3D upVector    = Vector3D.CalculatePerpendicularVector(direction);
                Vector3D rightVector = Vector3D.Cross(direction, upVector);
                MatrixD originMatrix = MatrixD.CreateWorld(origin.Value, direction, upVector);

                // CH:TODO: Convex cast to detect collision
                // Check ships' path to avoid possible collisions. (TODO: But only if it is said in the definitions)
                m_raycastHits.Clear( );
                foreach (var shipPrefab in spawnGroup.Prefabs)
                {
                    var prefabDef = MyDefinitionManager.Static.GetPrefabDefinition(shipPrefab.SubtypeId);
                    Debug.Assert(prefabDef != null);

                    Vector3D shipPosition    = Vector3.Transform(shipPrefab.Position, originMatrix);
                    Vector3D shipDestination = shipPosition + directionMult;
                    float radius             = prefabDef == null ? 10.0f : prefabDef.BoundingSphere.Radius;

                    if (checkGravity)
                    {
                        //these point checks could be done in the trajectory intersect, but checking points is faster than ray intersect
                        if (MyGravityProviderSystem.IsPositionInNaturalGravity(shipPosition, spawnGroup.SpawnRadius))
                        {
                            if (ExtenderOptions.IsDebugging)
                            {
                                Essentials.Log.Info("Failed to spawn cargo ship: Spawn location is in gravity well");
                            }
                            return;
                        }
                        if (MyGravityProviderSystem.IsPositionInNaturalGravity(shipDestination, spawnGroup.SpawnRadius))
                        {
                            if (ExtenderOptions.IsDebugging)
                            {
                                Essentials.Log.Info("Failed to spawn cargo ship: Destination is in gravity well");
                            }
                        }
                        if (MyGravityProviderSystem.DoesTrajectoryIntersectNaturalGravity(shipPosition, shipDestination, spawnGroup.SpawnRadius + 500))
                        {
                            if (ExtenderOptions.IsDebugging)
                            {
                                Essentials.Log.Info("Failed to spawn cargo ship: Ship path intersects gravity well");
                            }
                            return;
                        }
                    }

                    MyPhysics.CastRay(shipPosition, shipDestination, m_raycastHits, MyPhysics.CollisionLayers.ObjectDetectionCollisionLayer);
                    if (m_raycastHits.Count > 0)
                    {
                        if (ExtenderOptions.IsDebugging)
                        {
                            Essentials.Log.Info("Failed to spawn cargo ship: Ship path intersects another object");
                        }
                        return;
                    }

                    for (int i = 0; i < 4; ++i)
                    {
                        Vector3D shiftVector = upVector * m_upVecMultipliers[i] * radius + rightVector * m_rightVecMultipliers[i] * radius;
                        MyPhysics.CastRay(shipPosition + shiftVector, shipDestination + shiftVector, m_raycastHits, MyPhysics.CollisionLayers.ObjectDetectionCollisionLayer);

                        if (m_raycastHits.Count > 0)
                        {
                            if (ExtenderOptions.IsDebugging)
                            {
                                Essentials.Log.Info("Failed to spawn cargo ship: Ship path intersects another object");
                            }
                            return;
                        }
                    }
                }

                long spawnGroupId = MyPirateAntennas.GetPiratesId( );

                // The ships were collision-free. Now spawn them
                foreach (var shipPrefab in spawnGroup.Prefabs)
                {
                    // Yes, this could have been saved in the previous loop, but compared to (e.g.) raycasts, this does not take too much time to recalculate
                    Vector3D shipPosition    = Vector3D.Transform((Vector3D)shipPrefab.Position, originMatrix);
                    Vector3D shipDestination = shipPosition + directionMult;
                    Vector3D up = Vector3D.CalculatePerpendicularVector(-direction);

                    m_tmpGridList.Clear( );

                    // CH: We don't want a new identity for each ship anymore. We should handle that in a better way...

                    /*if (shipPrefab.ResetOwnership)
                     * {
                     * if (spawnGroupId == 0)
                     * {
                     * //This is not an NPC so that it doesn't show up in assign ownership drop down menu
                     * MyIdentity spawnGroupIdentity = Sync.Players.CreateNewIdentity("Neutral NPC");
                     * spawnGroupId = spawnGroupIdentity.IdentityId;
                     * }
                     * }*/

                    // Deploy ship
                    MyPrefabManager.Static.SpawnPrefab(
                        resultList: m_tmpGridList,
                        prefabName: shipPrefab.SubtypeId,
                        position: shipPosition,
                        forward: direction,
                        up: up,
                        initialLinearVelocity: shipPrefab.Speed * direction,
                        beaconName: shipPrefab.BeaconText,
                        spawningOptions: VRage.Game.ModAPI.SpawningOptions.RotateFirstCockpitTowardsDirection |
                        VRage.Game.ModAPI.SpawningOptions.SpawnRandomCargo |
                        VRage.Game.ModAPI.SpawningOptions.DisableDampeners,
                        ownerId: shipPrefab.ResetOwnership ? spawnGroupId : 0,
                        updateSync: true);

                    /*
                     * foreach (var grid in m_tmpGridList)
                     * {
                     * var cockpit = grid.GetFirstBlockOfType<MyCockpit>();
                     * if (cockpit != null)
                     * {
                     * MySimpleAutopilot ai = new MySimpleAutopilot(shipDestination, (Vector3)direction);
                     * cockpit.AttachAutopilot(ai);
                     * break;
                     * }
                     * }
                     */
                    m_tmpGridList.Clear( );
                }
            });
        }
Example #2
0
        public static void OnGlobalSpawnEvent(object senderEvent)
        {
            // Select a spawn group to spawn
            MySpawnGroupDefinition spawnGroup = PickRandomSpawnGroup();

            if (spawnGroup == null)
            {
                return;
            }

            spawnGroup.ReloadPrefabs();

            ProfilerShort.Begin("Generate position and direction");

            double   spawnDistance  = NEUTRAL_SHIP_SPAWN_DISTANCE;
            Vector3D playerPosition = Vector3D.Zero;
            bool     isWorldLimited = MyEntities.IsWorldLimited();
            int      numPlayers     = 0;

            if (isWorldLimited)
            {
                spawnDistance = Math.Min(spawnDistance, MyEntities.WorldSafeHalfExtent() - spawnGroup.SpawnRadius);
            }
            else
            {
                // In infinite worlds players can be thousands of kilometers away, so spawn ship around random player
                // so cargo ships will be spawned around every player at some time
                var players = MySession.Static.Players.GetOnlinePlayers();
                // In DS there can be no players connected
                numPlayers = Math.Max(0, players.Count - 1);
                int randomPlayerPosition = MyUtils.GetRandomInt(0, numPlayers);
                int i = 0;
                foreach (var player in players)
                {
                    if (i == randomPlayerPosition)
                    {
                        if (player.Character != null)
                        {
                            playerPosition = player.GetPosition();
                        }
                        break;
                    }
                    i++;
                }
            }
            if (spawnDistance < 0.0f)
            {
                MySandboxGame.Log.WriteLine("Not enough space in the world to spawn such a huge spawn group!");
                return;
            }

            double       forbiddenRadius = NEUTRAL_SHIP_FORBIDDEN_RADIUS;
            BoundingBoxD spawnBox;

            if (isWorldLimited)
            {
                spawnBox = new BoundingBoxD(new Vector3D(playerPosition - spawnDistance), new Vector3D(playerPosition + spawnDistance));
            }
            else
            {
                // We need to extend bouding box so cargo ships aren't spawned near other players
                GetSafeBoundingBoxForPlayers(playerPosition, spawnDistance, out spawnBox);
                // Forbidden radius is sphere around all players in box.
                // Bounding box is generated from players positions so their distance to center shall be same for all players
                forbiddenRadius += spawnBox.HalfExtents.Max() - NEUTRAL_SHIP_FORBIDDEN_RADIUS;
            }

            // Get the direction to the center and deviate it randomly
            Vector3D?origin = MyUtils.GetRandomBorderPosition(ref spawnBox);

            origin = MyEntities.TestPlaceInSpace(origin.Value, spawnGroup.SpawnRadius);
            if (!origin.HasValue)
            {
                if (++m_eventSpawnTry <= EVENT_SPAWN_TRY_MAX)
                {
                    MySandboxGame.Log.WriteLine("Could not spawn neutral ships - no free place found. Try " + m_eventSpawnTry + " of " + EVENT_SPAWN_TRY_MAX);

                    MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                    ProfilerShort.End();
                    return;
                }
                else
                {
                    m_eventSpawnTry = 0;
                    return;
                }
            }
            m_eventSpawnTry = 0;

            // Radius in arc units of the forbidden sphere in the center, when viewed from origin
            float centerArcRadius = (float)Math.Atan(forbiddenRadius / (origin.Value - spawnBox.Center).Length());

            // Generate direction with elevation from centerArcRadius radians to (cAR + N_S_D_S) radians
            Vector3D direction = -Vector3D.Normalize(origin.Value);
            float    theta     = MyUtils.GetRandomFloat(centerArcRadius, centerArcRadius + NEUTRAL_SHIP_DIRECTION_SPREAD);
            float    phi       = MyUtils.GetRandomRadian();
            Vector3D cosVec    = Vector3D.CalculatePerpendicularVector(direction);
            Vector3D sinVec    = Vector3D.Cross(direction, cosVec);

            cosVec   *= (Math.Sin(theta) * Math.Cos(phi));
            sinVec   *= (Math.Sin(theta) * Math.Sin(phi));
            direction = direction * Math.Cos(theta) + cosVec + sinVec;

            Vector3D destination  = Vector3D.Zero;
            RayD     ray          = new RayD(origin.Value, direction);
            double?  intersection = ray.Intersects(spawnBox);
            Vector3D directionMult;

            if (!intersection.HasValue || intersection.Value < NEUTRAL_SHIP_MINIMAL_ROUTE_LENGTH)
            {
                directionMult = direction * NEUTRAL_SHIP_MINIMAL_ROUTE_LENGTH;
            }
            else
            {
                directionMult = direction * intersection.Value;
            }
            destination = origin.Value + directionMult;

            Vector3D upVector     = Vector3D.CalculatePerpendicularVector(direction);
            Vector3D rightVector  = Vector3D.Cross(direction, upVector);
            MatrixD  originMatrix = MatrixD.CreateWorld(origin.Value, direction, upVector);

            ProfilerShort.End();

            ProfilerShort.Begin("Check free space");

            // CH:TODO: Convex cast to detect collision
            // Check ships' path to avoid possible collisions. (TODO: But only if it is said in the definitions)
            m_raycastHits.Clear();
            foreach (var shipPrefab in spawnGroup.Prefabs)
            {
                var prefabDef = MyDefinitionManager.Static.GetPrefabDefinition(shipPrefab.SubtypeId);
                Debug.Assert(prefabDef != null);

                Vector3D shipPosition    = Vector3.Transform(shipPrefab.Position, originMatrix);
                Vector3D shipDestination = shipPosition + directionMult;
                float    radius          = prefabDef == null ? 10.0f : prefabDef.BoundingSphere.Radius;

                //these point checks could be done in the trajectory intersect, but checking points is faster than ray intersect
                if (MyGravityProviderSystem.IsPositionInNaturalGravity(shipPosition, spawnGroup.SpawnRadius))
                {
                    if (!MyFinalBuildConstants.IS_OFFICIAL)
                    {
                        MySandboxGame.Log.WriteLine("Could not spawn neutral ships: spawn point is inside gravity well");
                    }
                    MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                    ProfilerShort.End();
                    return;
                }
                if (MyGravityProviderSystem.IsPositionInNaturalGravity(shipDestination, spawnGroup.SpawnRadius))
                {
                    if (!MyFinalBuildConstants.IS_OFFICIAL)
                    {
                        MySandboxGame.Log.WriteLine("Could not spawn neutral ships: destination point is inside gravity well");
                    }
                    MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                    ProfilerShort.End();
                    return;
                }
                if (MyGravityProviderSystem.DoesTrajectoryIntersectNaturalGravity(shipPosition, shipDestination, spawnGroup.SpawnRadius + NEUTRAL_SHIP_SPAWN_OFFSET))
                {
                    if (!MyFinalBuildConstants.IS_OFFICIAL)
                    {
                        MySandboxGame.Log.WriteLine("Could not spawn neutral ships: flight path intersects gravity well");
                    }
                    MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                    ProfilerShort.End();
                    return;
                }

                MyPhysics.CastRay(shipPosition, shipDestination, m_raycastHits, MyPhysics.CollisionLayers.ObjectDetectionCollisionLayer);
                if (m_raycastHits.Count > 0)
                {
                    if (!MyFinalBuildConstants.IS_OFFICIAL)
                    {
                        MySandboxGame.Log.WriteLine("Could not spawn neutral ships due to collision");
                    }
                    MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                    ProfilerShort.End();
                    return;
                }

                for (int i = 0; i < 4; ++i)
                {
                    Vector3D shiftVector = upVector * m_upVecMultipliers[i] * radius + rightVector * m_rightVecMultipliers[i] * radius;
                    MyPhysics.CastRay(shipPosition + shiftVector, shipDestination + shiftVector, m_raycastHits, MyPhysics.CollisionLayers.ObjectDetectionCollisionLayer);

                    if (m_raycastHits.Count > 0)
                    {
                        if (!MyFinalBuildConstants.IS_OFFICIAL)
                        {
                            MySandboxGame.Log.WriteLine("Could not spawn neutral ships due to collision");
                        }
                        MyGlobalEvents.RescheduleEvent(senderEvent as MyGlobalEventBase, NEUTRAL_SHIP_RESCHEDULE_TIME);
                        ProfilerShort.End();
                        return;
                    }
                }
            }

            ProfilerShort.End();

            ProfilerShort.Begin("Spawn ships");

            long spawnGroupId = MyPirateAntennas.GetPiratesId();

            // The ships were collision-free. Now spawn them
            foreach (var shipPrefab in spawnGroup.Prefabs)
            {
                ProfilerShort.Begin(shipPrefab.BeaconText);

                // Yes, this could have been saved in the previous loop, but compared to (e.g.) raycasts, this does not take too much time to recalculate
                Vector3D shipPosition    = Vector3D.Transform((Vector3D)shipPrefab.Position, originMatrix);
                Vector3D shipDestination = shipPosition + directionMult;
                Vector3D up = Vector3D.CalculatePerpendicularVector(-direction);

                List <MyCubeGrid> tmpGridList = new List <MyCubeGrid>();

                // CH: We don't want a new identity for each ship anymore. We should handle that in a better way...

                /*if (shipPrefab.ResetOwnership)
                 * {
                 *  if (spawnGroupId == 0)
                 *  {
                 *      //This is not an NPC so that it doesn't show up in assign ownership drop down menu
                 *      MyIdentity spawnGroupIdentity = Sync.Players.CreateNewIdentity("Neutral NPC");
                 *      spawnGroupId = spawnGroupIdentity.IdentityId;
                 *  }
                 * }*/

                // Deploy ship
                ProfilerShort.Begin("Spawn cargo ship");
                Stack <Action> callbacks = new Stack <Action>();
                callbacks.Push(delegate()
                {
                    InitAutopilot(tmpGridList, shipDestination, direction);
                    foreach (var grid in tmpGridList)
                    {
                        Debug.Assert(grid.Physics.Enabled);
                        grid.ActivatePhysics(); //last chance to activate physics
                    }
                });
                MyPrefabManager.Static.SpawnPrefab(
                    resultList: tmpGridList,
                    prefabName: shipPrefab.SubtypeId,
                    position: shipPosition,
                    forward: direction,
                    up: up,
                    initialLinearVelocity: shipPrefab.Speed * direction,
                    beaconName: shipPrefab.BeaconText,
                    spawningOptions: VRage.Game.ModAPI.SpawningOptions.RotateFirstCockpitTowardsDirection |
                    VRage.Game.ModAPI.SpawningOptions.SpawnRandomCargo |
                    VRage.Game.ModAPI.SpawningOptions.DisableDampeners,
                    ownerId: shipPrefab.ResetOwnership ? spawnGroupId : 0,
                    callbacks: callbacks);
                ProfilerShort.End();
                ProfilerShort.End();
            }

            ProfilerShort.End();
        }