using CommonAI.RTS;
using CommonLang.Vector;
using CommonAI.Zone;
using CommonAI.Zone.Helper;
using CommonLang;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonLang.Log;

namespace CommonAI.ZoneClient
{
    public class ZoneSpell : ZoneObject
    {
        public readonly SpellTemplate Info;

        public override string Name { get { return null; } }
        public override string DisplayName { get { return Info.Name; } }
        public override float X { get { return mLocalPos.X; } }
        public override float Y { get { return mLocalPos.Y; } }
        public override float Z { get { return mLocal_Z; } }
        public override float RadiusSize { get { return mDisplaySize; } }

        public ZoneObject Sender { get; internal set; }
        public ZoneUnit Launcher { get; internal set; }
        public ZoneUnit Target { get; internal set; }
        public Vector2 TargetPos { get; internal set; }
        public Vector2 StartPos { get { return mStartPos; } }

        public float LaunchHeight { get { return mStartHeight; } set { this.mStartHeight = value; this.mLocal_Z = value; } }

        public float BodySize { get { return mDisplaySize; } }
        public float Distance { get { return mDisplayDistance; } }
        public int PassTimeMS { get { return mPassTimeMS; } }

        private Vector2 mLocalPos = new Vector2();
        private float mLocal_Z = 0;
        private float mSizeLimit, mDisplaySize;
        private float mDistanceLimit, mDisplayDistance;
        private Vector2 mStartPos = new Vector2();
        private float mStartDirection;
        private float mStartHeight;
        private int mPassTimeMS;
        private float mSpeed;

        private float mMoveDistance;
        private int mCurveMissileIndex = -1;

        private PopupKeyFrames<SpellTemplate.KeyFrame> mKeyFrames;
        private TimeInterval<SpellTemplate.KeyFrame> mHitIntervalTicker;

        private AddSpellEvent mAddEvent;

        public System.Action OnTargetChanged = null;

        private static HashMap<string, int> hashCurveMissileCnt = new HashMap<string, int>();
        private string strCurveMissileIndexKey;
        public ZoneSpell(SpellTemplate info, SyncSpellInfo syn, ZoneLayer parent, AddSpellEvent add)
            : base(syn.ObjectID, parent)
        {
            this.Info = info;

			this.mStartPos.SetX(syn.x);
			this.mStartPos.SetY(syn.y);
			this.mPos.SetX(syn.x);
			this.mPos.SetY(syn.y);
			this.mLocalPos.SetX(syn.x);
            this.mLocalPos.SetY(syn.y);
            this.mDirection = this.mStartDirection = syn.direction;
            this.mDistanceLimit = this.mDisplayDistance = info.Distance;
            this.mSizeLimit = this.mDisplaySize = info.BodySize;
            this.mPassTimeMS = 0;
            this.mAddEvent = add;
            if (add != null && add.IsHeight)
            {
                this.mLocal_Z = add.height;
            }
            this.mKeyFrames = new PopupKeyFrames<SpellTemplate.KeyFrame>();
            this.mKeyFrames.AddRange(info.KeyFrames);
            this.mHitIntervalTicker = new TimeInterval<SpellTemplate.KeyFrame>(info.HitIntervalMS);
            this.mHitIntervalTicker.Tag = info.HitIntervalKeyFrame;
            this.mSpeed = info.MSpeedSEC;
            this.mMoveDistance = 0;
            if (syn.HasSpeed)
            {
                this.mSpeed = syn.CurSpeed;
            }
            if(info.MType == SpellTemplate.MotionType.CurveMissile)
            {
                int cnt;
                strCurveMissileIndexKey = add.sender_unit_id.ToString() + ":" + info.TemplateID.ToString();
                if (hashCurveMissileCnt.TryGetValue(strCurveMissileIndexKey, out cnt))
                {
                    hashCurveMissileCnt[strCurveMissileIndexKey] = ++cnt;
                }
                else
                {
                    hashCurveMissileCnt.Add(strCurveMissileIndexKey, 1);
                    cnt = 1;
                }
                mCurveMissileIndex = cnt-1;
            }
        }

