Example #1
0
        /// <summary>
        /// Returns a rect that contains the borders of this sub and all subs docked to it
        /// </summary>
        public Rectangle GetDockedBorders()
        {
            Rectangle dockedBorders = Borders;

            dockedBorders.Y -= dockedBorders.Height;

            var connectedSubs = GetConnectedSubs();

            foreach (Submarine dockedSub in connectedSubs)
            {
                if (dockedSub == this)
                {
                    continue;
                }

                Vector2 diff = dockedSub.Submarine == this ? dockedSub.WorldPosition : dockedSub.WorldPosition - WorldPosition;

                Rectangle dockedSubBorders = dockedSub.Borders;
                dockedSubBorders.Y        -= dockedSubBorders.Height;
                dockedSubBorders.Location += MathUtils.ToPoint(diff);

                dockedBorders = Rectangle.Union(dockedBorders, dockedSubBorders);
            }

            dockedBorders.Y += dockedBorders.Height;
            return(dockedBorders);
        }
Example #2
0
        public override void UpdatePlacing(Camera cam)
        {
            if (PlayerInput.SecondaryMouseButtonClicked())
            {
                selected = null;
                return;
            }

            Vector2   position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
            Vector2   size     = ScaledSize;
            Rectangle newRect  = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y);

            if (placePosition == Vector2.Zero)
            {
                if (PlayerInput.PrimaryMouseButtonHeld())
                {
                    placePosition = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
                }

                newRect.X = (int)position.X;
                newRect.Y = (int)position.Y;
            }
            else
            {
                Vector2 placeSize = size;
                if (ResizeHorizontal)
                {
                    placeSize.X = position.X - placePosition.X;
                }
                if (ResizeVertical)
                {
                    placeSize.Y = placePosition.Y - position.Y;
                }

                //don't allow resizing width/height to less than the grid size
                if (ResizeHorizontal && Math.Abs(placeSize.X) < Submarine.GridSize.X)
                {
                    placeSize.X = Submarine.GridSize.X;
                }
                if (ResizeVertical && Math.Abs(placeSize.Y) < Submarine.GridSize.Y)
                {
                    placeSize.Y = Submarine.GridSize.Y;
                }

                newRect = Submarine.AbsRect(placePosition, placeSize);
                if (PlayerInput.PrimaryMouseButtonReleased())
                {
                    newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
                    new Structure(newRect, this, Submarine.MainSub)
                    {
                        Submarine = Submarine.MainSub
                    };

                    selected = null;
                    return;
                }
            }
        }
        public virtual void UpdatePlacing(Camera cam)
        {
            if (PlayerInput.SecondaryMouseButtonClicked())
            {
                selected = null;
                return;
            }

            Vector2 placeSize = Submarine.GridSize;

            if (placePosition == Vector2.Zero)
            {
                Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);

                if (PlayerInput.PrimaryMouseButtonHeld())
                {
                    placePosition = position;
                }
            }
            else
            {
                Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);

                if (ResizeHorizontal)
                {
                    placeSize.X = position.X - placePosition.X;
                }
                if (ResizeVertical)
                {
                    placeSize.Y = placePosition.Y - position.Y;
                }

                Rectangle newRect = Submarine.AbsRect(placePosition, placeSize);
                newRect.Width  = (int)Math.Max(newRect.Width, Submarine.GridSize.X);
                newRect.Height = (int)Math.Max(newRect.Height, Submarine.GridSize.Y);

                if (Submarine.MainSub != null)
                {
                    newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);
                }

                if (PlayerInput.PrimaryMouseButtonReleased())
                {
                    CreateInstance(newRect);
                    placePosition = Vector2.Zero;
                    if (!PlayerInput.IsShiftDown())
                    {
                        selected = null;
                    }
                }

                newRect.Y = -newRect.Y;
            }
        }
Example #4
0
        public static LinkedSubmarine Load(XElement element, Submarine submarine)
        {
            Vector2         pos       = element.GetAttributeVector2("pos", Vector2.Zero);
            LinkedSubmarine linkedSub = null;

            if (Screen.Selected == GameMain.SubEditorScreen)
            {
                linkedSub                       = CreateDummy(submarine, element, pos);
                linkedSub.saveElement           = element;
                linkedSub.purchasedLostShuttles = false;
            }
            else
            {
                linkedSub = new LinkedSubmarine(submarine)
                {
                    saveElement = element
                };

                linkedSub.purchasedLostShuttles = GameMain.GameSession.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles;
                string levelSeed = element.GetAttributeString("location", "");
                if (!string.IsNullOrWhiteSpace(levelSeed) &&
                    GameMain.GameSession.Level != null &&
                    GameMain.GameSession.Level.Seed != levelSeed &&
                    !linkedSub.purchasedLostShuttles)
                {
                    linkedSub.loadSub = false;
                }
                else
                {
                    linkedSub.loadSub       = true;
                    linkedSub.rect.Location = MathUtils.ToPoint(pos);
                }
            }

            linkedSub.filePath = element.GetAttributeString("filepath", "");
            int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
            for (int i = 0; i < linkedToIds.Length; i++)
            {
                linkedSub.linkedToID.Add((ushort)linkedToIds[i]);
                if (Screen.Selected == GameMain.SubEditorScreen)
                {
                    if (FindEntityByID((ushort)linkedToIds[i]) is MapEntity linked)
                    {
                        linkedSub.linkedTo.Add(linked);
                    }
                }
            }
            linkedSub.originalLinkedToID = (ushort)element.GetAttributeInt("originallinkedto", 0);
            linkedSub.originalMyPortID   = (ushort)element.GetAttributeInt("originalmyport", 0);


            return(linkedSub.loadSub ? linkedSub : null);
        }
Example #5
0
        public override void UpdatePlacing(Camera cam)
        {
            Vector2   position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
            Vector2   size     = ScaledSize;
            Rectangle newRect  = new Rectangle((int)position.X, (int)position.Y, (int)size.X, (int)size.Y);

            if (placePosition == Vector2.Zero)
            {
                if (PlayerInput.LeftButtonHeld())
                {
                    placePosition = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
                }

                newRect.X = (int)position.X;
                newRect.Y = (int)position.Y;
            }
            else
            {
                Vector2 placeSize = size;
                if (ResizeHorizontal)
                {
                    placeSize.X = position.X - placePosition.X;
                }
                if (ResizeVertical)
                {
                    placeSize.Y = placePosition.Y - position.Y;
                }

                newRect = Submarine.AbsRect(placePosition, placeSize);

                if (PlayerInput.LeftButtonReleased())
                {
                    //don't allow resizing width/height to zero
                    if ((!ResizeHorizontal || placeSize.X != 0.0f) && (!ResizeVertical || placeSize.Y != 0.0f))
                    {
                        newRect.Location -= MathUtils.ToPoint(Submarine.MainSub.Position);

                        var structure = new Structure(newRect, this, Submarine.MainSub);
                        structure.Submarine = Submarine.MainSub;
                    }

                    selected = null;
                    return;
                }
            }

            if (PlayerInput.RightButtonHeld())
            {
                selected = null;
            }
        }
        /// <summary>
        /// Moves away any character that is inside the bounding box of the sub (but not inside the sub)
        /// </summary>
        /// <param name="subTranslation">The translation that was applied to the sub before doing the displacement
        /// (used for determining where to push the characters)</param>
        private void DisplaceCharacters(Vector2 subTranslation)
        {
            Rectangle worldBorders = Borders;

            worldBorders.Location += MathUtils.ToPoint(ConvertUnits.ToDisplayUnits(Body.SimPosition));

            Vector2 translateDir = Vector2.Normalize(subTranslation);

            if (!MathUtils.IsValid(translateDir))
            {
                translateDir = Vector2.UnitY;
            }

            foreach (Character c in Character.CharacterList)
            {
                if (c.AnimController.CurrentHull != null && c.AnimController.CanEnterSubmarine)
                {
                    continue;
                }

                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered)
                    {
                        continue;
                    }
                    //if the character isn't inside the bounding box, continue
                    if (!Submarine.RectContains(worldBorders, limb.WorldPosition))
                    {
                        continue;
                    }

                    //cast a line from the position of the character to the same direction as the translation of the sub
                    //and see where it intersects with the bounding box
                    if (!MathUtils.GetLineRectangleIntersection(limb.WorldPosition,
                                                                limb.WorldPosition + translateDir * 100000.0f, worldBorders, out Vector2 intersection))
                    {
                        //should never happen when casting a line out from inside the bounding box
                        Debug.Assert(false);
                        continue;
                    }


                    //"+ translatedir" in order to move the character slightly away from the wall
                    c.AnimController.SetPosition(ConvertUnits.ToSimUnits(c.WorldPosition + (intersection - limb.WorldPosition)) + translateDir);

                    return;
                }
            }
        }
