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); } }