        protected override void Disposing()
        {
            if (!string.IsNullOrEmpty(strCurveMissileIndexKey))
            {
                int cnt;
                if (hashCurveMissileCnt.TryGetValue(strCurveMissileIndexKey, out cnt))
                {
                    if (cnt <= 1)
                    {
                        hashCurveMissileCnt.Remove(strCurveMissileIndexKey);
                    }
                    else
                    {
                        hashCurveMissileCnt[strCurveMissileIndexKey] = cnt - 1;
                    }
                }
            }
            base.Disposing();
        }

        protected internal override void OnAdded()
        {
            base.OnAdded();

            float radius = 0;
            if (mAddEvent != null)
            {
                if (mAddEvent.LaunchData.FromUnitBody && (Sender is ZoneUnit))
                {
                    radius = (Sender as ZoneUnit).Info.LaunchSpellRadius;
                }
                else
                {
                    radius = mAddEvent.LaunchData.LaunchSpellRadius;
                }
            }

            if (!Info.IsLaunchSpellEventSyncPos)
            {
                switch (Info.MType)
                {
                    case SpellTemplate.MotionType.SelectTarget:
                        if (Target != null)
                        {
                            mStartPos.SetX(Target.X);
                            mStartPos.SetY(Target.Y);
                            mLocal_Z = Target.Z;
                            mStartDirection = Target.ServerDirection;
                        }
                        MathVector.movePolar(mStartPos, mStartDirection, radius);
                        break;
                    case SpellTemplate.MotionType.SelectLauncher:
                        if (Launcher != null)
                        {
                            mStartPos.SetX(Launcher.X);
                            mStartPos.SetY(Launcher.Y);
                            mLocal_Z = Launcher.Z;
                            mStartDirection = Launcher.ServerDirection;
                        }
                        MathVector.movePolar(mStartPos, mStartDirection, radius);
                        break;
                    case SpellTemplate.MotionType.Chain:
                        if (Sender != null)
                        {
                            mStartPos.SetX(Sender.X);
                            mStartPos.SetY(Sender.Y);
                            mLocal_Z = Sender.Z;
                        }
                        break;

                    case SpellTemplate.MotionType.AOE_Binding:
                    case SpellTemplate.MotionType.Binding:
                        if (Sender != null)
                        {
                            mStartPos.SetX(Sender.X);
                            mStartPos.SetY(Sender.Y);
                            mLocal_Z = Sender.Z;
                        }
                        break;
                    case SpellTemplate.MotionType.AOE_BindingTarget:
                    case SpellTemplate.MotionType.BindingTarget:
                        if (Target != null)
                        {
                            mStartPos.SetX(Target.X);
                            mStartPos.SetY(Target.Y);
                            mLocal_Z = Target.Z;
                        }
                        break;
                }
				this.mPos.SetX(this.mStartPos.X);
				this.mPos.SetY(this.mStartPos.Y);
				this.mLocalPos.SetX(this.mStartPos.X);
                this.mLocalPos.SetY(this.mStartPos.Y);
                this.mDirection = this.mStartDirection;
            }
            if (Info.TargetEffect != null)
            {
                if (Target != null)
                {
                    Parent.PreQueueEvent(new UnitEffectEvent(Target.ObjectID, Info.TargetEffect));
                }
                else if (TargetPos != null)
                {
                    Parent.PreQueueEvent(new AddEffectEvent(0,TargetPos.X, TargetPos.Y, mDirection, Info.TargetEffect));
                }
            }
        }

        public override void ForceSyncPos(float x, float y)
        {
			this.mPos.SetX(x);
			this.mPos.SetY(y);
			this.mLocalPos.SetX(x);
			this.mLocalPos.SetY(y);
		}

        internal protected override void DoEvent(ObjectEvent e)
        {
            if (e is SpellLockTargetEvent)
            {
                doSpellLockTargetEvent(e as SpellLockTargetEvent);
            }
            else if (e is ObjectForceSyncPosEvent)
            {
                var oe = e as ObjectForceSyncPosEvent;
                this.ForceSyncPos(oe.X, oe.Y);
                this.Direction = oe.Direction;
            }
        }
        private void doSpellLockTargetEvent(SpellLockTargetEvent e)
        {
            if (Parent == null)
            {
                log.Error("doSpellLockTargetEvent -> Parent == null");
            }
            if (Info == null)
            {
                log.Error("doSpellLockTargetEvent -> Info == null");
            }
            if (e == null)
            {
                log.Error("doSpellLockTargetEvent -> e == null");
            }
            this.Target = Parent.GetUnit(e.target_obj_id);
            if(OnTargetChanged != null)
            {
                OnTargetChanged();
            }
            switch (Info.MType)
            {
                case SpellTemplate.MotionType.SeekerSelectTarget:
                    if (Target != null)
                    {
                        this.mLocalPos.SetX(Target.X);
                        this.mLocalPos.SetY(Target.Y);
                        this.mLocal_Z = Target.Z;
                    }
                    break;
            }
        }