Example #7
0
        /// <summary>
        /// Finds the sub whose borders contain the position
        /// </summary>
        public static Submarine FindContaining(Vector2 position)
        {
            foreach (Submarine sub in Submarine.Loaded)
            {
                Rectangle subBorders = sub.Borders;
                subBorders.Location += MathUtils.ToPoint(sub.HiddenSubPosition) - new Microsoft.Xna.Framework.Point(0, sub.Borders.Height);

                subBorders.Inflate(500.0f, 500.0f);

                if (subBorders.Contains(position))
                {
                    return(sub);
                }
            }

            return(null);
        }
Example #8
0
        public void AddToGrid(Submarine submarine)
        {
            foreach (EntityGrid grid in entityGrids)
            {
                if (grid.Submarine != submarine)
                {
                    continue;
                }

                rect.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition);

                grid.InsertEntity(this);

                rect.Location += MathUtils.ToPoint(submarine.HiddenSubPosition);
                return;
            }
        }
        // LinkedSubmarine.Load() is called from MapEntity.LoadAll()
        public static LinkedSubmarine Load(XElement element, Submarine submarine, IdRemap idRemap)
        {
            Vector2         pos = element.GetAttributeVector2("pos", Vector2.Zero);
            LinkedSubmarine linkedSub;

            if (Screen.Selected == GameMain.SubEditorScreen)
            {
                linkedSub                       = CreateDummy(submarine, element, pos, idRemap.AssignMaxId());
                linkedSub.saveElement           = element;
                linkedSub.purchasedLostShuttles = false;
            }
            else
            {
                string    levelSeed = element.GetAttributeString("location", "");
                LevelData levelData = GameMain.GameSession?.Campaign?.NextLevel ?? GameMain.GameSession?.LevelData;
                linkedSub = new LinkedSubmarine(submarine, idRemap.AssignMaxId())
                {
                    purchasedLostShuttles = GameMain.GameSession?.GameMode is CampaignMode campaign && campaign.PurchasedLostShuttles,
                    saveElement           = element
                };

                if (!string.IsNullOrWhiteSpace(levelSeed) && levelData != null &&
                    levelData.Seed != levelSeed && !linkedSub.purchasedLostShuttles)
                {
                    linkedSub.loadSub = false;
                }
                else
                {
                    linkedSub.loadSub       = true;
                    linkedSub.rect.Location = MathUtils.ToPoint(pos);
                }
            }

            linkedSub.filePath = element.GetAttributeString("filepath", "");
            int[] linkedToIds = element.GetAttributeIntArray("linkedto", new int[0]);
            for (int i = 0; i < linkedToIds.Length; i++)
            {
                linkedSub.linkedToID.Add(idRemap.GetOffsetId(linkedToIds[i]));
            }
            linkedSub.originalLinkedToID = idRemap.GetOffsetId(element.GetAttributeInt("originallinkedto", 0));
            linkedSub.originalMyPortID   = (ushort)element.GetAttributeInt("originalmyport", 0);

            return(linkedSub.loadSub ? linkedSub : null);
        }
Example #10
0
        public static LinkedSubmarine Load(XElement element, Submarine submarine)
        {
            Vector2 pos = element.GetAttributeVector2("pos", Vector2.Zero);

            LinkedSubmarine linkedSub = null;

            if (Screen.Selected == GameMain.SubEditorScreen)
            {
                linkedSub             = CreateDummy(submarine, element, pos);
                linkedSub.saveElement = element;
            }
            else
            {
                linkedSub = new LinkedSubmarine(submarine)
                {
                    saveElement = element
                };

                string levelSeed = element.GetAttributeString("location", "");
                if (!string.IsNullOrWhiteSpace(levelSeed) && GameMain.GameSession.Level != null && GameMain.GameSession.Level.Seed != levelSeed)
                {
                    linkedSub.loadSub = false;
                    return(null);
                }

                linkedSub.loadSub = true;

                linkedSub.rect.Location = MathUtils.ToPoint(pos);
            }

            linkedSub.filePath = element.GetAttributeString("filepath", "");

            string linkedToString = element.GetAttributeString("linkedto", "");

            if (linkedToString != "")
            {
                string[] linkedToIds = linkedToString.Split(',');
                for (int i = 0; i < linkedToIds.Length; i++)
                {
                    linkedSub.linkedToID.Add((ushort)int.Parse(linkedToIds[i]));
                }
            }
            return(linkedSub);
        }
Example #11
0
        /// <summary>
        /// Returns a rect that contains the borders of this sub and all subs docked to it
        /// </summary>
        public Rectangle GetDockedBorders()
        {
            Rectangle dockedBorders = Borders;

            dockedBorders.Y -= dockedBorders.Height;

            foreach (Submarine dockedSub in DockedTo)
            {
                Vector2 diff = dockedSub.Submarine == this ? dockedSub.WorldPosition : dockedSub.WorldPosition - WorldPosition;


                Rectangle dockedSubBorders = dockedSub.Borders;
                dockedSubBorders.Y        -= dockedSubBorders.Height;
                dockedSubBorders.Location += MathUtils.ToPoint(diff);

                dockedBorders = Rectangle.Union(dockedBorders, dockedSubBorders);
            }

            dockedBorders.Y += dockedBorders.Height;
            return(dockedBorders);
        }
