public MovePart(MoveUnderground move, WPos from, WPos to, int fromFacing, int toFacing, int startingFraction)
            {
                Move = move;

                From              = from;
                To                = to;
                FromFacing        = fromFacing;
                ToFacing          = toFacing;
                moveFraction      = startingFraction;
                MoveFractionTotal = (to - from).Length;
                IsInterruptible   = false; // See comments in Move.Cancel()

                // Calculate an elliptical arc that joins from and to
                var delta = Util.NormalizeFacing(fromFacing - toFacing);

                if (delta != 0 && delta != 128)
                {
                    // The center of rotation is where the normal vectors cross
                    var u = new WVec(1024, 0, 0).Rotate(WRot.FromFacing(fromFacing));
                    var v = new WVec(1024, 0, 0).Rotate(WRot.FromFacing(toFacing));
                    var w = from - to;
                    var s = (v.Y * w.X - v.X * w.Y) * 1024 / (v.X * u.Y - v.Y * u.X);
                    var x = from.X + s * u.X / 1024;
                    var y = from.Y + s * u.Y / 1024;

                    ArcCenter     = new WPos(x, y, 0);
                    ArcFromLength = (ArcCenter - from).HorizontalLength;
                    ArcFromAngle  = (ArcCenter - from).Yaw;
                    ArcToLength   = (ArcCenter - to).HorizontalLength;
                    ArcToAngle    = (ArcCenter - to).Yaw;
                    EnableArc     = true;
                }
            }
 protected override MovePart OnComplete(Actor self, MobileUnderground mobile, MoveUnderground parent)
 {
     mobile.SetPosition(self, mobile.ToCell);
     return(null);
 }
 public MoveSecondHalf(MoveUnderground move, WPos from, WPos to, int fromFacing, int toFacing, int startingFraction)
     : base(move, from, to, fromFacing, toFacing, startingFraction)
 {
 }
            protected override MovePart OnComplete(Actor self, MobileUnderground mobile, MoveUnderground parent)
            {
                var map = self.World.Map;
                var fromSubcellOffset = map.Grid.OffsetOfSubCell(mobile.FromSubCell);
                var toSubcellOffset   = map.Grid.OffsetOfSubCell(mobile.ToSubCell);

                if (!IsCanceled || self.Location.Layer == CustomMovementLayerType.Tunnel)
                {
                    var nextCell = parent.PopPath(self);
                    if (nextCell != null)
                    {
                        if (IsTurn(mobile, nextCell.Value.First))
                        {
                            var nextSubcellOffset = map.Grid.OffsetOfSubCell(nextCell.Value.Second);
                            var ret = new MoveFirstHalf(
                                Move,
                                Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
                                Util.BetweenCells(self.World, mobile.ToCell, nextCell.Value.First) + (toSubcellOffset + nextSubcellOffset) / 2,
                                mobile.Facing,
                                Util.GetNearestFacing(mobile.Facing, map.FacingBetween(mobile.ToCell, nextCell.Value.First, mobile.Facing)),
                                moveFraction - MoveFractionTotal);

                            mobile.FinishedMoving(self);
                            mobile.SetLocation(mobile.ToCell, mobile.ToSubCell, nextCell.Value.First, nextCell.Value.Second);
                            return(ret);
                        }

                        parent.path.Add(nextCell.Value.First);
                    }
                }

                var toPos = mobile.ToCell.Layer == 0 ? map.CenterOfCell(mobile.ToCell) :
                            self.World.GetCustomMovementLayers()[mobile.ToCell.Layer].CenterOfCell(mobile.ToCell);

                var ret2 = new MoveSecondHalf(
                    Move,
                    Util.BetweenCells(self.World, mobile.FromCell, mobile.ToCell) + (fromSubcellOffset + toSubcellOffset) / 2,
                    toPos + toSubcellOffset,
                    mobile.Facing,
                    mobile.Facing,
                    moveFraction - MoveFractionTotal);

                mobile.EnteringCell(self);
                mobile.SetLocation(mobile.ToCell, mobile.ToSubCell, mobile.ToCell, mobile.ToSubCell);
                return(ret2);
            }
 protected abstract MovePart OnComplete(Actor self, MobileUnderground mobile, MoveUnderground parent);