        protected internal override void UpdateAI()
        {
        }

        internal protected override void Update()
        {
            int intervalMS = Parent.CurrentIntervalMS;
            mPassTimeMS += intervalMS;

            if (Parent.ActorSyncMode != SyncMode.ForceByServer && (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance))
            {
                updateMotion(intervalMS);
            }
            updateZPos();

            switch (Info.MType)
            {
                case SpellTemplate.MotionType.AOE:
                case SpellTemplate.MotionType.AOE_Binding:
                case SpellTemplate.MotionType.AOE_BindingTarget:
                    updateAOE(intervalMS);
                    break;
            }
            switch (Info.BodyShape)
            {
                case SpellTemplate.Shape.StripRayTouchEnd:
                    updateRayTouchEnd();
                    break;
                case SpellTemplate.Shape.LineToStart:
                case SpellTemplate.Shape.LineToTarget:
                    updateLineToTarget();
                    break;
            }
            if (Info.IsMoveable && (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance))
            {
                MoveHelper.UpdateSpeed(intervalMS, ref mSpeed, Info.MSpeedAdd, Info.MSpeedAcc);
                MoveHelper.UpdateMoveDistance(intervalMS, ref mMoveDistance, mSpeed);
            }
            updateKeyFrames();
        }

        public override void SyncPos(ref SyncPosEvent.UnitPos pos)
        {
            base.SyncPos(ref pos);
            if (Parent.ActorSyncMode == SyncMode.ForceByServer)
            {
                mLocalPos.SetX(pos.X);
                mLocalPos.SetY(pos.Y);
                if (Parent.IsSyncZ) { this.Z = pos.Z; }
            }
        }

        //-----------------------------------------------------------------------
        #region _UpdateMotions_

        float foxfireRoundAngle = 0f;

        float foxfireSeekWait = 0f;

        private Logger log = LoggerFactory.GetLogger("ZoneClient");