Example #12
0
        private void GenerateRuin(List <VoronoiCell> mainPath)
        {
            Vector2 ruinSize   = new Vector2(Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server), Rand.Range(5000.0f, 8000.0f, Rand.RandSync.Server));
            float   ruinRadius = Math.Max(ruinSize.X, ruinSize.Y) * 0.5f;

            Vector2 ruinPos = cells[Rand.Int(cells.Count, Rand.RandSync.Server)].Center;

            //50% chance of placing the ruins at a cave
            if (Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) < 0.5f)
            {
                TryGetInterestingPosition(true, PositionType.Cave, 0.0f, out ruinPos);
            }

            ruinPos.Y = Math.Min(ruinPos.Y, borders.Y + borders.Height - ruinSize.Y / 2);
            ruinPos.Y = Math.Max(ruinPos.Y, SeaFloorTopPos + ruinSize.Y / 2.0f);

            int iter = 0;

            while (mainPath.Any(p => Vector2.Distance(ruinPos, p.Center) < ruinRadius * 2.0f))
            {
                Vector2 weighedPathPos = ruinPos;
                iter++;

                foreach (VoronoiCell pathCell in mainPath)
                {
                    float dist = Vector2.Distance(pathCell.Center, ruinPos);
                    if (dist > 10000.0f)
                    {
                        continue;
                    }

                    Vector2 moveAmount = Vector2.Normalize(ruinPos - pathCell.Center) * 100000.0f / dist;

                    weighedPathPos  += moveAmount;
                    weighedPathPos.Y = Math.Min(borders.Y + borders.Height - ruinSize.Y / 2, weighedPathPos.Y);
                }

                ruinPos = weighedPathPos;

                if (iter > 10000)
                {
                    break;
                }
            }

            VoronoiCell closestPathCell = null;
            float       closestDist     = 0.0f;

            foreach (VoronoiCell pathCell in mainPath)
            {
                float dist = Vector2.Distance(pathCell.Center, ruinPos);
                if (closestPathCell == null || dist < closestDist)
                {
                    closestPathCell = pathCell;
                    closestDist     = dist;
                }
            }

            var ruin = new Ruin(closestPathCell, cells, new Rectangle(MathUtils.ToPoint(ruinPos - ruinSize * 0.5f), MathUtils.ToPoint(ruinSize)));

            ruins.Add(ruin);

            ruin.RuinShapes.Sort((shape1, shape2) => shape2.DistanceFromEntrance.CompareTo(shape1.DistanceFromEntrance));
            for (int i = 0; i < 4; i++)
            {
                positionsOfInterest.Add(new InterestingPosition(ruin.RuinShapes[i].Rect.Center.ToVector2(), PositionType.Ruin));
            }

            foreach (RuinShape ruinShape in ruin.RuinShapes)
            {
                var tooClose = GetTooCloseCells(ruinShape.Rect.Center.ToVector2(), Math.Max(ruinShape.Rect.Width, ruinShape.Rect.Height));

                foreach (VoronoiCell cell in tooClose)
                {
                    if (cell.CellType == CellType.Empty)
                    {
                        continue;
                    }
                    foreach (GraphEdge e in cell.edges)
                    {
                        Rectangle rect = ruinShape.Rect;
                        rect.Y += rect.Height;
                        if (ruinShape.Rect.Contains(e.point1) || ruinShape.Rect.Contains(e.point2) ||
                            MathUtils.GetLineRectangleIntersection(e.point1, e.point2, rect) != null)
                        {
                            cell.CellType = CellType.Removed;

                            int x = (int)Math.Floor(cell.Center.X / GridCellSize);
                            int y = (int)Math.Floor(cell.Center.Y / GridCellSize);

                            cellGrid[x, y].Remove(cell);
                            cells.Remove(cell);
                            break;
                        }
                    }
                }
            }
        }
        public void Update(float deltaTime)
        {
            while (impactQueue.Count > 0)
            {
                var impact = impactQueue.Dequeue();

                if (impact.Target.UserData is VoronoiCell cell)
                {
                    HandleLevelCollision(impact);
                }
                else if (impact.Target.Body.UserData is Structure)
                {
                    HandleLevelCollision(impact);
                }
                else if (impact.Target.Body.UserData is Submarine otherSub)
                {
                    HandleSubCollision(impact, otherSub);
                }
                else if (impact.Target.Body.UserData is Limb limb)
                {
                    HandleLimbCollision(impact, limb);
                }
            }

            //-------------------------

            if (Body.FarseerBody.BodyType == BodyType.Static)
            {
                return;
            }

            ClientUpdatePosition(deltaTime);
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return;
            }

            Vector2 totalForce = CalculateBuoyancy();

            //-------------------------

            //if outside left or right edge of the level
            if (Level.Loaded != null && (Position.X < 0 || Position.X > Level.Loaded.Size.X))
            {
                Rectangle worldBorders = Borders;
                worldBorders.Location += MathUtils.ToPoint(Position);

                //push the sub back below the upper "barrier" of the level
                if (worldBorders.Y > Level.Loaded.Size.Y)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Min(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.Size.Y - worldBorders.Y)));
                }
                else if (worldBorders.Y - worldBorders.Height < Level.Loaded.BottomPos)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Max(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.BottomPos - (worldBorders.Y - worldBorders.Height))));
                }

                if (Position.X < 0)
                {
                    float force = Math.Abs(Position.X * 0.5f);
                    totalForce += Vector2.UnitX * force;
                    if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
                    {
                        GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, Math.Min(force * 0.0001f, 5.0f));
                    }
                }
                else
                {
                    float force = (Position.X - Level.Loaded.Size.X) * 0.5f;
                    totalForce -= Vector2.UnitX * force;
                    if (Character.Controlled != null && Character.Controlled.Submarine == submarine)
                    {
                        GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, Math.Min(force * 0.0001f, 5.0f));
                    }
                }
            }

            //-------------------------

            if (Body.LinearVelocity.LengthSquared() > 0.0001f)
            {
                //TODO: sync current drag with clients?
                float     attachedMass = 0.0f;
                JointEdge jointEdge    = Body.FarseerBody.JointList;
                while (jointEdge != null)
                {
                    Body      otherBody = jointEdge.Joint.BodyA == Body.FarseerBody ? jointEdge.Joint.BodyB : jointEdge.Joint.BodyA;
                    Character character = (otherBody.UserData as Limb)?.character;
                    if (character != null)
                    {
                        attachedMass += character.Mass;
                    }

                    jointEdge = jointEdge.Next;
                }

                float horizontalDragCoefficient = MathHelper.Clamp(HorizontalDrag + attachedMass / 5000.0f, 0.0f, MaxDrag);
                totalForce.X -= Math.Sign(Body.LinearVelocity.X) * Body.LinearVelocity.X * Body.LinearVelocity.X * horizontalDragCoefficient * Body.Mass;

                float verticalDragCoefficient = MathHelper.Clamp(VerticalDrag + attachedMass / 5000.0f, 0.0f, MaxDrag);
                totalForce.Y -= Math.Sign(Body.LinearVelocity.Y) * Body.LinearVelocity.Y * Body.LinearVelocity.Y * verticalDragCoefficient * Body.Mass;
            }

            ApplyForce(totalForce);

            UpdateDepthDamage(deltaTime);
        }
