private void WriteGenericSegment(VMIPathSegment seg, BinaryWriter writer)
 {
     if (seg is VMPathLineSegment)
     {
         writer.Write((byte)0);
         var line = (VMPathLineSegment)seg;
         WritePoint(line.From, writer);
         WritePoint(line.To, writer);
     }
     else if (seg is VMPathBezierSegment)
     {
         writer.Write((byte)1);
         var bezier = (VMPathBezierSegment)seg;
         WritePoint(bezier.A, writer);
         WritePoint(bezier.B, writer);
         WritePoint(bezier.C, writer);
         WritePoint(bezier.D, writer);
     }
     else
     {
         writer.Write((byte)2);
     }
 }
        public override void Deserialize(BinaryReader reader)
        {
            base.Deserialize(reader);
            var bezierRouting = (Version > 31);

            var roomN = reader.ReadInt32();

            Rooms = new VMRoomPortal[roomN];
            for (int i = 0; i < roomN; i++)
            {
                Rooms[i] = new VMRoomPortal(reader);
            }

            if (reader.ReadBoolean())
            {
                CurrentPortal = new VMRoomPortal(reader);
            }

            var wtLen = reader.ReadInt32();

            Point[] points = null;
            if (wtLen > -1)
            {
                WalkTo = new VMIPathSegment[wtLen];
                if (bezierRouting)
                {
                    for (int i = 0; i < wtLen; i++)
                    {
                        WalkTo[i] = ReadGenericSegment(reader);
                    }
                }
                else
                {
                    //old point list. convert to line segments.
                    points = new Point[wtLen];
                    for (int i = 0; i < wtLen; i++)
                    {
                        points[i] = new Point(reader.ReadInt32(), reader.ReadInt32());
                    }
                }
            }

            WalkDirection   = reader.ReadDouble();
            TargetDirection = reader.ReadDouble();
            IgnoreRooms     = reader.ReadBoolean();

            State       = (VMRoutingFrameState)reader.ReadByte();
            PortalTurns = reader.ReadInt32();
            WaitTime    = reader.ReadInt32();
            Timeout     = reader.ReadInt32();
            Retries     = reader.ReadInt32();

            AttemptedChair = reader.ReadBoolean();
            TurnTweak      = reader.ReadSingle();
            TurnFrames     = reader.ReadInt32();

            MoveTotalFrames = reader.ReadInt32();
            MoveFrames      = reader.ReadInt32();
            Velocity        = reader.ReadInt32();

            CallFailureTrees = reader.ReadBoolean();

            var igrN = reader.ReadInt32();

            IgnoredRooms = new VMRoomPortal[igrN];
            for (int i = 0; i < igrN; i++)
            {
                IgnoredRooms[i] = new VMRoomPortal(reader);
            }

            var avaN = reader.ReadInt32();

            AvatarsToConsider = new short[avaN];
            for (int i = 0; i < avaN; i++)
            {
                AvatarsToConsider[i] = reader.ReadInt16();
            }

            LotTilePos CurrentWaypoint = new LotTilePos();

            PreviousPosition.Deserialize(reader);
            if (bezierRouting)
            {
                CurrentPath = ReadGenericSegment(reader);
            }
            else
            {
                //convert old format into new
                CurrentWaypoint.Deserialize(reader);
                CurrentPath = new VMPathLineSegment(
                    new Point(PreviousPosition.x * 0x8000, PreviousPosition.y * 0x8000),
                    new Point(CurrentWaypoint.x * 0x8000, CurrentWaypoint.y * 0x8000));
                if (points != null)
                {
                    if (points.Length > 0)
                    {
                        WalkTo    = new VMIPathSegment[points.Length];
                        WalkTo[0] = new VMPathLineSegment(
                            new Point(CurrentWaypoint.x * 0x8000, CurrentWaypoint.y * 0x8000),
                            new Point(points[0].X * 0x8000, points[0].Y * 0x8000));
                        for (int i = 1; i < WalkTo.Length; i++)
                        {
                            WalkTo[i] = new VMPathLineSegment(
                                new Point(points[i - 1].X * 0x8000, points[i - 1].Y * 0x8000),
                                new Point(points[i].X * 0x8000, points[i].Y * 0x8000));
                        }
                    }
                    else
                    {
                        WalkTo = new VMIPathSegment[0];
                    }
                }
                else
                {
                    WalkTo = null;
                }
            }

            CurrentPath.CalculateTotalFrames();
            CurrentPath.UpdateTotalFrames(MoveTotalFrames);
            CurrentPath.ResetToFrame(MoveFrames);

            RoomRouteInvalid = reader.ReadBoolean();

            if (reader.ReadBoolean())
            {
                Slot = SLOTItemSerializer.Deserialize(reader);
            }
            Target = reader.ReadInt16();

            var chLen = reader.ReadInt32();

            if (chLen > -1)
            {
                Choices = new VMFindLocationResultMarshal[chLen];
                for (int i = 0; i < chLen; i++)
                {
                    Choices[i] = new VMFindLocationResultMarshal();
                    Choices[i].Deserialize(reader);
                }
            }

            if (reader.ReadBoolean())
            {
                CurRoute = new VMFindLocationResultMarshal();
                CurRoute.Deserialize(reader);
            }

            if (Version > 29)
            {
                LastWalkStyle = reader.ReadInt16();
            }
        }
        public static LinkedList <VMIPathSegment> GeneratePath(LinkedList <VMWalkableRect> rects, Point dest, float?dirIn, float?dirOut)
        {
            if (rects == null)
            {
                return(null);
            }
            var            result  = new LinkedList <VMIPathSegment>();
            VMWalkableRect last    = null;
            VMWalkableRect last2   = null;
            VMIPathSegment lastSeg = null;

            dest = new Point(dest.X * 0x8000, dest.Y * 0x8000);

            int            lineProcess  = -1;
            VMWalkableRect lineStart    = null;
            VMWalkableRect preLineStart = null;

            foreach (var rect in rects)
            {
                if (last != null)
                {
                    if (lineProcess != -1 || last.LineID != -1)
                    {
                        if (lineProcess == -1)
                        {
                            //
                            lineStart    = last;
                            preLineStart = last2;
                            lineProcess  = last.LineID;
                        }
                        if (rect.LineID != lineProcess)
                        {
                            var seg2 = new VMPathLineSegment(lineStart.ParentSourceHiP, rect.ParentSourceHiP);
                            //var seg2 = new VMPathBezierSegment() { A = lineStart.ParentSourceHiP, D = rect.ParentSourceHiP };
                            //seg2.B = seg2.A;
                            //seg2.C = seg2.D;
                            GenerateBezierControl(lastSeg, seg2, preLineStart ?? lineStart, lineStart);
                            result.AddLast(seg2);
                            lastSeg     = seg2;
                            lineProcess = -1;
                        }
                        last2 = last;
                        last  = rect;
                        continue;
                    }
                    if (rect.ParentSourceHiP == last.ParentSourceHiP)
                    {
                        last = rect;
                        continue; //there's not any value in this line, just skip it
                    }
                    //new line from
                    var seg = new VMPathBezierSegment()
                    {
                        A = last.ParentSourceHiP, D = rect.ParentSourceHiP
                    };
                    if (lastSeg == null)
                    {
                        //starting.
                        if (dirIn != null)
                        {
                            //we've been instructed to set up a basic control point for entry.
                            //right now rather weak
                            var dirVec = new Vector2((float)Math.Sin(dirIn.Value), (float)-Math.Cos(dirIn.Value)) * (6 * 0x8000);
                            var dirPt  = seg.A + dirVec.ToPoint();
                            seg.B = last.ClosestHiP(dirPt.X, dirPt.Y);
                        }
                        else
                        {
                            //initialize control point as empty
                            seg.B = seg.A;
                        }
                    }
                    else
                    {
                        //initialize our first control point and the last segment's second
                        //based on the angle between each line, within rectangle limits.
                        //these should be symmetrical
                        GenerateBezierControl(lastSeg, seg, last2, last);
                    }
                    //line between the two ParentSource points
                    result.AddLast(seg);
                    lastSeg = seg;
                }
                last2 = last;
                last  = rect;
            }

            //finally, a line to the destination point.
            if (last != null)
            {
                var seg = new VMPathBezierSegment()
                {
                    A = last.ParentSourceHiP, C = dest, D = dest
                };
                if (dirOut != null)
                {
                    var dirVec = new Vector2((float)Math.Sin(dirOut.Value), (float)-Math.Cos(dirOut.Value)) * (16 * 0x8000);
                    var dirPt  = seg.D - dirVec.ToPoint();
                    seg.C = last.ClosestHiP(dirPt.X, dirPt.Y);
                }

                if (last2 == null)
                {
                    //starting.
                    if (dirIn != null)
                    {
                        //we've been instructed to set up a basic control point for entry.
                        //right now rather weak
                        var dirVec = new Vector2((float)Math.Sin(dirIn.Value), (float)-Math.Cos(dirIn.Value)) * (6 * 0x8000);
                        var dirPt  = seg.A + dirVec.ToPoint();
                        seg.B = last.ClosestHiP(dirPt.X, dirPt.Y);
                    }
                    else
                    {
                        //initialize control point as empty
                        seg.B = seg.A;
                    }
                }
                else
                {
                    //initialize our first control point and the last segment's second
                    //based on the angle between each line, within rectangle limits.
                    //these should be symmetrical
                    GenerateBezierControl(lastSeg, seg, last2, last);
                }
                //line between the two ParentSource points
                result.AddLast(seg);
            }
            return(result);
        }
        // STATIC

        private static void GenerateBezierControl(VMIPathSegment fromSegI, VMIPathSegment toSegI,
                                                  VMWalkableRect from, VMWalkableRect to)
        {
            //find average direction line

            var fromSlice = new VMObstacle(from.x1 * 0x8000, from.y1 * 0x8000, from.x2 * 0x8000, from.y2 * 0x8000); //new VMObstacle(fromSegI.Source, fromSegI.Destination);
            var toSlice   = new VMObstacle(to.x1 * 0x8000, to.y1 * 0x8000, to.x2 * 0x8000, to.y2 * 0x8000);         //new VMObstacle(toSegI.Source, toSegI.Destination);

            if (fromSegI is VMPathBezierSegment)
            {
                var fromSeg = (VMPathBezierSegment)fromSegI;
                if (toSegI is VMPathBezierSegment)
                {
                    var toSeg    = (VMPathBezierSegment)toSegI;
                    var fromDiff = (fromSeg.D - fromSeg.A).ToVector2();
                    var toDiff   = (toSeg.D - toSeg.A).ToVector2();

                    //essentially normalizing, but keeping the lengths stored
                    var fromLength = fromDiff.Length();
                    var toLength   = toDiff.Length();
                    fromDiff /= fromLength;
                    toDiff   /= toLength;

                    //our control points should create a line with average direction between its bordering lines.
                    var avg = fromDiff + toDiff;
                    avg.Normalize();

                    var controlStrength = Math.Min(fromLength, toLength) / 2;
                    toSeg.B   = toSeg.A + (avg * toLength / 3).ToPoint();
                    fromSeg.C = fromSeg.D - (avg * fromLength / 3).ToPoint();

                    int  changed = 2;
                    bool testTo  = true;

                    int emergency = 0;
                    while (emergency++ < 1000 && changed-- > 0)
                    {
                        if (testTo)
                        {
                            //make sure to is within rect bounds
                            var n = toSlice.Closest(toSeg.B.X, toSeg.B.Y);
                            if (n != toSeg.B)
                            {
                                //if we changed, we'll need to check the other side again.
                                changed = 1;

                                //multiply the other side by the change factors here
                                var diff  = (toSeg.B - toSeg.A);
                                var diff2 = (n - toSeg.A);

                                var otherDiff = (fromSeg.C - fromSeg.D);
                                if (diff.X != 0)
                                {
                                    otherDiff.X = (int)(((long)otherDiff.X * diff2.X) / diff.X);
                                }
                                if (diff.Y != 0)
                                {
                                    otherDiff.Y = (int)(((long)otherDiff.Y * diff2.Y) / diff.Y);
                                }

                                toSeg.B = n;

                                fromSeg.C = fromSeg.D + otherDiff;
                            }
                        }
                        else
                        {
                            //make sure from is within rect bounds
                            var n = fromSlice.Closest(fromSeg.C.X, fromSeg.C.Y);
                            if (n != fromSeg.C)
                            {
                                //if we changed, we'll need to check the other side again.
                                changed = 1;

                                //multiply the other side by the change factors here
                                var diff  = (fromSeg.C - fromSeg.D);
                                var diff2 = (n - fromSeg.D);

                                var otherDiff = (toSeg.B - toSeg.A);
                                if (diff.X != 0)
                                {
                                    otherDiff.X = (int)(((long)otherDiff.X * diff2.X) / diff.X);
                                }
                                if (diff.Y != 0)
                                {
                                    otherDiff.Y = (int)(((long)otherDiff.Y * diff2.Y) / diff.Y);
                                }

                                fromSeg.C = n;
                                toSeg.B   = toSeg.A + otherDiff;
                            }
                        }
                        testTo = !testTo;
                    }
                }
                else
                {
                    var fromDiff = (fromSeg.D - fromSeg.A).ToVector2();
                    var toDiff   = (toSegI.Destination - toSegI.Source).ToVector2();

                    //essentially normalizing, but keeping the lengths stored
                    var fromLength = fromDiff.Length();
                    var toLength   = toDiff.Length();
                    fromDiff /= fromLength;
                    toDiff   /= toLength;

                    //our control points should create a line with average direction between its bordering lines.
                    fromSeg.C = fromSeg.D - (toDiff * fromLength / 3).ToPoint();
                    fromSeg.C = fromSlice.Closest(fromSeg.C.X, fromSeg.C.Y);
                }
            }
            else if (toSegI is VMPathBezierSegment)
            {
                var toSeg    = (VMPathBezierSegment)toSegI;
                var fromDiff = (fromSegI.Destination - fromSegI.Source).ToVector2();
                var toDiff   = (toSeg.D - toSeg.A).ToVector2();

                //essentially normalizing, but keeping the lengths stored
                var fromLength = fromDiff.Length();
                var toLength   = toDiff.Length();
                fromDiff /= fromLength;
                toDiff   /= toLength;

                //our control points should create a line with average direction between its bordering lines.
                toSeg.B = toSeg.A + (fromDiff * toLength / 3).ToPoint();
                toSeg.B = toSlice.Closest(toSeg.B.X, toSeg.B.Y);
            }
        }