        /// <summary>
        /// 更新移动行为
        /// </summary>
        private void updateMotion(int intervalMS)
        {
            float speed_distance = MoveHelper.GetDistance(intervalMS, mSpeed);
            try
            {
                switch (Info.MType)
                {
                    case SpellTemplate.MotionType.SelectLauncher:
                    case SpellTemplate.MotionType.SelectTarget:
                        break;

                    case SpellTemplate.MotionType.Cannon:
                    case SpellTemplate.MotionType.Straight:
                        MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        break;
                    case SpellTemplate.MotionType.StraightAndStop:
                        if (PassTimeMS < Info.BoomerangFlyTime)
                        {
                            MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        }
                        //else if (PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime)
                        //{
                        //    PreFlyTo(Sender.X, Sender.Y, mSpeed * 1.8f, intervalMS);
                        //}
                        break;
                    case SpellTemplate.MotionType.Boomerang1:
                        if (PassTimeMS < Info.BoomerangFlyTime)
                        {
                            MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        }
                        else if (PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime)
                        {
                            PreFlyTo(Sender.X, Sender.Y, mSpeed * 1.4f, intervalMS);
                        }
                        break;
                    case SpellTemplate.MotionType.Boomerang2:
                        if (PassTimeMS < Info.BoomerangFlyTime)
                        {
                            MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        }
                        else if (PassTimeMS > Info.BoomerangFlyTime + Info.BoomerangHangtime)
                        {
                            PreFlyTo(mStartPos.X, mStartPos.Y, mSpeed * 1.4f, intervalMS);
                        }
                        break;
                    case SpellTemplate.MotionType.Immovability:
                    case SpellTemplate.MotionType.AOE:
						mLocalPos.SetX(mPos.X);
                        mLocalPos.SetY(mPos.Y);
                        break;


                    case SpellTemplate.MotionType.AOE_Binding:
                    case SpellTemplate.MotionType.Binding:
                        if (Sender != null)
                        {
                            updateBinding(Sender.X, Sender.Y, Sender.Direction, intervalMS);
                        }
                        else
                        {
                            adjustPos(speed_distance);
                        }
                        break;
                    case SpellTemplate.MotionType.AOE_BindingTarget:
                    case SpellTemplate.MotionType.BindingTarget:
                        if (Target != null)
                        {
                            updateBinding(Target.X, Target.Y, Target.Direction, intervalMS);
                        }
                        else
                        {
                            adjustPos(speed_distance);
                        }
                        break;

                    case SpellTemplate.MotionType.Missile:
					case SpellTemplate.MotionType.MissileAttackRoute:
						if (Target != null)
                        {
                            if (Info.RotateSpeedSEC == 0)
                            {
                                PreFaceTo(Target.X, Target.Y);
                            }
                            PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS);
                        }
                        else
                        {
                            MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        }
                        break;
                    case SpellTemplate.MotionType.SeekerMissile:
                        if (Target != null)
                        {
                            if (Info.SeekingTurningAngleSEC != 0)
                            {
                                FlyToTargetTunning(Target.X, Target.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS);
                            }
                            else
                            {
                                PreFaceTo(Target.X, Target.Y);
                                PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS);
                            }
                        }
                        else
                        {
                            MathVector.movePolar(mLocalPos, mStartDirection, speed_distance);
                        }
                        break;
                    case SpellTemplate.MotionType.SeekerSelectTarget:
                        break;
                    case SpellTemplate.MotionType.Chain:
                        if (Sender != null && Sender.IsEnable && Target != null && Target.IsActive)
                        {
                            mLocalPos.SetX(Sender.X);
                            mLocalPos.SetY(Sender.Y);
                            PreFaceTo(Target.X, Target.Y);
                        }
                        break;
					case SpellTemplate.MotionType.CurveMissile:
						IPosBase targetPos = Target == null ? (IPosBase)TargetPos : (IPosBase)Target;
						if(targetPos == null)
						{
							PreFlyTo(mStartDirection, mSpeed, intervalMS);
						}
						else if (Info.SeekingTurningAngleSEC != 0)
						{
							FlyToTargetTunning(targetPos.X, targetPos.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS);
						}
						else
						{
							PreFaceTo(targetPos.X, targetPos.Y);
							PreFlyTo(targetPos.X, targetPos.Y, mSpeed, intervalMS);
						}
						break;
					case SpellTemplate.MotionType.Foxfire:
                        if (Target == null)
                        {
                            var mLaunchData = mAddEvent.LaunchData;
                            foxfireRoundAngle = mStartDirection + PassTimeMS * 0.001f * 3;
                            var x = (float)(Math.Cos(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius);
                            var y = (float)(Math.Sin(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius);
                            if (Sender != null)
                            {
                                x = Sender.X + x;
                                y = Sender.Y + y;
                            }
                            else
                            {
                                x = mStartPos.X + x;
                                y = mStartPos.Y + y;
                            }
                            PreFlyTo(x, y, mSpeed, intervalMS);
                            if (Info.RotateSpeedSEC == 0)
                            {
                                PreFaceTo(foxfireRoundAngle + (float)Math.PI * 0.25f);
                            }
                            foxfireSeekWait += intervalMS;
                        }
                        else
                        {
                            if (Info.SeekingTurningAngleSEC != 0)
                            {
                                FlyToTargetTunning(Target.X, Target.Y, mSpeed, Info.SeekingTurningAngleSEC, intervalMS);
                            }
                            else
                            {
                                PreFaceTo(Target.X, Target.Y);
                                PreFlyTo(Target.X, Target.Y, mSpeed, intervalMS);
                            }
                        }
                        break;
                }
                if (Info.BodyShape == SpellTemplate.Shape.LineToTarget)
                {
                    if (Target != null)
                    {
                        PreFaceTo(Target.X, Target.Y);
                    }
                }
                else if (Info.BodyShape == SpellTemplate.Shape.LineToStart)
                {
                    PreFaceTo(StartPos.X, StartPos.Y);
                }
                else if (Info.RotateSpeedSEC != 0 && Info.MType != SpellTemplate.MotionType.SeekerMissile)
                {
                    this.mDirection += MoveHelper.GetDistance(intervalMS, Info.RotateSpeedSEC);
                }
            }
            catch (Exception e) {
                log.Error(e.Message + "\n" + "Info.MType = " + Info.MType + "  Info.BodyShape = " + Info.BodyShape);
            }
            
        }