Example #14
0
        public static void GenerateSubWaypoints(Submarine submarine)
        {
            if (!Hull.hullList.Any())
            {
                DebugConsole.ThrowError("Couldn't generate waypoints: no hulls found.");
                return;
            }

            List <WayPoint> existingWaypoints = WayPointList.FindAll(wp => wp.spawnType == SpawnType.Path);

            foreach (WayPoint wayPoint in existingWaypoints)
            {
                wayPoint.Remove();
            }

            float minDist         = 150.0f;
            float heightFromFloor = 110.0f;

            foreach (Hull hull in Hull.hullList)
            {
                if (hull.Rect.Height < 150)
                {
                    continue;
                }

                WayPoint prevWaypoint = null;

                if (hull.Rect.Width < minDist * 3.0f)
                {
                    new WayPoint(
                        new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);
                    continue;
                }

                for (float x = hull.Rect.X + minDist; x <= hull.Rect.Right - minDist; x += minDist)
                {
                    var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);

                    if (prevWaypoint != null)
                    {
                        wayPoint.ConnectTo(prevWaypoint);
                    }

                    prevWaypoint = wayPoint;
                }
            }

            float outSideWaypointInterval = 200.0f;
            int   outsideWaypointDist     = 100;

            Rectangle borders = Hull.GetBorders();

            borders.X -= outsideWaypointDist;
            borders.Y += outsideWaypointDist;

            borders.Width  += outsideWaypointDist * 2;
            borders.Height += outsideWaypointDist * 2;

            borders.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition);

            if (borders.Width <= outSideWaypointInterval * 2)
            {
                borders.Inflate(outSideWaypointInterval * 2 - borders.Width, 0);
            }

            if (borders.Height <= outSideWaypointInterval * 2)
            {
                int inflateAmount = (int)(outSideWaypointInterval * 2) - borders.Height;
                borders.Y += inflateAmount / 2;

                borders.Height += inflateAmount;
            }

            WayPoint[,] cornerWaypoint = new WayPoint[2, 2];

            for (int i = 0; i < 2; i++)
            {
                for (float x = borders.X + outSideWaypointInterval; x < borders.Right - outSideWaypointInterval; x += outSideWaypointInterval)
                {
                    var wayPoint = new WayPoint(
                        new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
                        SpawnType.Path, submarine);

                    if (x == borders.X + outSideWaypointInterval)
                    {
                        cornerWaypoint[i, 0] = wayPoint;
                    }
                    else
                    {
                        wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count - 2]);
                    }
                }

                cornerWaypoint[i, 1] = WayPoint.WayPointList[WayPointList.Count - 1];
            }

            for (int i = 0; i < 2; i++)
            {
                WayPoint wayPoint = null;
                for (float y = borders.Y - borders.Height; y < borders.Y; y += outSideWaypointInterval)
                {
                    wayPoint = new WayPoint(
                        new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
                        SpawnType.Path, submarine);

                    if (y == borders.Y - borders.Height)
                    {
                        wayPoint.ConnectTo(cornerWaypoint[1, i]);
                    }
                    else
                    {
                        wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count - 2]);
                    }
                }

                wayPoint.ConnectTo(cornerWaypoint[0, i]);
            }

            List <Structure> stairList = new List <Structure>();

            foreach (MapEntity me in mapEntityList)
            {
                Structure stairs = me as Structure;
                if (stairs == null)
                {
                    continue;
                }

                if (stairs.StairDirection != Direction.None)
                {
                    stairList.Add(stairs);
                }
            }

            foreach (Structure stairs in stairList)
            {
                WayPoint[] stairPoints = new WayPoint[3];

                stairPoints[0] = new WayPoint(
                    new Vector2(stairs.Rect.X - 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? 80 : stairs.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);

                stairPoints[1] = new WayPoint(
                    new Vector2(stairs.Rect.Right + 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? stairs.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);

                for (int i = 0; i < 2; i++)
                {
                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = stairPoints[i].FindClosest(dir, true, new Vector2(-30.0f, 30f));
                        if (closest == null)
                        {
                            continue;
                        }
                        stairPoints[i].ConnectTo(closest);
                    }
                }

                stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine);
                stairPoints[0].ConnectTo(stairPoints[2]);
                stairPoints[2].ConnectTo(stairPoints[1]);
            }

            foreach (Item item in Item.ItemList)
            {
                var ladders = item.GetComponent <Items.Components.Ladder>();
                if (ladders == null)
                {
                    continue;
                }

                WayPoint[] ladderPoints = new WayPoint[2];

                ladderPoints[0] = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height + heightFromFloor), SpawnType.Path, submarine);
                ladderPoints[1] = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);

                WayPoint prevPoint = ladderPoints[0];
                Vector2  prevPos   = prevPoint.SimPosition;

                List <Body> ignoredBodies = new List <Body>();

                while (prevPoint != ladderPoints[1])
                {
                    var pickedBody = Submarine.PickBody(prevPos, ladderPoints[1].SimPosition, ignoredBodies);

                    if (pickedBody == null)
                    {
                        break;
                    }

                    ignoredBodies.Add(pickedBody);

                    if (pickedBody.UserData is Item)
                    {
                        var door = ((Item)pickedBody.UserData).GetComponent <Door>();
                        if (door != null)
                        {
                            WayPoint newPoint = new WayPoint(door.Item.Position, SpawnType.Path, submarine);
                            newPoint.Ladders      = ladders;
                            newPoint.ConnectedGap = door.LinkedGap;

                            newPoint.ConnectTo(prevPoint);

                            prevPoint = newPoint;

                            prevPos = ConvertUnits.ToSimUnits(door.Item.Position - Vector2.UnitY * door.Item.Rect.Height);
                        }
                        else
                        {
                            prevPos = Submarine.LastPickedPosition;
                        }
                    }
                    else
                    {
                        prevPos = Submarine.LastPickedPosition;
                    }
                }

                prevPoint.ConnectTo(ladderPoints[1]);



                //for (float y = ladderPoints[0].Position.Y+100.0f; y < ladderPoints[1].Position.Y; y+=100.0f )
                //{
                //    var midPoint = new WayPoint(new Vector2(item.Rect.Center.X, y), SpawnType.Path, Submarine.Loaded);
                //    midPoint.Ladders = ladders;

                //    midPoint.ConnectTo(prevPoint);
                //    prevPoint = midPoint;
                //}
                //ladderPoints[1].ConnectTo(prevPoint);

                for (int i = 0; i < 2; i++)
                {
                    ladderPoints[i].Ladders = ladders;

                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = ladderPoints[i].FindClosest(dir, true, new Vector2(-150.0f, 10f));
                        if (closest == null)
                        {
                            continue;
                        }
                        ladderPoints[i].ConnectTo(closest);
                    }
                }

                //ladderPoints[0].ConnectTo(ladderPoints[1]);
            }

            foreach (Gap gap in Gap.GapList)
            {
                if (!gap.isHorizontal)
                {
                    continue;
                }

                //too small to walk through
                if (gap.Rect.Height < 150.0f)
                {
                    continue;
                }

                var wayPoint = new WayPoint(
                    new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor), SpawnType.Path, submarine, gap);

                for (int dir = -1; dir <= 1; dir += 2)
                {
                    float tolerance = gap.IsRoomToRoom ? 50.0f : outSideWaypointInterval / 2.0f;

                    WayPoint closest = wayPoint.FindClosest(
                        dir, true, new Vector2(-tolerance, tolerance),
                        gap.ConnectedDoor == null ? null : gap.ConnectedDoor.Body.FarseerBody);

                    if (closest != null)
                    {
                        wayPoint.ConnectTo(closest);
                    }
                }
            }

            foreach (Gap gap in Gap.GapList)
            {
                if (gap.isHorizontal || gap.IsRoomToRoom)
                {
                    continue;
                }

                //too small to walk through
                if (gap.Rect.Width < 100.0f)
                {
                    continue;
                }

                var wayPoint = new WayPoint(
                    new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2), SpawnType.Path, submarine, gap);

                for (int dir = -1; dir <= 1; dir += 2)
                {
                    WayPoint closest = wayPoint.FindClosest(dir, false, new Vector2(-outSideWaypointInterval, outSideWaypointInterval) / 2.0f);
                    if (closest == null)
                    {
                        continue;
                    }
                    wayPoint.ConnectTo(closest);
                }
            }

            var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && !w.linkedTo.Any());

            foreach (WayPoint wp in orphans)
            {
                wp.Remove();
            }
        }
Example #15
0
        public static bool GenerateSubWaypoints(Submarine submarine)
        {
            if (!Hull.hullList.Any())
            {
                DebugConsole.ThrowError("Couldn't generate waypoints: no hulls found.");
                return(false);
            }

            List <WayPoint> existingWaypoints = WayPointList.FindAll(wp => wp.spawnType == SpawnType.Path);

            foreach (WayPoint wayPoint in existingWaypoints)
            {
                wayPoint.Remove();
            }

            //find all open doors and temporarily activate their bodies to prevent visibility checks
            //from ignoring the doors and generating waypoint connections that go straight through the door
            List <Door> openDoors = new List <Door>();

            foreach (Item item in Item.ItemList)
            {
                var door = item.GetComponent <Door>();
                if (door != null && !door.Body.Enabled)
                {
                    openDoors.Add(door);
                    door.Body.Enabled = true;
                }
            }

            float diffFromHullEdge = 50;
            float minDist          = 100.0f;
            float heightFromFloor  = 110.0f;
            float hullMinHeight    = 100;

            foreach (Hull hull in Hull.hullList)
            {
                // Ignore hulls that a human couldn't fit in.
                // Doesn't take multi-hull rooms into account, but it's probably best to leave them to be setup manually.
                if (hull.Rect.Height < hullMinHeight)
                {
                    continue;
                }
                // Do five raycasts to check if there's a floor. Don't create waypoints unless we can find a floor.
                Body floor = null;
                for (int i = 0; i < 5; i++)
                {
                    float horizontalOffset = 0;
                    switch (i)
                    {
                    case 1:
                        horizontalOffset = hull.RectWidth * 0.2f;
                        break;

                    case 2:
                        horizontalOffset = hull.RectWidth * 0.4f;
                        break;

                    case 3:
                        horizontalOffset = -hull.RectWidth * 0.2f;
                        break;

                    case 4:
                        horizontalOffset = -hull.RectWidth * 0.4f;
                        break;
                    }
                    horizontalOffset = ConvertUnits.ToSimUnits(horizontalOffset);
                    Vector2 floorPos = new Vector2(hull.SimPosition.X + horizontalOffset, ConvertUnits.ToSimUnits(hull.Rect.Y - hull.RectHeight - 50));
                    floor = Submarine.PickBody(new Vector2(hull.SimPosition.X + horizontalOffset, hull.SimPosition.Y), floorPos, collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform, customPredicate: f => !(f.Body.UserData is Submarine));
                    if (floor != null)
                    {
                        break;
                    }
                }
                if (floor == null)
                {
                    continue;
                }
                float waypointHeight = hull.Rect.Height > heightFromFloor * 2 ? heightFromFloor : hull.Rect.Height / 2;
                if (hull.Rect.Width < diffFromHullEdge * 3.0f)
                {
                    new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
                }
                else
                {
                    WayPoint prevWaypoint = null;
                    for (float x = hull.Rect.X + diffFromHullEdge; x <= hull.Rect.Right - diffFromHullEdge; x += minDist)
                    {
                        var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
                        if (prevWaypoint != null)
                        {
                            wayPoint.ConnectTo(prevWaypoint);
                        }
                        prevWaypoint = wayPoint;
                    }
                    if (prevWaypoint == null)
                    {
                        // Ensure that we always create at least one waypoint per hull.
                        new WayPoint(new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + waypointHeight), SpawnType.Path, submarine);
                    }
                }
            }

            // Platforms
            foreach (Structure platform in Structure.WallList)
            {
                if (!platform.IsPlatform)
                {
                    continue;
                }
                float    waypointHeight = heightFromFloor;
                WayPoint prevWaypoint   = null;
                for (float x = platform.Rect.X + diffFromHullEdge; x <= platform.Rect.Right - diffFromHullEdge; x += minDist)
                {
                    WayPoint wayPoint = new WayPoint(new Vector2(x, platform.Rect.Y + waypointHeight), SpawnType.Path, submarine);
                    if (prevWaypoint != null)
                    {
                        wayPoint.ConnectTo(prevWaypoint);
                    }
                    // If the waypoint is close to hull waypoints, remove it.
                    if (wayPoint != null)
                    {
                        for (int dir = -1; dir <= 1; dir += 2)
                        {
                            if (wayPoint.FindClosest(dir, horizontalSearch: true, tolerance: new Vector2(minDist, heightFromFloor), ignored: prevWaypoint.ToEnumerable()) != null)
                            {
                                wayPoint.Remove();
                                wayPoint = null;
                                break;
                            }
                        }
                    }
                    prevWaypoint = wayPoint;
                }
            }

            float outSideWaypointInterval = 100.0f;

            if (submarine.Info.Type != SubmarineType.OutpostModule)
            {
                List <(WayPoint, int)> outsideWaypoints = new List <(WayPoint, int)>();

                Rectangle borders        = Hull.GetBorders();
                int       originalWidth  = borders.Width;
                int       originalHeight = borders.Height;
                borders.X        -= Math.Min(500, originalWidth / 4);
                borders.Y        += Math.Min(500, originalHeight / 4);
                borders.Width    += Math.Min(1500, originalWidth / 2);
                borders.Height   += Math.Min(1000, originalHeight / 2);
                borders.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition);

                if (borders.Width <= outSideWaypointInterval * 2)
                {
                    borders.Inflate(outSideWaypointInterval * 2 - borders.Width, 0);
                }

                if (borders.Height <= outSideWaypointInterval * 2)
                {
                    int inflateAmount = (int)(outSideWaypointInterval * 2) - borders.Height;
                    borders.Y      += inflateAmount / 2;
                    borders.Height += inflateAmount;
                }

                WayPoint[,] cornerWaypoint = new WayPoint[2, 2];
                for (int i = 0; i < 2; i++)
                {
                    for (float x = borders.X + outSideWaypointInterval; x < borders.Right - outSideWaypointInterval; x += outSideWaypointInterval)
                    {
                        var wayPoint = new WayPoint(
                            new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
                            SpawnType.Path, submarine);

                        outsideWaypoints.Add((wayPoint, i));

                        if (x == borders.X + outSideWaypointInterval)
                        {
                            cornerWaypoint[i, 0] = wayPoint;
                        }
                        else
                        {
                            wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]);
                        }
                    }

                    cornerWaypoint[i, 1] = WayPointList[WayPointList.Count - 1];
                }

                for (int i = 0; i < 2; i++)
                {
                    WayPoint wayPoint = null;
                    for (float y = borders.Y - borders.Height; y < borders.Y; y += outSideWaypointInterval)
                    {
                        wayPoint = new WayPoint(
                            new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
                            SpawnType.Path, submarine);

                        outsideWaypoints.Add((wayPoint, i));

                        if (y == borders.Y - borders.Height)
                        {
                            wayPoint.ConnectTo(cornerWaypoint[1, i]);
                        }
                        else
                        {
                            wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]);
                        }
                    }

                    wayPoint.ConnectTo(cornerWaypoint[0, i]);
                }

                Vector2 center     = ConvertUnits.ToSimUnits(submarine.HiddenSubPosition);
                float   halfHeight = ConvertUnits.ToSimUnits(borders.Height / 2);
                // Try to move the waypoints so that they are near the walls, roughly following the shape of the sub.
                foreach (var wayPoint in outsideWaypoints)
                {
                    WayPoint wp        = wayPoint.Item1;
                    float    xDiff     = center.X - wp.SimPosition.X;
                    Vector2  targetPos = new Vector2(center.X - xDiff * 0.5f, center.Y);
                    Body     wall      = Submarine.PickBody(wp.SimPosition, targetPos, collisionCategory: Physics.CollisionWall, customPredicate: f => !(f.Body.UserData is Submarine));
                    if (wall == null)
                    {
                        // Try again, and shoot to the center now. It happens with some subs that the first, offset raycast don't hit the walls.
                        targetPos = new Vector2(center.X - xDiff, center.Y);
                        wall      = Submarine.PickBody(wp.SimPosition, targetPos, collisionCategory: Physics.CollisionWall, customPredicate: f => !(f.Body.UserData is Submarine));
                    }
                    if (wall != null)
                    {
                        float distanceFromWall = 1;
                        if (xDiff > 0 && !submarine.Info.HasTag(SubmarineTag.Shuttle))
                        {
                            // We don't want to move the waypoints near the tail too close to the engine.
                            float yDist = Math.Abs(center.Y - wp.SimPosition.Y);
                            distanceFromWall = MathHelper.Lerp(1, 3, MathUtils.InverseLerp(halfHeight, 0, yDist));
                        }
                        Vector2 newPos = Submarine.LastPickedPosition + Submarine.LastPickedNormal * distanceFromWall;
                        wp.rect = new Rectangle(ConvertUnits.ToDisplayUnits(newPos).ToPoint(), wp.rect.Size);
                        wp.FindHull();
                    }
                }
                // Remove unwanted points
                var      removals = new List <WayPoint>();
                WayPoint previous = null;
                float    tooClose = outSideWaypointInterval / 2;
                foreach (var wayPoint in outsideWaypoints)
                {
                    WayPoint wp = wayPoint.Item1;
                    if (wp.CurrentHull != null ||
                        Submarine.PickBody(wp.SimPosition, wp.SimPosition + Vector2.Normalize(center - wp.SimPosition) * 0.1f, collisionCategory: Physics.CollisionWall | Physics.CollisionItem, customPredicate: f => !(f.Body.UserData is Submarine), allowInsideFixture: true) != null)
                    {
                        // Remove waypoints that got inside/too near the sub.
                        removals.Add(wp);
                        previous = wp;
                        continue;
                    }
                    foreach (var otherWayPoint in outsideWaypoints)
                    {
                        WayPoint otherWp = otherWayPoint.Item1;
                        if (otherWp == wp)
                        {
                            continue;
                        }
                        if (removals.Contains(otherWp))
                        {
                            continue;
                        }
                        float sqrDist = Vector2.DistanceSquared(wp.Position, otherWp.Position);
                        // Remove waypoints that are too close to each other.
                        if (!removals.Contains(previous) && sqrDist < tooClose * tooClose)
                        {
                            removals.Add(wp);
                        }
                    }
                    previous = wp;
                }
                foreach (WayPoint wp in removals)
                {
                    outsideWaypoints.RemoveAll(w => w.Item1 == wp);
                    wp.Remove();
                }
                for (int i = 0; i < outsideWaypoints.Count; i++)
                {
                    WayPoint current = outsideWaypoints[i].Item1;
                    if (current.linkedTo.Count > 1)
                    {
                        continue;
                    }
                    WayPoint next           = null;
                    int      maxConnections = 2;
                    float    tooFar         = outSideWaypointInterval * 5;
                    for (int j = 0; j < maxConnections; j++)
                    {
                        if (current.linkedTo.Count >= maxConnections)
                        {
                            break;
                        }
                        tooFar /= current.linkedTo.Count;
                        next    = current.FindClosestOutside(outsideWaypoints, tolerance: tooFar, filter: wp => wp.Item1 != next && wp.Item1.linkedTo.None(e => current.linkedTo.Contains(e)) && wp.Item1.linkedTo.Count < 2 && wp.Item2 < i);
                        if (next != null)
                        {
                            current.ConnectTo(next);
                        }
                    }
                }
            }

            List <Structure> stairList = new List <Structure>();

            foreach (MapEntity me in mapEntityList)
            {
                if (!(me is Structure stairs))
                {
                    continue;
                }

                if (stairs.StairDirection != Direction.None)
                {
                    stairList.Add(stairs);
                }
            }

            foreach (Structure stairs in stairList)
            {
                WayPoint[] stairPoints = new WayPoint[3];

                stairPoints[0] = new WayPoint(
                    new Vector2(stairs.Rect.X - 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? 80 : stairs.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);

                stairPoints[1] = new WayPoint(
                    new Vector2(stairs.Rect.Right + 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? stairs.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);

                for (int i = 0; i < 2; i++)
                {
                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = stairPoints[i].FindClosest(dir, horizontalSearch: true, new Vector2(100, 70));
                        if (closest == null)
                        {
                            continue;
                        }
                        stairPoints[i].ConnectTo(closest);
                    }
                }

                stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine);
                stairPoints[0].ConnectTo(stairPoints[2]);
                stairPoints[2].ConnectTo(stairPoints[1]);
            }

            foreach (Item item in Item.ItemList)
            {
                var ladders = item.GetComponent <Ladder>();
                if (ladders == null)
                {
                    continue;
                }

                Vector2         bottomPoint  = new Vector2(item.Rect.Center.X, item.Rect.Top - item.Rect.Height + 10);
                List <WayPoint> ladderPoints = new List <WayPoint>
                {
                    new WayPoint(bottomPoint, SpawnType.Path, submarine),
                };

                List <Body> ignoredBodies = new List <Body>();
                // Lowest point is only meaningful for hanging ladders inside the sub, but it shouldn't matter in other cases either.
                // Start point is where the bots normally grasp the ladder when they stand on ground.
                WayPoint lowestPoint = ladderPoints[0];
                WayPoint prevPoint   = lowestPoint;
                Vector2  prevPos     = prevPoint.SimPosition;
                Body     ground      = Submarine.PickBody(lowestPoint.SimPosition, lowestPoint.SimPosition - Vector2.UnitY, ignoredBodies,
                                                          collisionCategory: Physics.CollisionWall | Physics.CollisionPlatform | Physics.CollisionStairs,
                                                          customPredicate: f => !(f.Body.UserData is Submarine));
                float startHeight = ground != null?ConvertUnits.ToDisplayUnits(ground.Position.Y) : bottomPoint.Y;

                startHeight += heightFromFloor;
                WayPoint startPoint = lowestPoint;
                Vector2  nextPos    = new Vector2(item.Rect.Center.X, startHeight);
                // Don't create the start point if it's too close to the lowest point or if it's outside of the sub.
                // If we skip creating the start point, the lowest point is used instead.
                if (lowestPoint == null || Math.Abs(startPoint.Position.Y - startHeight) > 40 && Hull.FindHull(nextPos) != null)
                {
                    startPoint = new WayPoint(nextPos, SpawnType.Path, submarine);
                    ladderPoints.Add(startPoint);
                    if (lowestPoint != null)
                    {
                        startPoint.ConnectTo(lowestPoint);
                    }
                    prevPoint = startPoint;
                    prevPos   = prevPoint.SimPosition;
                }
                for (float y = startPoint.Position.Y + LadderWaypointInterval; y < item.Rect.Y - 1.0f; y += LadderWaypointInterval)
                {
                    //first check if there's a door in the way
                    //(we need to create a waypoint linked to the door for NPCs to open it)
                    Body pickedBody = Submarine.PickBody(
                        ConvertUnits.ToSimUnits(new Vector2(startPoint.Position.X, y)),
                        prevPos, ignoredBodies, Physics.CollisionWall, false,
                        (Fixture f) => f.Body.UserData is Item && ((Item)f.Body.UserData).GetComponent <Door>() != null);

                    Door pickedDoor = null;
                    if (pickedBody != null)
                    {
                        pickedDoor = (pickedBody?.UserData as Item).GetComponent <Door>();
                    }
                    else
                    {
                        //no door, check for walls
                        pickedBody = Submarine.PickBody(
                            ConvertUnits.ToSimUnits(new Vector2(startPoint.Position.X, y)), prevPos, ignoredBodies, null, false,
                            (Fixture f) => f.Body.UserData is Structure);
                    }

                    if (pickedBody == null)
                    {
                        prevPos = Submarine.LastPickedPosition;
                        continue;
                    }
                    else
                    {
                        ignoredBodies.Add(pickedBody);
                    }

                    if (pickedDoor != null)
                    {
                        WayPoint newPoint = new WayPoint(pickedDoor.Item.Position, SpawnType.Path, submarine);
                        ladderPoints.Add(newPoint);
                        newPoint.ConnectedGap = pickedDoor.LinkedGap;
                        newPoint.ConnectTo(prevPoint);
                        prevPoint = newPoint;
                        prevPos   = new Vector2(prevPos.X, ConvertUnits.ToSimUnits(pickedDoor.Item.Position.Y - pickedDoor.Item.Rect.Height));
                    }
                    else
                    {
                        WayPoint newPoint = new WayPoint(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * heightFromFloor, SpawnType.Path, submarine);
                        ladderPoints.Add(newPoint);
                        newPoint.ConnectTo(prevPoint);
                        prevPoint = newPoint;
                        prevPos   = ConvertUnits.ToSimUnits(newPoint.Position);
                    }
                }

                // Cap
                if (prevPoint.rect.Y < item.Rect.Y - 40)
                {
                    WayPoint wayPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);
                    ladderPoints.Add(wayPoint);
                    wayPoint.ConnectTo(prevPoint);
                }

                // Connect ladder waypoints to hull points at the right and left side
                foreach (WayPoint ladderPoint in ladderPoints)
                {
                    ladderPoint.Ladders = ladders;
                    bool isHatch = ladderPoint.ConnectedGap != null && !ladderPoint.ConnectedGap.IsRoomToRoom;
                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = null;
                        if (isHatch)
                        {
                            closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null, ignored: ladderPoints);
                        }
                        else
                        {
                            closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 100), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
                        }
                        if (closest == null)
                        {
                            continue;
                        }
                        ladderPoint.ConnectTo(closest);
                    }
                }
            }

            // Another pass: connect cap and bottom points with other ladders when they are vertically adjacent to another (double ladders)
            foreach (Item item in Item.ItemList)
            {
                var ladders = item.GetComponent <Ladder>();
                if (ladders == null)
                {
                    continue;
                }
                var      wps   = WayPointList.Where(wp => wp.Ladders == ladders).OrderByDescending(wp => wp.Rect.Y);
                WayPoint cap   = wps.First();
                WayPoint above = cap.FindClosest(1, horizontalSearch: false, tolerance: new Vector2(25, 50), filter: wp => wp.Ladders != null && wp.Ladders != ladders);
                above?.ConnectTo(cap);
                WayPoint bottom = wps.Last();
                WayPoint below  = bottom.FindClosest(-1, horizontalSearch: false, tolerance: new Vector2(25, 50), filter: wp => wp.Ladders != null && wp.Ladders != ladders);
                below?.ConnectTo(bottom);
            }

            foreach (Gap gap in Gap.GapList)
            {
                if (gap.IsHorizontal)
                {
                    // Too small to walk through
                    if (gap.Rect.Height < hullMinHeight)
                    {
                        continue;
                    }
                    Vector2 pos      = new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor);
                    var     wayPoint = new WayPoint(pos, SpawnType.Path, submarine, gap);
                    // The closest waypoint can be quite far if the gap is at an exterior door.
                    Vector2 tolerance = gap.IsRoomToRoom ? new Vector2(150, 70) : new Vector2(1000, 1000);
                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = wayPoint.FindClosest(dir, horizontalSearch: true, tolerance, gap.ConnectedDoor?.Body.FarseerBody);
                        if (closest != null)
                        {
                            wayPoint.ConnectTo(closest);
                        }
                    }
                }
                else
                {
                    // Create waypoints on vertical gaps on the outer walls, also hatches.
                    if (gap.IsRoomToRoom || gap.linkedTo.None(l => l is Hull))
                    {
                        continue;
                    }
                    // Too small to swim through
                    if (gap.Rect.Width < 50.0f)
                    {
                        continue;
                    }
                    Vector2 pos = new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2);
                    // Some hatches are created in the block above where we handle the ladder waypoints. So we need to check for duplicates.
                    if (WayPointList.Any(wp => wp.ConnectedGap == gap))
                    {
                        continue;
                    }
                    var      wayPoint      = new WayPoint(pos, SpawnType.Path, submarine, gap);
                    Hull     connectedHull = (Hull)gap.linkedTo.First(l => l is Hull);
                    int      dir           = Math.Sign(connectedHull.Position.Y - gap.Position.Y);
                    WayPoint closest       = wayPoint.FindClosest(dir, horizontalSearch: false, new Vector2(50, 100));
                    if (closest != null)
                    {
                        wayPoint.ConnectTo(closest);
                    }
                    for (dir = -1; dir <= 1; dir += 2)
                    {
                        closest = wayPoint.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), gap.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null);
                        if (closest != null)
                        {
                            wayPoint.ConnectTo(closest);
                        }
                    }
                }
            }

            var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && w.linkedTo.None());

            foreach (WayPoint wp in orphans)
            {
                wp.Remove();
            }

            foreach (WayPoint wp in WayPointList)
            {
                if (wp.CurrentHull == null && wp.Ladders == null && wp.linkedTo.Count < 2)
                {
                    DebugConsole.ThrowError($"Couldn't automatically link the waypoint {wp.ID} outside of the submarine. You should do it manually. The waypoint ID is shown in red color.");
                }
            }

            //re-disable the bodies of the doors that are supposed to be open
            foreach (Door door in openDoors)
            {
                door.Body.Enabled = false;
            }

            return(true);
        }