        private void updateZPos()
        {
            switch (Info.MType)
            {
                case SpellTemplate.MotionType.Immovability:
                case SpellTemplate.MotionType.SelectTarget:
                case SpellTemplate.MotionType.SelectLauncher:
                case SpellTemplate.MotionType.AOE:
                    updateFloatingZ(mStartHeight);
                    break;
                case SpellTemplate.MotionType.Straight:
                case SpellTemplate.MotionType.Boomerang1:
                case SpellTemplate.MotionType.Boomerang2:
                case SpellTemplate.MotionType.StraightAndStop:
                    updateFloatingZ(mStartHeight);
                    break;
                case SpellTemplate.MotionType.AOE_Binding:
                case SpellTemplate.MotionType.Binding:
                    if (Sender != null)
                    {
                        this.mLocal_Z = Sender.Z;
                    }
                    break;
                case SpellTemplate.MotionType.AOE_BindingTarget:
                case SpellTemplate.MotionType.BindingTarget:
                    if (Target != null)
                    {
                        this.mLocal_Z = Target.Z;
                    }
                    break;
                case SpellTemplate.MotionType.Cannon:
                    if (TargetPos != null)
                    {
                        updateTargetZ(TargetPos, 0);
                    }
                    break;
                case SpellTemplate.MotionType.Missile:
                case SpellTemplate.MotionType.SeekerMissile:
				case SpellTemplate.MotionType.MissileAttackRoute:
					if (Target != null)
                    {
                        updateTargetZ(new TVector2(Target.X, Target.Y), Target.Z + Target.Info.BodyHeight * 0.6f);
                    }
                    break;
                case SpellTemplate.MotionType.CurveMissile:
                    if (Target != null)
                    {
                        updata3DParabola(new TVector2(Target.X, Target.Y));
                    }
                    break;
            }
        }

        private void updata3DParabola(IVector2 targetPos)
        {
            TVector2 nowPos = new TVector2(mLocalPos.X, mLocalPos.Y);
            TVector2 vcStart = new TVector2(mStartPos.X, mStartPos.Y);
            TVector2 vcEnd = new TVector2(targetPos.X, targetPos.Y);
            TVector2 v1 = nowPos - vcStart;
            TVector2 v2 = vcEnd - vcStart;
            float a = (float)Math.Atan2(v1.Y, v1.X);
            float b = (float)Math.Atan2(v2.Y, v2.X);
            if (float.Equals(a, b) || float.Equals(Math.PI, Math.Abs(a - b)))
            {
                mLocal_Z = Target.Z;
            }
            else
            {
                float dis = nowPos.DistanceTo(vcStart) * (float)Math.Abs(Math.Sin(a - b));
                mLocal_Z = this.mStartHeight + dis *1.6f* (float)Math.Sin(Math.PI / 6 * mCurveMissileIndex);
                //ClientLog.LogWarning(">z:{0}, index:{1}, ({2},{3})->({4},{5})->({6},{7})", mLocal_Z, mCurveMissileIndex, mStartPos.X, mStartPos.Y, TargetPos.X, targetPos.Y, nowPos.X, nowPos.Y);
            }
        }