Example #16
0
        public void Update(float deltaTime)
        {
            if (Body.FarseerBody.IsStatic)
            {
                return;
            }

            ClientUpdatePosition(deltaTime);
            if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
            {
                return;
            }

            //if outside left or right edge of the level
            if (Position.X < 0 || Position.X > Level.Loaded.Size.X)
            {
                Rectangle worldBorders = Borders;
                worldBorders.Location += MathUtils.ToPoint(Position);

                //push the sub back below the upper "barrier" of the level
                if (worldBorders.Y > Level.Loaded.Size.Y)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Min(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.Size.Y - worldBorders.Y)));
                }
                else if (worldBorders.Y - worldBorders.Height < Level.Loaded.BottomPos)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Max(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.BottomPos - (worldBorders.Y - worldBorders.Height))));
                }
            }

            //-------------------------

            Vector2 totalForce = CalculateBuoyancy();

            if (Body.LinearVelocity.LengthSquared() > 0.0001f)
            {
                //TODO: sync current drag with clients?
                float     attachedMass = 0.0f;
                JointEdge jointEdge    = Body.FarseerBody.JointList;
                while (jointEdge != null)
                {
                    Body      otherBody = jointEdge.Joint.BodyA == Body.FarseerBody ? jointEdge.Joint.BodyB : jointEdge.Joint.BodyA;
                    Character character = (otherBody.UserData as Limb)?.character;
                    if (character != null)
                    {
                        attachedMass += character.Mass;
                    }

                    jointEdge = jointEdge.Next;
                }

                float horizontalDragCoefficient = MathHelper.Clamp(HorizontalDrag + attachedMass / 5000.0f, 0.0f, MaxDrag);
                totalForce.X -= Math.Sign(Body.LinearVelocity.X) * Body.LinearVelocity.X * Body.LinearVelocity.X * horizontalDragCoefficient * Body.Mass;

                float verticalDragCoefficient = MathHelper.Clamp(VerticalDrag + attachedMass / 5000.0f, 0.0f, MaxDrag);
                totalForce.Y -= Math.Sign(Body.LinearVelocity.Y) * Body.LinearVelocity.Y * Body.LinearVelocity.Y * verticalDragCoefficient * Body.Mass;
            }

            ApplyForce(totalForce);

            UpdateDepthDamage(deltaTime);
        }
Example #17
0
        public void Update(float deltaTime)
        {
            if (GameMain.Client != null)
            {
                if (memPos.Count == 0)
                {
                    return;
                }

                Vector2 newVelocity = Body.LinearVelocity;
                Vector2 newPosition = Body.SimPosition;

                Body.CorrectPosition(memPos, deltaTime, out newVelocity, out newPosition);
                Vector2 moveAmount = ConvertUnits.ToDisplayUnits(newPosition - Body.SimPosition);
                newVelocity = newVelocity.ClampLength(100.0f);
                if (!MathUtils.IsValid(newVelocity))
                {
                    return;
                }

                List <Submarine> subsToMove = submarine.GetConnectedSubs();
                foreach (Submarine dockedSub in subsToMove)
                {
                    if (dockedSub == submarine)
                    {
                        continue;
                    }
                    //clear the position buffer of the docked subs to prevent unnecessary position corrections
                    dockedSub.SubBody.memPos.Clear();
                }

                Submarine closestSub = null;
                if (Character.Controlled == null)
                {
                    closestSub = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);
                }
                else
                {
                    closestSub = Character.Controlled.Submarine;
                }

                bool displace = moveAmount.Length() > 100.0f;
                foreach (Submarine sub in subsToMove)
                {
                    sub.PhysicsBody.SetTransform(sub.PhysicsBody.SimPosition + ConvertUnits.ToSimUnits(moveAmount), 0.0f);
                    sub.PhysicsBody.LinearVelocity = newVelocity;

                    if (displace)
                    {
                        sub.SubBody.DisplaceCharacters(moveAmount);
                    }
                }

                if (closestSub != null && subsToMove.Contains(closestSub))
                {
                    GameMain.GameScreen.Cam.Position += moveAmount;
                    if (GameMain.GameScreen.Cam.TargetPos != Vector2.Zero)
                    {
                        GameMain.GameScreen.Cam.TargetPos += moveAmount;
                    }

                    if (Character.Controlled != null)
                    {
                        Character.Controlled.CursorPosition += moveAmount;
                    }
                }

                return;
            }

            //if outside left or right edge of the level
            if (Position.X < 0 || Position.X > Level.Loaded.Size.X)
            {
                Rectangle worldBorders = Borders;
                worldBorders.Location += MathUtils.ToPoint(Position);

                //push the sub back below the upper "barrier" of the level
                if (worldBorders.Y > Level.Loaded.Size.Y)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Min(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.Size.Y - worldBorders.Y)));
                }
                else if (worldBorders.Y - worldBorders.Height < Level.Loaded.BottomPos)
                {
                    Body.LinearVelocity = new Vector2(
                        Body.LinearVelocity.X,
                        Math.Max(Body.LinearVelocity.Y, ConvertUnits.ToSimUnits(Level.Loaded.BottomPos - (worldBorders.Y - worldBorders.Height))));
                }
            }

            //-------------------------

            Vector2 totalForce = CalculateBuoyancy();

            if (Body.LinearVelocity.LengthSquared() > 0.000001f)
            {
                float dragCoefficient = 0.01f;

                float speedLength = (Body.LinearVelocity == Vector2.Zero) ? 0.0f : Body.LinearVelocity.Length();
                float drag        = speedLength * speedLength * dragCoefficient * Body.Mass;

                totalForce += -Vector2.Normalize(Body.LinearVelocity) * drag;
            }

            ApplyForce(totalForce);

            UpdateDepthDamage(deltaTime);
        }