        /// <summary>
        /// 计算有固定目标Z坐标
        /// </summary>
        /// <param name="targetPos"></param>
        /// <param name="targetZ"></param>
        private void updateTargetZ(IVector2 targetPos, float targetZ)
        {
            float fdis = MathVector.getDistance(mStartPos, targetPos);
            float odis = MathVector.getDistance(mStartPos, mLocalPos);
            float pct = CMath.getInRange((fdis == 0 ? 0 : (odis / fdis)), 0, 1);//生命周期百分比//
            float dz = CMath.getMiddleValue(pct, mStartHeight, targetZ);//和目标高度差//
            float ph = 0;//抛物线相对高度//
            if (Info.ParabolaHeight != 0)
            {
                //计算抛物线//
                ph = (float)Math.Sin(pct * CMath.PI_F) * Info.ParabolaHeight;
            }
            this.mLocal_Z = dz + ph;
        }
        private void updateFloatingZ(float targetZ)
        {
            float pct = (float)mPassTimeMS / (float)Info.LifeTimeMS;//生命周期百分比//
            float dz = CMath.getMiddleValue(pct, mStartHeight, targetZ);//和目标高度差//
            float ph = 0;//抛物线相对高度//
            if (Info.ParabolaHeight != 0)
            {
                //计算抛物线//
                ph = (float)Math.Sin(pct * CMath.PI_F) * Info.ParabolaHeight;
            }
            this.mLocal_Z = dz + ph;
        }

        private void updateBinding(float x, float y, float direction, int intervalMS)
        {
            if (Info.IsBindingDirection)
            {
                mDirection = direction;
            }
            if (Info.IsBindingOrbit)
            {
                float dadd = Info.OrbitDistance;
                float ox = (float)Math.Cos(Direction) * dadd;
                float oy = (float)Math.Sin(Direction) * dadd;
                mLocalPos.SetX(x + ox);
                mLocalPos.SetY(y + oy);
            }
            else
            {
                mLocalPos.SetX(x);
                mLocalPos.SetY(y);
            }
        }

        #endregion

        //---------------------------------------------------------------------------------------------------
        #region _UpdateShape_

        private void updateAOE(int intervalMS)
        {
            switch (Info.BodyShape)
            {
                case SpellTemplate.Shape.LineToTarget:
                case SpellTemplate.Shape.LineToStart:
                case SpellTemplate.Shape.Strip:
                case SpellTemplate.Shape.StripRay:
                case SpellTemplate.Shape.StripRayTouchEnd:
                case SpellTemplate.Shape.RectStrip:
                case SpellTemplate.Shape.RectStripRay:
                    updateAoeMotion(intervalMS, Info.Distance, ref mDistanceLimit);
                    mDisplayDistance = mDistanceLimit;
                    break;
                default:
                    updateAoeMotion(intervalMS, Info.BodySize, ref mSizeLimit);
                    mDisplaySize = mSizeLimit;
                    break;
            }
        }
        private void updateAoeMotion(int intervalMS, float base_value, ref float value)
        {
            switch (Info.AOEMType)
            {
                case SpellTemplate.AoeMotionType.Sine:
                    value = (float)Math.Sin(CMath.PI_F * mPassTimeMS / (float)Info.LifeTimeMS) * base_value;
                    break;
                case SpellTemplate.AoeMotionType.Linear:
                default:
                    value += MoveHelper.GetDistance(intervalMS, mSpeed);
                    break;
            }
        }

        private void updateRayTouchEnd()
        {
            if (Launcher != null)
            {
                float d_width = Info.RectWide / 2f;
                Vector2 p1 = new Vector2(X, Y);
                MathVector.movePolar(p1, this.Direction, this.mDistanceLimit);
                using (var list = ListObjectPool<ZoneUnit>.AllocAutoRelease())
                {
                    Parent.ForEachNearObjectsRect(this.X, this.Y, p1.X, p1.Y, (ZoneObject u, ref bool cancel) =>
                    {
                        if ((u is ZoneUnit))
                        {
                            ZoneUnit zu = u as ZoneUnit;
                            if (Parent.IsAttackable(Launcher, zu, Info.ExpectTarget))
                            {
                                if ((CMath.intersectLineRound(this.X, this.Y, p1.X, p1.Y, zu.X, zu.Y, d_width + zu.BodyHitSize)))
                                {
                                    list.Add(u as ZoneUnit);
                                }
                            }
                        }
                    });
                    if (list.Count > 0)
                    {
                        list.Sort(new ObjectBodySorterNearest<ZoneUnit>(this.X, this.Y, 0));
                        this.mDisplayDistance = CMath.getDistance(this.X, this.Y, list[0].X, list[0].Y);
                        this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit);
                    }
                    else
                    {
                        this.mDisplayDistance = this.mDistanceLimit;
                    }
                }
            }
        }

        private void updateLineToTarget()
        {
            if (Info.BodyShape == SpellTemplate.Shape.LineToTarget)
            {
                if (Target != null)
                {
                    this.mDisplayDistance = CMath.getDistance(this.X, this.Y, Target.X, Target.Y);
                    this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit);
                    PreFaceTo(Target.X, Target.Y);
                }
                else
                {
                    this.mDisplayDistance = 0;
                }
            }
            else if (Info.BodyShape == SpellTemplate.Shape.LineToStart)
            {
                this.mDisplayDistance = CMath.getDistance(this.X, this.Y, mStartPos.X, mStartPos.Y);
                this.mDisplayDistance = Math.Min(mDisplayDistance, this.mDistanceLimit);
                PreFaceTo(mStartPos.X, mStartPos.Y);
            }
            else
            {
                this.mDisplayDistance = 0;
            }
        }

        #endregion
        //---------------------------------------------------------------------------------------------------
        #region _UpdateKeyFrames_


        /// <summary>
        /// 更新范围检测以及关键帧
        /// </summary>
        private void updateKeyFrames()
        {
            switch (Info.MType)
            {
                case SpellTemplate.MotionType.Missile:
                case SpellTemplate.MotionType.SeekerMissile:
                case SpellTemplate.MotionType.Cannon:
                case SpellTemplate.MotionType.Chain:
				case SpellTemplate.MotionType.CurveMissile:
                    break;
                default:
                    if (Info.BodyShape == SpellTemplate.Shape.LineToTarget || Info.BodyShape == SpellTemplate.Shape.LineToStart)
                    {

                    }
                    else
                    {
                        updateKeyFramesRanged();
                    }
                    break;
            }
        }

        private void updateKeyFramesRanged()
        {
            using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease())
            {
                int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs);
                bool is_interval_test = mHitIntervalTicker.Update(Parent.CurrentIntervalMS);
                if (kfs_count > 0)
                {
                    for (int i = 0; i < kfs.Count; i++)
                    {
                        if (kfs[i].Effect != null)
                        {
                            Parent.PreQueueEvent(new UnitEffectEvent(ObjectID, kfs[i].Effect));
                        }
                    }
                }
                if (Info.HitOnExplosion)
                {
                }
                else if (Info.HitIntervalMS == 0)
                {
                }
                else if (is_interval_test)
                {
                    if (Info.HitIntervalKeyFrame != null && Info.HitIntervalKeyFrame.Effect != null)
                    {
                        Parent.PreQueueEvent(new UnitEffectEvent(ObjectID, Info.HitIntervalKeyFrame.Effect));
                    }
                }
            }
        }

        #endregion
        //---------------------------------------------------------------------------------------------------
        private void adjustPos(float min_distance)
        {
            float fdistance = MathVector.getDistance(mLocalPos, mPos);
            if (fdistance < min_distance)
            {
                MathVector.moveTo(mLocalPos, mPos.X, mPos.Y, min_distance);
            }
            else
            {
                MathVector.moveTo(mLocalPos, mPos.X, mPos.Y, fdistance / 2f);
            }
        }

        private void PreFaceTo(float angle)
        {
            this.mDirection = angle;
        }

        private void PreFaceTo(float x, float y)
        {
            if (this.X == x && this.Y == y)
            {
                return;
            }
            this.mDirection = (float)(Math.Atan2(y - this.Y, x - this.X));
        }
        private void PreTurnTo(float add)
        {
            this.mDirection += add;
        }
        private void PreFlyTo(float x, float y, float speedSEC, int intervalMS)
        {
            float distance = MoveHelper.GetDistance(intervalMS, speedSEC);
            MathVector.moveTo(mLocalPos, x, y, distance);
        }
        private void PreFlyTo(float direction, float speedSEC, int intervalMS)
        {
            float distance = MoveHelper.GetDistance(intervalMS, speedSEC);
            float dx = (float)(Math.Cos(direction) * distance);
            float dy = (float)(Math.Sin(direction) * distance);
            mLocalPos.AddX(dx);
            mLocalPos.AddY(dy);
        }
        public void FlyToTargetTunning(float x, float y, float speedSEC, float tunningSpeedSEC, int intervalMS)
        {
            MoveHelper.MoveToTargetTunning(ref mLocalPos, ref mDirection, x, y, speedSEC, tunningSpeedSEC, intervalMS);
        }
    }
}