Example #18
0
        public static void GenerateSubWaypoints(Submarine submarine)
        {
            if (!Hull.hullList.Any())
            {
                DebugConsole.ThrowError("Couldn't generate waypoints: no hulls found.");
                return;
            }

            List <WayPoint> existingWaypoints = WayPointList.FindAll(wp => wp.spawnType == SpawnType.Path);

            foreach (WayPoint wayPoint in existingWaypoints)
            {
                wayPoint.Remove();
            }

            //find all open doors and temporarily activate their bodies to prevent visibility checks
            //from ignoring the doors and generating waypoint connections that go straight through the door
            List <Door> openDoors = new List <Door>();

            foreach (Item item in Item.ItemList)
            {
                var door = item.GetComponent <Door>();
                if (door != null && !door.Body.Enabled)
                {
                    openDoors.Add(door);
                    door.Body.Enabled = true;
                }
            }


            float minDist         = 150.0f;
            float heightFromFloor = 110.0f;

            foreach (Hull hull in Hull.hullList)
            {
                if (hull.Rect.Height < 150)
                {
                    continue;
                }

                WayPoint prevWaypoint = null;

                if (hull.Rect.Width < minDist * 3.0f)
                {
                    new WayPoint(
                        new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);
                    continue;
                }

                for (float x = hull.Rect.X + minDist; x <= hull.Rect.Right - minDist; x += minDist)
                {
                    var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine);

                    if (prevWaypoint != null)
                    {
                        wayPoint.ConnectTo(prevWaypoint);
                    }

                    prevWaypoint = wayPoint;
                }
            }

            float outSideWaypointInterval = 200.0f;
            int   outsideWaypointDist     = 100;

            Rectangle borders = Hull.GetBorders();

            borders.X -= outsideWaypointDist;
            borders.Y += outsideWaypointDist;

            borders.Width  += outsideWaypointDist * 2;
            borders.Height += outsideWaypointDist * 2;

            borders.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition);

            if (borders.Width <= outSideWaypointInterval * 2)
            {
                borders.Inflate(outSideWaypointInterval * 2 - borders.Width, 0);
            }

            if (borders.Height <= outSideWaypointInterval * 2)
            {
                int inflateAmount = (int)(outSideWaypointInterval * 2) - borders.Height;
                borders.Y += inflateAmount / 2;

                borders.Height += inflateAmount;
            }

            WayPoint[,] cornerWaypoint = new WayPoint[2, 2];

            for (int i = 0; i < 2; i++)
            {
                for (float x = borders.X + outSideWaypointInterval; x < borders.Right - outSideWaypointInterval; x += outSideWaypointInterval)
                {
                    var wayPoint = new WayPoint(
                        new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition,
                        SpawnType.Path, submarine);

                    if (x == borders.X + outSideWaypointInterval)
                    {
                        cornerWaypoint[i, 0] = wayPoint;
                    }
                    else
                    {
                        wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]);
                    }
                }

                cornerWaypoint[i, 1] = WayPointList[WayPointList.Count - 1];
            }

            for (int i = 0; i < 2; i++)
            {
                WayPoint wayPoint = null;
                for (float y = borders.Y - borders.Height; y < borders.Y; y += outSideWaypointInterval)
                {
                    wayPoint = new WayPoint(
                        new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition,
                        SpawnType.Path, submarine);

                    if (y == borders.Y - borders.Height)
                    {
                        wayPoint.ConnectTo(cornerWaypoint[1, i]);
                    }
                    else
                    {
                        wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count - 2]);
                    }
                }

                wayPoint.ConnectTo(cornerWaypoint[0, i]);
            }

            List <Structure> stairList = new List <Structure>();

            foreach (MapEntity me in mapEntityList)
            {
                Structure stairs = me as Structure;
                if (stairs == null)
                {
                    continue;
                }

                if (stairs.StairDirection != Direction.None)
                {
                    stairList.Add(stairs);
                }
            }

            foreach (Structure stairs in stairList)
            {
                WayPoint[] stairPoints = new WayPoint[3];

                stairPoints[0] = new WayPoint(
                    new Vector2(stairs.Rect.X - 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? 80 : stairs.Rect.Height) + heightFromFloor), SpawnType.Path, submarine);

                stairPoints[1] = new WayPoint(
                    new Vector2(stairs.Rect.Right + 32.0f,
                                stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? stairs.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine);

                for (int i = 0; i < 2; i++)
                {
                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = stairPoints[i].FindClosest(dir, true, new Vector2(-30.0f, 30f));
                        if (closest == null)
                        {
                            continue;
                        }
                        stairPoints[i].ConnectTo(closest);
                    }
                }

                stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine);
                stairPoints[0].ConnectTo(stairPoints[2]);
                stairPoints[2].ConnectTo(stairPoints[1]);
            }

            foreach (Item item in Item.ItemList)
            {
                var ladders = item.GetComponent <Ladder>();
                if (ladders == null)
                {
                    continue;
                }

                List <WayPoint> ladderPoints = new List <WayPoint>();
                ladderPoints.Add(new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height + heightFromFloor), SpawnType.Path, submarine));

                WayPoint    prevPoint     = ladderPoints[0];
                Vector2     prevPos       = prevPoint.SimPosition;
                List <Body> ignoredBodies = new List <Body>();

                for (float y = ladderPoints[0].Position.Y + 100.0f; y < item.Rect.Y - 1.0f; y += 100.0f)
                {
                    //first check if there's a door in the way
                    //(we need to create a waypoint linked to the door for NPCs to open it)
                    Body pickedBody = Submarine.PickBody(
                        ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)),
                        prevPos, ignoredBodies, Physics.CollisionWall, false,
                        (Fixture f) => f.Body.UserData is Item && ((Item)f.Body.UserData).GetComponent <Door>() != null);

                    Door pickedDoor = null;
                    if (pickedBody != null)
                    {
                        pickedDoor = (pickedBody?.UserData as Item).GetComponent <Door>();
                    }
                    else
                    {
                        //no door, check for walls
                        pickedBody = Submarine.PickBody(
                            ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)), prevPos, ignoredBodies, null, false);
                    }

                    if (pickedBody == null)
                    {
                        prevPos = Submarine.LastPickedPosition;
                        continue;
                    }
                    else
                    {
                        ignoredBodies.Add(pickedBody);
                    }


                    if (pickedDoor != null)
                    {
                        WayPoint newPoint = new WayPoint(pickedDoor.Item.Position, SpawnType.Path, submarine);
                        ladderPoints.Add(newPoint);
                        newPoint.ConnectedGap = pickedDoor.LinkedGap;
                        newPoint.ConnectTo(prevPoint);
                        prevPoint = newPoint;
                        prevPos   = new Vector2(prevPos.X, ConvertUnits.ToSimUnits(pickedDoor.Item.Position.Y - pickedDoor.Item.Rect.Height));
                    }
                    else
                    {
                        WayPoint newPoint = new WayPoint(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * heightFromFloor, SpawnType.Path, submarine);
                        ladderPoints.Add(newPoint);
                        newPoint.ConnectTo(prevPoint);
                        prevPoint = newPoint;
                        prevPos   = ConvertUnits.ToSimUnits(newPoint.Position);
                    }
                }

                if (prevPoint.rect.Y < item.Rect.Y - 10.0f)
                {
                    WayPoint newPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);
                    ladderPoints.Add(newPoint);
                    newPoint.ConnectTo(prevPoint);
                }

                //connect ladder waypoints to hull points at the right and left side
                foreach (WayPoint ladderPoint in ladderPoints)
                {
                    ladderPoint.Ladders = ladders;
                    //don't connect if the waypoint is at a gap (= at the boundary of hulls and/or at a hatch)
                    if (ladderPoint.ConnectedGap != null)
                    {
                        continue;
                    }

                    for (int dir = -1; dir <= 1; dir += 2)
                    {
                        WayPoint closest = ladderPoint.FindClosest(dir, true, new Vector2(-150.0f, 10f));
                        if (closest == null)
                        {
                            continue;
                        }
                        ladderPoint.ConnectTo(closest);
                    }
                }
            }

            foreach (Gap gap in Gap.GapList)
            {
                if (!gap.IsHorizontal)
                {
                    continue;
                }

                //too small to walk through
                if (gap.Rect.Height < 150.0f)
                {
                    continue;
                }

                var wayPoint = new WayPoint(
                    new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor), SpawnType.Path, submarine, gap);

                for (int dir = -1; dir <= 1; dir += 2)
                {
                    float tolerance = gap.IsRoomToRoom ? 50.0f : outSideWaypointInterval / 2.0f;

                    WayPoint closest = wayPoint.FindClosest(
                        dir, true, new Vector2(-tolerance, tolerance),
                        gap.ConnectedDoor?.Body.FarseerBody);

                    if (closest != null)
                    {
                        wayPoint.ConnectTo(closest);
                    }
                }
            }

            foreach (Gap gap in Gap.GapList)
            {
                if (gap.IsHorizontal || gap.IsRoomToRoom || !gap.linkedTo.Any(l => l is Hull))
                {
                    continue;
                }

                //too small to walk through
                if (gap.Rect.Width < 100.0f)
                {
                    continue;
                }

                var wayPoint = new WayPoint(
                    new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2), SpawnType.Path, submarine, gap);

                float tolerance     = outSideWaypointInterval / 2.0f;
                Hull  connectedHull = (Hull)gap.linkedTo.First(l => l is Hull);
                int   dir           = Math.Sign(connectedHull.Position.Y - gap.Position.Y);

                WayPoint closest = wayPoint.FindClosest(
                    dir, false, new Vector2(-tolerance, tolerance),
                    gap.ConnectedDoor?.Body.FarseerBody);

                if (closest != null)
                {
                    wayPoint.ConnectTo(closest);
                }
            }

            var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && !w.linkedTo.Any());

            foreach (WayPoint wp in orphans)
            {
                wp.Remove();
            }

            //re-disable the bodies of the doors that are supposed to be open
            foreach (Door door in openDoors)
            {
                door.Body.Enabled = false;
            }
        }