using System;
using System.Collections.Generic;
using System.Text;
using CommonAI.RTS;
using CommonLang.Vector;
using CommonAI.ZoneClient;
using CommonLang;
using CommonAI.Zone.Formula;
using CommonAI.Zone.Helper;
using static CommonAI.Zone.Instance.InstanceUnit;
using CommonAI.ZoneServer.JSGModule;
using CommonAI.Data;

namespace CommonAI.Zone.Instance
{
    public class InstanceSpell : InstanceZoneObject
    {
        readonly protected SpellTemplate mInfo;
        readonly protected SyncSpellInfo mSyncInfo;
        readonly protected LaunchSpell mLaunchData;
        // 释放者
        readonly protected InstanceUnit mLauncherUnit;
        // 发出者
        readonly protected InstanceZoneObject mSender;
        protected AttackRangeHelper AttackRange;

        protected Vector2 mStartPos;
        protected float mLaunchDirection;
        // 被跟踪目标
        protected InstanceUnit mTarget;
        protected Vector2 mTargetPos;
        protected TimeExpire<int> mSeekingCooldownTime;
        protected SpellChainLevelInfo mChainInfo;

        /// <summary>
        /// 如果是技能产生的伤害,就是该技能的类型(就算是法术,也要追根到技能)
        /// </summary>
        public readonly XmdsSkillType FromSkillType = XmdsSkillType.none;

        private float mBaseSize;
        private float mDistance;
        private float mSpeed;

        private PopupKeyFrames<SpellTemplate.KeyFrame> mKeyFrames;
        private Dictionary<uint, InstanceUnit> mHittedUnits;
        private bool Finish = false;

        private TimeInterval<SpellTemplate.KeyFrame> mHitIntervalTicker;
        private InstanceUnit.StateSkill mBindingSkill;

        private bool mClientVisible;
        private bool mSyncPos;

        private float mMoveDistance;

        //服务器专用的扩展字段
        private int actionIndex;
        private int mMaxAffectUnit;
        //法术创建时,玩家的AOI状态
        private byte aoiFlag = 0;

        //每隔N秒触发,触发次数
        private int mHitTotalTimes;

        //法术总时长
        private readonly int mSpellTotalTime;               //法术的总时长

        // 飞弹各种奇葩数据记录, 法术数量可控
        private JSGCreateSpellData mCreateSpellData;           //法术创建信息(可变的,由技能数据确定)
                                                               // 飞弹不同的法术id
        public List<LaunchSpell> mCurveSpellList;
        //连续飞弹
        private byte mCurveSpellIndex;

        private TVector2 mPrvePos = new TVector2();

        public InstanceSpell(
            InstanceZone zone,
            SpellTemplate data,
            LaunchSpell launch,
            InstanceUnit launcher,
            InstanceZoneObject sender,
            Dictionary<uint, InstanceUnit> damageList,
            XmdsSkillType fromSkillType = XmdsSkillType.none,
            int spellIndex = 0,
            JSGCreateSpellData createData = null)
            : base(zone, false)
        {
            this.mSpellTotalTime = data.LifeTimeMS;
            this.mCurveSpellIndex = (byte)spellIndex;

            SenderID = 0;
            if (sender != null)
            {
                if (sender is InstanceUnit)
                    SenderID = sender.ID;
                else if (sender is InstanceSpell)
                    SenderID = (sender as InstanceSpell).SenderID;
            }

            //记录AOI状态
            if (sender.AoiStatus != null)
            {
                this.aoiFlag = 1;
            }

            this.FromSkillType = fromSkillType;
            this.mLaunchData = launch;
            if (createData == null)
            {
                this.mCreateSpellData = new JSGCreateSpellData();
                this.mCreateSpellData.mMaxSpellCount = launch.Count;
            }
            else
            {
                this.mCreateSpellData = createData;
            }


            this.mKeyFrames = new PopupKeyFrames<SpellTemplate.KeyFrame>();
            this.mKeyFrames.AddRange(data.KeyFrames);
            this.mInfo = data;
            this.mLauncherUnit = launcher;
            this.mSender = sender;
            this.mBaseSize = data.BodySize;
            this.mDistance = data.Distance;
            this.faceTo(launcher.Direction);
            this.IsAffectNearChange = false;
            this.mSyncInfo = new SyncSpellInfo(zone.IsHalfSync);
            this.mSyncInfo.TemplateID = mInfo.ID;
            this.mSyncInfo.Force = launcher.Force;
            if (mInfo.HitIntervalMS <= 0)
            {
                this.mHittedUnits = (damageList == null ? new Dictionary<uint, InstanceUnit>() : damageList);
            }
            this.mClientVisible = mInfo.ClientVisible;
            this.mSyncPos = true;// zone.SyncPos;
            this.mHitIntervalTicker = new TimeInterval<SpellTemplate.KeyFrame>(data.HitIntervalMS);
            this.mHitIntervalTicker.Tag = data.HitIntervalKeyFrame;
            this.mHitTotalTimes = 0;
            this.AttackRange = new AttackRangeHelper(launcher);
            this.mSpeed = mInfo.MSpeedSEC;
            this.mMoveDistance = 0;
            if (launcher.AoiStatus != null)
            {
                this.setAoiStatus(launcher.AoiStatus);
            }
        }
        public SpellTemplate Info { get { return mInfo; } }
        public LaunchSpell LaunchData { get { return mLaunchData; } }

        /// <summary>
        /// 最先技能的发起者
        /// </summary>
        public InstanceUnit Launcher { get { return mLauncherUnit; } }
        public uint LauncherID { get { return mLauncherUnit.ID; } }
        /// <summary>
        /// 技能的出口,比如技能触发技能,则Sender就是一个Spell
        /// </summary>
        public InstanceZoneObject Sender { get { return mSender; } }
        public InstanceUnit Target { get { return mTarget; } }
        public override bool IntersectMap { get { return false; } }
        public override bool IntersectObj { get { return false; } }
        public override bool Moveable { get { return true; } }
        public override float BodyBlockSize { get { return mBaseSize; } }
        public override float BodyHitSize { get { return mBaseSize; } }
        public override float BodyHeight { get { return mInfo.BodySize; } }
        public override float Weight { get { return 0; } }
        public override bool ClientVisible { get { return mClientVisible; } }
        public override bool SyncPos { get { return mSyncPos; } }

        public int BindActionIndex { get { return actionIndex; } set { this.actionIndex = value; } }

        public int MaxAffectUnit { get { return mMaxAffectUnit; } set { this.mMaxAffectUnit = value; } }

        /** 获取触发次数 */
        public int GetHitTimes()
        {
            return this.mHitTotalTimes;
        }

        /// <summary>
        /// 连锁等级
        /// </summary>
        public int ChainLevel { get { return (mChainInfo != null) ? mChainInfo.Level : 0; } }
        public SpellChainLevelInfo ChainInfo { get { return mChainInfo; } }

        public uint SenderID;
        public uint TargetID { get { return (mTarget != null) ? mTarget.ID : 0; } }
        public Vector2 TargetPos { get { return mTargetPos; } }
        public float LaunchHeightZ
        {
            get
            {
                if (mSender is InstanceSpell spell && spell.mInfo.MType == SpellTemplate.MotionType.Cannon)
                {
                    return 0;
                }
                if (mLaunchData.FromUnitBody)
                {
					//TODO: 此时的Z并不是spell的实际高度位置
                    return mSender.Z;
                }
                else
                {
                    return mLaunchData.LaunchSpellHeight;
                }
            }
        }

        public override SyncObjectInfo GenSyncInfo(bool net)
        {
            return GenSyncSpellInfo(net);
        }
        public SyncSpellInfo GenSyncSpellInfo(bool net = false)
        {
            mSyncInfo.x = X;
            mSyncInfo.y = Y;
            mSyncInfo.direction = this.Direction;
            mSyncInfo.HasSpeed = mInfo.IsMoveable && mSpeed != mInfo.MSpeedSEC;
            mSyncInfo.CurSpeed = mSpeed;
            return mSyncInfo;
        }

        // 如果是导弹类型,则需要目标
        public void setTarget(InstanceUnit target)
        {
            this.mTarget = target;
        }
        public void setTargetPos(Vector2 targetPos)
        {
            this.mTargetPos = targetPos;
        }
        public void setChainInfo(SpellChainLevelInfo c)
        {
            this.mChainInfo = c;
        }
        protected override void onAdded(bool pointLv)
        {
            if (mLaunchData.IsAutoSeekingTarget && mLaunchData.PType != LaunchSpell.PosType.POS_TYPE_AREA)
            {
                //目标不存在,或者已死亡才重新寻找, 不能打开,弹射逻辑
                //if(mTarget == null || mTarget.IsDead())
                {
                    mTarget = Parent.SeekSpellAttackable(this.Launcher, this.Info, this.Launcher.AoiStatus,
                   X, Y, mLaunchData.SeekingTargetRange, Info.ExpectTarget, mLaunchData.SeekingTargetExpect, mChainInfo);
                    //Console.WriteLine("重新索敌:" + mTarget.ID);
                }
            }

            this.mSyncInfo.ObjectID = base.ID;
            this.mLaunchDirection = this.Direction;

            float radius = mLaunchData.LaunchSpellRadius;
            if (mLaunchData.FromUnitBody && (Sender is InstanceUnit))
            {
                radius = (Sender as InstanceUnit).Info.LaunchSpellRadius;
            }
            mStartPos = new Vector2(X, Y);
            switch (mInfo.MType)
            {
                case SpellTemplate.MotionType.SelectTarget:
                    if (mTarget != null)
                    {
                        mStartPos.SetX(mTarget.X);
                        mStartPos.SetY(mTarget.Y);
                    }
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.SelectLauncher:
                    if (mLauncherUnit != null)
                    {
                        mStartPos.SetX(mLauncherUnit.X);
                        mStartPos.SetY(mLauncherUnit.Y);
                    }
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.Immovability:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.Cannon:
                    if (mTargetPos != null)
                    {
                        MathVector.moveTo(mStartPos, mTargetPos.X, mTargetPos.Y, radius);
                    }
                    else if (mTarget != null)
                    {
                        mTargetPos = new Vector2(mTarget.X, mTarget.Y);
                        MathVector.moveTo(mStartPos, mTargetPos.X, mTargetPos.Y, radius);
                    }
                    break;
                case SpellTemplate.MotionType.AOE:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.AOE_Binding:
                    break;
                case SpellTemplate.MotionType.AOE_BindingTarget:
                    break;
                case SpellTemplate.MotionType.Straight:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.Binding:
                    break;
                case SpellTemplate.MotionType.BindingTarget:
                    break;
                case SpellTemplate.MotionType.Missile:
                    if (mTarget != null)
                    {
                        MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius);
                    }
                    else
                    {
                        MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    }
                    break;
                case SpellTemplate.MotionType.MissileAttackRoute:
                    if (mTarget != null)
                    {
                        MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius);
                    }
                    else
                    {
                        MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    }
                    //设置索敌间隔
                    if (this.Info.SeekingCooldownMS > XmdsConstConfig.GAME_UPDATE_INTERVAL_MS)
                    {
                        this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS);
                    }
                    else
                    {
                        log.Error("法术:" + this.Info.ID + ", 索敌间隔配置异常!");
                    }
                    break;
                case SpellTemplate.MotionType.SeekerMissile:
                    if (mTarget != null)
                    {
                        MathVector.moveTo(mStartPos, mTarget.X, mTarget.Y, radius);
                    }
                    else
                    {
                        MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                        if (mInfo.SeekingCooldownMS == 0)
                        {
                            mTarget = seekAttackable(mInfo.SeekingRange);
                        }
                        else
                        {
                            this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS);
                        }
                    }
                    break;
                case SpellTemplate.MotionType.SeekerSelectTarget:
                    if (mTarget == null)
                    {
                        if (mInfo.SeekingCooldownMS == 0)
                        {
                            mTarget = seekAttackable(mInfo.SeekingRange);
                            if (mTarget != null)
                            {
                                mStartPos.SetX(mTarget.X);
                                mStartPos.SetY(mTarget.Y);
                            }
                        }
                        else
                        {
                            this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS);
                        }
                    }
                    break;
                case SpellTemplate.MotionType.Chain:
                    if (mSender != null)
                    {
                        mStartPos.SetX(mSender.X);
                        mStartPos.SetY(mSender.Y);
                    }
                    break;
                case SpellTemplate.MotionType.Boomerang1:
                case SpellTemplate.MotionType.Boomerang2:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
                case SpellTemplate.MotionType.CurveMissile:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    if (mInfo.SeekingCooldownMS > 0)
                    {
                        this.mSeekingCooldownTime = new TimeExpire<int>(mInfo.SeekingCooldownMS);
                    }
                    break;
                case SpellTemplate.MotionType.Foxfire:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    mTarget = null;
                    foxfireSeekWait = 0;
                    break;
                case SpellTemplate.MotionType.StraightAndStop:
                    MathVector.movePolar(mStartPos, mLaunchDirection, radius);
                    break;
            }
            setPos(mStartPos.X, mStartPos.Y, this.LaunchHeightZ);

        }

        //public long mDisPosingTime = 0;
        //public long mDisPosingTime1 = 0;
        protected override void Disposing()
        {
            this.mSeekingCooldownTime = null;
            this.mKeyFrames = null;
            //mDisPosingTime = CommonLang.CUtils.localTimeMS;
            //mDisPosingTime1 = System.DateTime.Now.Ticks;

            if (this.mHittedUnits != null)
            {
                this.mHittedUnits.Clear();
                this.mHittedUnits = null;
            }

            this.mHitIntervalTicker = null;
            this.mCreateSpellData = null;

            if (this.mCurveSpellList != null)
            {
                this.mCurveSpellList.Clear();
                this.mCurveSpellList = null;
            }

            if (brothers != null)
            {
                for (int i = 0; i < brothers.Length; i++)
                {
                    if (this == brothers[i])
                    {
                        brothers[i] = null;
                        break;
                    }
                }
            }

            this.AttackRange = null;

            base.Disposing();
        }

        protected override void onRemoved()
        {
            if (mInfo.StopBindingSkillOnRemoved)
            {
                if (this.Launcher != null && this.Launcher.CurrentState is InstanceUnit.StateSkill)
                {
                    (this.Launcher.CurrentState as InstanceUnit.StateSkill).block();
                    this.Launcher.doSomething();
                }
            }
        }

        override protected void onUpdate(bool slowRefresh)
        {
            if (IsPaused) { return; }

            if (Info.MaxMoveDistance == 0 || mMoveDistance < Info.MaxMoveDistance)
            {
                updateMotion();

                if (mInfo.IsMoveable)
                {
                    MoveHelper.UpdateSpeed(Parent.UpdateIntervalMS, ref mSpeed, mInfo.MSpeedAdd, mInfo.MSpeedAcc, mInfo.MSpeed_MIN, mInfo.MSpeed_MAX);
                    MoveHelper.UpdateMoveDistance(Parent.UpdateIntervalMS, ref mMoveDistance, mSpeed);
                }
            }

            if (this.mKeyFrames != null)
            {
                updateKeyFrames(slowRefresh);
            }

            if (mInfo.MType == SpellTemplate.MotionType.Foxfire && mTarget != null)
            {
            }
            else if (mInfo.MType == SpellTemplate.MotionType.CurveMissile && mTarget != null)
            {

            }
            else if (mInfo.MType == SpellTemplate.MotionType.Boomerang1 || mInfo.MType == SpellTemplate.MotionType.Boomerang2)
            {

            }
            else if (mInfo.MType == SpellTemplate.MotionType.Binding)//绑定自身的spell才在自己死亡时移除
            {
                if (!Sender.Enable || Sender.IsDead())
                {
                    Parent.RemoveObject(this);
                }
                else if ((Sender.AoiStatus == null && this.aoiFlag == 1) || (Sender.AoiStatus != null && this.aoiFlag == 0))
                {
                    //玩家AOI状态改变了,也要清一下
                    Parent.RemoveObject(this);
                }

                StateSkill skill = this.mLauncherUnit.CurrentState as StateSkill;
                if (skill != null && skill.Skill != null && skill.SkillData.ActionQueue.Count > 1)
                {
                    //UnitActionData curSkillAction = skill.Skill.GetCurAction();
                    if (skill.CurrentActionIndex != this.actionIndex
                        && (0 <= this.actionIndex && this.actionIndex < skill.SkillData.ActionQueue.Count)
                        && skill.SkillData.ActionQueue[this.actionIndex].IsCancelBySkillNext)
                    {
                        Parent.RemoveObject(this);
                    }
                }
            }

            if (PassTimeMS >= this.mSpellTotalTime)
            {
                Parent.RemoveObject(this);//Parent.RemoveObjectByID(ID);
            }
        }

        /// <summary>
        /// 更新移动行为
        /// </summary>
        private void updateMotion()
        {
            this.mPrvePos.SetX(X);
            this.mPrvePos.SetY(Y);
            switch (mInfo.MType)
            {
                case SpellTemplate.MotionType.Immovability:
                case SpellTemplate.MotionType.SelectTarget:
                case SpellTemplate.MotionType.SelectLauncher:
                    break;

                case SpellTemplate.MotionType.Cannon:
                    if (mTargetPos != null)
                    {
                        if (flyToTarget(mTargetPos.X, mTargetPos.Y, mSpeed, Parent.UpdateIntervalMS))
                        {
                            this.Finish = true;
                        }
                    }
                    else if (PassTimeMS + Parent.UpdateIntervalMS >= this.mSpellTotalTime)//没有目标,结束时触发下
                    {
                        if (Parent.UpdateIntervalMS > 0)
                        {
                            this.Finish = true;
                        }
                    }

                    break;
                case SpellTemplate.MotionType.Straight:
                    flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    break;

                case SpellTemplate.MotionType.AOE:
                    updateAOE();
                    break;
                case SpellTemplate.MotionType.AOE_Binding:
                    updateAOE();
                    if (mSender != null && mSender.Enable)
                    {
                        updateBinding(mSender);
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                    }
                    break;
                case SpellTemplate.MotionType.AOE_BindingTarget:
                    updateAOE();
                    if (mTarget != null && mTarget.IsActive)
                    {
                        updateBinding(mTarget);
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                    }
                    break;
                case SpellTemplate.MotionType.Binding:
                    if (mSender != null && mSender.Enable)
                    {
                        updateBinding(mSender);
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                    }
                    break;
                case SpellTemplate.MotionType.BindingTarget:
                    if (mTarget != null && mTarget.IsActive)
                    {
                        updateBinding(mTarget);
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                    }
                    break;
                case SpellTemplate.MotionType.Missile:
                case SpellTemplate.MotionType.MissileAttackRoute:
                    if (mTarget != null)
                    {
                        //if (mInfo.RotateSpeedSEC == 0)
                        {
                            faceTo(mTarget.X, mTarget.Y);
                        }
                        flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS);
                    }
                    else
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    }
                    break;
                case SpellTemplate.MotionType.SeekerMissile:
                    if (mTarget != null)
                    {
                        if (mInfo.SeekingTurningAngleSEC != 0)
                        {
                            flyToTargetTunning(mTarget.X, mTarget.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS);
                        }
                        else
                        {
                            faceTo(mTarget.X, mTarget.Y);
                            flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS);
                        }
                    }
                    else
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                        if (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS))
                        {
                            mTarget = seekAttackable(mInfo.SeekingRange);
                            if (mTarget != null)
                            {
                                Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y));
                            }
                        }
                    }
                    break;
                case SpellTemplate.MotionType.SeekerSelectTarget:
                    if (mTarget == null)
                    {
                        if (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS))
                        {
                            mTarget = seekAttackable(mInfo.SeekingRange);
                            if (mTarget != null)
                            {
                                this.setPos(mTarget.X, mTarget.Y);
                                Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y));
                            }
                        }
                    }
                    break;
                case SpellTemplate.MotionType.Chain:
                    if (mSender != null && mSender.Enable && mTarget != null && mTarget.IsActive)
                    {
                        setPos(mSender.X, mSender.Y);
                        faceTo(mTarget.X, mTarget.Y);
                        updateBinding(mTarget);
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                    }
                    break;

                case SpellTemplate.MotionType.Boomerang1:
                    if (PassTimeMS < mInfo.BoomerangFlyTime)
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    }
                    else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime)
                    {
                        if (flyToTarget(mLauncherUnit.X, mLauncherUnit.Y, mSpeed * 1.4f, Parent.UpdateIntervalMS))
                        {
                            this.Finish = true;
                        }
                    }
                    break;
                case SpellTemplate.MotionType.Boomerang2:
                    if (PassTimeMS < mInfo.BoomerangFlyTime)
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    }
                    else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime)
                    {
                        if (flyToTarget(mStartPos.X, mStartPos.Y, mSpeed * 1.4f, Parent.UpdateIntervalMS))
                        {
                            this.Finish = true;
                        }
                    }
                    break;
                case SpellTemplate.MotionType.CurveMissile:
                    //IPosBase targetPos = mTargetPos;
                    IPosBase targetPos = (mTarget != null) ? (IPosBase)mTarget : (IPosBase)mTargetPos;
                    if (targetPos == null)
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    }
                    else if (mInfo.SeekingTurningAngleSEC != 0)
                    {
                        flyToTargetTunning(targetPos.X, targetPos.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS);
                    }
                    else
                    {
                        faceTo(targetPos.X, targetPos.Y);
                        flyToTarget(targetPos.X, targetPos.Y, mSpeed, Parent.UpdateIntervalMS);
                    }

                    if (mTarget == null && (mSeekingCooldownTime == null || mSeekingCooldownTime.Update(Parent.UpdateIntervalMS)))
                    {
                        mTarget = seekAttackable(mInfo.SeekingRange);
                        if (mTarget != null)
                        {
                            Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, mTarget.ID, X, Y));
                        }
                    }

                    this.checkAndLaunchCurveSpell();
                    break;
                case SpellTemplate.MotionType.Foxfire:
                    if (mTarget == null)
                    {
                        foxfireRoundAngle = mLaunchDirection + PassTimeMS * 0.001f * 3;
                        var x = mLauncherUnit.X + (float)(Math.Cos(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius);
                        var y = mLauncherUnit.Y + (float)(Math.Sin(foxfireRoundAngle + 0.3f) * mLaunchData.LaunchSpellRadius);
                        flyToTarget(x, y, mSpeed, Parent.UpdateIntervalMS);
                        if (mInfo.RotateSpeedSEC == 0)
                        {
                            faceTo(foxfireRoundAngle + (float)Math.PI * 0.25f);
                        }
                        if (foxfireSeekWait >= this.Info.SeekingCooldownMS && PassTimeMS < this.mSpellTotalTime - 500)
                        {
                            var tar = seekAttackable(mInfo.SeekingRange);
                            if (tar != null && tar != mLauncherUnit)
                            {
                                changeBrotherSeekWait(-250);
                                Parent.queueObjectEvent(this, new SpellLockTargetEvent(ID, tar.ID, X, Y));
                                mTarget = tar;
                            }
                        }
                        foxfireSeekWait += Parent.UpdateIntervalMS;
                    }
                    else
                    {

                        if (mInfo.SeekingTurningAngleSEC != 0)
                        {
                            flyToTargetTunning(mTarget.X, mTarget.Y, mSpeed, mInfo.SeekingTurningAngleSEC, Parent.UpdateIntervalMS);
                        }
                        else
                        {
                            faceTo(mTarget.X, mTarget.Y);
                            flyToTarget(mTarget.X, mTarget.Y, mSpeed, Parent.UpdateIntervalMS);
                        }
                    }
                    break;
                case SpellTemplate.MotionType.StraightAndStop:
                    if (PassTimeMS < mInfo.BoomerangFlyTime)
                    {
                        flyTo(mLaunchDirection, mSpeed, Parent.UpdateIntervalMS);
                    }
                    //else if (PassTimeMS > mInfo.BoomerangFlyTime + mInfo.BoomerangHangtime)
                    //{
                    //    if (flyToTarget(mLauncherUnit.X, mLauncherUnit.Y, mSpeed * 1.8f, Parent.UpdateIntervalMS))
                    //    {
                    //        this.Finish = true;
                    //    }
                    //}
                    break;
            }
            if (mInfo.BodyShape == SpellTemplate.Shape.LineToTarget)
            {
                if (mTarget != null)
                {
                    this.faceTo(mTarget.X, mTarget.Y);
                }
            }
            else if (mInfo.BodyShape == SpellTemplate.Shape.LineToStart)
            {
                this.faceTo(mStartPos.X, mStartPos.Y);
            }
            else if (mInfo.RotateSpeedSEC != 0 && mInfo.MType != SpellTemplate.MotionType.SeekerMissile)
            {
                this.turn(MoveHelper.GetDistance(Parent.UpdateIntervalMS, mInfo.RotateSpeedSEC));
            }
        }


        /** 检测释放后续飞弹 */
        private void checkAndLaunchCurveSpell(bool force = false)
        {
            //////创建后续个飞弹
            if ((force || PassTimeMS >= XmdsConstConfig.GAME_UPDATE_INTERVAL_MS) && mCurveSpellIndex > 0 && this.Launcher.IsActive)
            {
                int nextSpellIndex = Math.Max(0, mCurveSpellIndex - 1);
                int flag = (this.mCreateSpellData.mMaxSpellCount - mCurveSpellIndex) % 2 == 0 ? 2 : -2;
                //float angleOffset = (this.Target == null ? this.LaunchData.StartAngle / 3 : this.LaunchData.StartAngle);
                float angleOffset = JSGModule.GetCurveStartAngleOffect(mLauncherUnit, this.mTarget, this.LaunchData.StartAngle, this.Info.SeekingRange);
                float startDirection = this.mLaunchDirection + angleOffset * flag;
                float launchX = this.Launcher.X - (float)(1.0f * Math.Cos(this.Launcher.Direction));
                float launchY = this.Launcher.Y - (float)(1.0f * Math.Sin(this.Launcher.Direction));

                LaunchSpell launchSpell = this.LaunchData;
                if (mCurveSpellList != null && mCurveSpellList.Count > mCurveSpellIndex)
                {
                    launchSpell = mCurveSpellList[mCurveSpellIndex];
                }
                InstanceSpell addSpell = this.Parent.AddSpell(this.FromSkillType, this.Info, launchSpell, this.Sender, this.mLauncherUnit, (this.mTarget == null ? 0 : this.mTarget.ID),
                    JSGModule.GetCurveDefaultTargetPos(mLauncherUnit, this.Info.SeekingRange), launchX, launchY, startDirection, null, -1, 0, null, nextSpellIndex);
                addSpell.mCurveSpellList = this.mCurveSpellList;

                mCurveSpellIndex = 0;
            }
        }

		public static double AngleBetween(Vector2 vector1, Vector2 vector2)
        {
            double sin = vector1.X * vector2.Y - vector2.X * vector1.Y;
            double cos = vector1.X * vector2.X + vector1.Y * vector2.Y;

            return Math.Atan2(sin, cos);

        }

        private void updateAOE()
        {
            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(Info.Distance, ref mDistance);
                    break;
                default:
                    updateAoeMotion(Info.BodySize, ref mBaseSize);
                    break;
            }
        }

        private void updateAoeMotion(float base_value, ref float value)
        {
            switch (Info.AOEMType)
            {
                case SpellTemplate.AoeMotionType.Sine:
                    value = (float)Math.Sin(CMath.PI_F * PassTimeMS / (float)this.mSpellTotalTime) * base_value;
                    break;
                case SpellTemplate.AoeMotionType.Linear:
                default:
                    value += MoveHelper.GetDistance(Parent.UpdateIntervalMS, mSpeed);
                    break;
            }
        }


        private void updateBinding(InstanceZoneObject target)
        {
            if ((target is InstanceUnit))
            {
                var target_unit = target as InstanceUnit;
                if (mInfo.RemoveOnBindingUncontrollable)
                {
                    // 目标不可操控,停止法术 //
                    if (target_unit.IsControllable == false)
                    {
                        Parent.RemoveObject(this);
                        return;
                    }
                }
                if (mInfo.RemoveOnBindingSkillOver)
                {
                    // 目标非技能状态,停止法术 //
                    if (target_unit.CurrentState is InstanceUnit.StateSkill)
                    {
                        if (mBindingSkill == null)
                        {
                            mBindingSkill = target_unit.CurrentState as InstanceUnit.StateSkill;
                        }
                        else if (mBindingSkill != target_unit.CurrentState)
                        {
                            Parent.RemoveObject(this);
                            return;
                        }
                    }
                    else
                    {
                        Parent.RemoveObject(this);
                        return;
                    }
                }
            }
            if (mInfo.IsBinding)
            {
                if (mInfo.IsBindingDirection)
                {
                    this.faceTo(target.Direction);
                }
                if (mInfo.IsBindingOrbit)
                {
                    float dadd = mInfo.OrbitDistance;
                    float ox = (float)Math.Cos(Direction) * dadd;
                    float oy = (float)Math.Sin(Direction) * dadd;
                    this.setPos(target.X + ox, target.Y + oy);
                }
                else
                {
					float xChanage, yChange;
					this.LaunchData.GetXModify(this.Launcher, out xChanage, out yChange);

					this.setPos(target.X + xChanage, target.Y + yChange);
                }
            }
        }

        private void affectToSingle(InstanceUnit target, SpellTemplate.KeyFrame kf)
        {
            if (kf == null) return;
            if (kf.Effect != null)
            {
                Parent.queueObjectEvent(this, new UnitEffectEvent(this.ID, kf.Effect));
            }

            // 法术造成伤害
            if (kf.Attack != null)
            {
                Parent.unitAttackSingle(mLauncherUnit, new AttackSource(this, kf.Attack), target, mInfo.ExpectTarget);
            }

            // 法术产生法术
            if (kf.Spell != null)
            {
                Parent.spellLaunchSpell(this.FromSkillType, this, kf.Spell, this.X, this.Y, target.ID);
                //Parent.spellLaunchSpell(this, kf.Spell, mLauncherUnit.X + 5, mLauncherUnit.Y + 5, target.ID);
            }
            // 召唤
            if (kf.Summon != null)
            {
                Parent.spellSummonUnit(this, kf.Summon);
            }

        }

        private void affectToMulti(List<InstanceUnit> list, SpellTemplate.KeyFrame kf, bool effect)
        {
            if (kf == null) return;

            if (effect && kf.Effect != null)
            {
                Parent.queueObjectEvent(this, new UnitEffectEvent(this.ID, kf.Effect));
            }

			uint targetUnitID = 0;
			if (list.Count > 0)
            {
				targetUnitID = list[0].ID;
				// 法术造成伤害
				if (kf.Attack != null)
                {
					Parent.unitAttackDirect(mLauncherUnit, new AttackSource(this, kf.Attack), list);
                }
            }

            // 法术产生法术
            if (kf.Spell != null)
            {
				Parent.spellLaunchSpell(this.FromSkillType, this, kf.Spell, this.X, this.Y, targetUnitID);
				//System.Console.WriteLine("------法术产生法术: " + kf.Spell.SpellID + ", " + this.X + ", " + this.Y);
            }

            // 召唤
            if (kf.Summon != null)
            {
                Parent.spellSummonUnit(this, kf.Summon);
            }
        }

        #region __检测范围内目标__

        // 跟踪导弹查找附近单位 //
        public InstanceUnit seekAttackable(float range)
        {
            return Parent.SeekSpellAttackable(this.Launcher, this.Info, this.AoiStatus,
                X, Y, range, mInfo.ExpectTarget, mInfo.SeekingExpectTarget, mChainInfo);
        }

        // 范围伤害,碰撞检测 //
        private void getShapeAttackable(List<InstanceUnit> ret, AttackReason reason, bool slowRefresh)
        {
            AttackRange.Shape = (AttackShape)mInfo.BodyShape;
            AttackRange.BodySize = this.mBaseSize;
            AttackRange.Direction = this.Direction;
            AttackRange.Distance = this.mDistance;
            AttackRange.ExpectTarget = mInfo.ExpectTarget;
            AttackRange.FanAngle = this.mInfo.FanAngle;
            AttackRange.StripWide = this.mInfo.RectWide;
            float prv_x = this.X;
            float prv_y = this.Y;
            if (mInfo.MType == SpellTemplate.MotionType.Straight || mInfo.MType == SpellTemplate.MotionType.StraightAndStop)
            {
                prv_x = mPrvePos.X;
                prv_y = mPrvePos.Y;
            }
            AttackRange.GetShapeAttackable(ret, reason, this.Info, X, Y, prv_x, prv_y, mInfo.MaxAffectUnit, AoiStatus);
        }

        #endregion

        float foxfireRoundAngle = 0f;
        int foxfireSeekWait = 0;

        public InstanceSpell[] brothers = null;
        void changeBrotherSeekWait(int timeValue)
        {
            if (brothers == null)
            {
                return;
            }
            foreach (var b in brothers)
            {
				if(b != null)
				{
					b.foxfireSeekWait += timeValue;
				}                
            }
        }

        /// <summary>
        /// 更新范围检测以及关键帧
        /// </summary>
        private void updateKeyFrames(bool slowRefresh)
        {
            switch (Info.MType)
            {
                case SpellTemplate.MotionType.Missile:
                case SpellTemplate.MotionType.SeekerMissile:
					//【战斗】Missile类技能,在目标消失后失效//
					if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y))
                    {
                        if (mTarget.IsActive)
                        {
                            affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame);
                        }
                        Parent.RemoveObject(this);
                    }
                    break;
				case SpellTemplate.MotionType.CurveMissile:
					//【战斗】Missile类技能,在目标消失后失效//
					if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize + this.BodyHitSize, this.X, this.Y))
					{
						if (mTarget.IsActive)
						{
							affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame);
						}
                        this.checkAndLaunchCurveSpell(true);
                        Parent.RemoveObject(this);
					}
					break;
				case SpellTemplate.MotionType.MissileAttackRoute:
					if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y))
					{
						if (mTarget.IsActive)
						{
							affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame);
						}
						Parent.RemoveObject(this);
					}
					else if (mSeekingCooldownTime.Update(Parent.UpdateIntervalMS))
					{
						using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
						{
							getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh);
							if (enemy_list.Count > 0)
							{
								InstanceUnit target = enemy_list[0];
								if (target.IsActive)
								{
									affectToSingle(target, mInfo.HitOnExplosionKeyFrame);
								}
								mSeekingCooldownTime.Reset();
							}
						}
					}
					break;
				case SpellTemplate.MotionType.Cannon:
                    if (Finish)
                    {
                        SpellTemplate.KeyFrame kf = mInfo.HitOnExplosionKeyFrame;
                        if (kf != null)
                        {
                            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                            {
                                getShapeAttackable(list, AttackReason.Attack, slowRefresh);
                                affectToMulti(list, kf, true);
                            }
                        }
                        Parent.RemoveObject(this);
                    }
                    break;
                case SpellTemplate.MotionType.Chain:
                    if (mTarget != null)
                    {
                        if (Collider.Object_Pos_IncludeInRound(mTarget, this.X, this.Y, this.mDistance))
                        {
                            updateKeyFrameSingleTarget(mTarget, true);
                        }
                        else
                        {
                            Parent.RemoveObject(this);
                        }
                    }
                    break;
                case SpellTemplate.MotionType.Boomerang1:
                case SpellTemplate.MotionType.Boomerang2:                  
                    if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DragPath && Info.SpecialEffectParam != 0f)
                    {
                        updateDragPathKeyFramesRanged(slowRefresh);
                    }
                    else if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DarkHole && Info.SpecialEffectParam != 0f)
                    {
                        updateDarkHoleKeyFramesRanged(slowRefresh);
                    }
                    else
                    {
                        updateKeyFramesRanged(slowRefresh);
                    }
					if (Finish)
					{
						Parent.RemoveObject(this);
					}
					break;
                case SpellTemplate.MotionType.Foxfire:
                    if (mTarget != null && CMath.includeRoundPoint(mTarget.X, mTarget.Y, mTarget.BodyHitSize, this.X, this.Y))
                    {
                        if (mTarget.IsActive)
                        {
                            affectToSingle(mTarget, mInfo.HitOnExplosionKeyFrame);
                        }
                        Parent.RemoveObject(this);
                    }
                    break;
                default:
                    if (Info.BodyShape == SpellTemplate.Shape.LineToTarget || Info.BodyShape == SpellTemplate.Shape.LineToStart)
                    {
                        if (mTarget != null && Collider.Object_Pos_IncludeInRound(mTarget, this.X, this.Y, this.mDistance))
                        {
                            updateKeyFrameSingleTarget(mTarget, true);
                        }
                        else
                        {
                            updateKeyFrameSingleTarget(mTarget, false);
                        }
                    }
                    else
                    {
                        if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DragPath && Info.SpecialEffectParam != 0f)
                        {
                            updateDragPathKeyFramesRanged(slowRefresh);
                        }
                        else if (Info.SpecialEffect == SpellTemplate.SpecialAdditionalEffect.DarkHole && Info.SpecialEffectParam != 0f)
                        {
                            updateDarkHoleKeyFramesRanged(slowRefresh);
                        }
                        else
                        {
                            updateKeyFramesRanged(slowRefresh);
                        }
                    }
                    break;
            }
        }

        private void updateKeyFrameSingleTarget(InstanceUnit enemy, bool affect)
        {
            using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease())
            {
                int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs);
                bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS);
                if (affect)
                {
                    if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0)
                    {
                        if (kfs_count > 0)
                        {
                            for (int i = 0; i < kfs.Count; i++)
                            {
                                affectToSingle(enemy, kfs[i]);
                            }
                        }
                        if (mInfo.HitOnExplosion)
                        {
                            // 击中后爆炸
                            affectToSingle(enemy, mInfo.HitOnExplosionKeyFrame);
                            Parent.RemoveObject(this);
                        }
                        else if (mInfo.HitIntervalKeyFrame != null)
                        {
                            if (mInfo.HitIntervalMS == 0)
                            {
                                // 只在接触后第一次产生效果
                                if (!mHittedUnits.ContainsKey(enemy.ID))
                                {
                                    affectToSingle(enemy, mInfo.HitIntervalKeyFrame);
                                    mHittedUnits.Add(enemy.ID, enemy);
                                }
                            }
                            else if (is_interval_test)
                            {
                                // 间隔产生效果
                                affectToSingle(enemy, mInfo.HitIntervalKeyFrame);
                            }
                        }
                    }
                }
            }
        }


        private void updateKeyFramesRanged(bool slowRefresh)
        {
            using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease())
            {
				//if(mKeyFrames == null)
				//{
				//	long ltime1 = CommonLang.CUtils.localTimeMS - mDisPosingTime;
				//	long ltime2 = System.DateTime.Now.Ticks - mDisPosingTime1;
				//}
                int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs);
                bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS);
                //此处修正 加入触发次数上限检测
                if (is_interval_test && mInfo.HitIntervalMS > 0 && mHitIntervalTicker.TotalTickCount > (this.mSpellTotalTime / mInfo.HitIntervalMS))
                {
                    is_interval_test = false;
                }
                if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0)
                {
					this.mHitTotalTimes++;

					using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                    {
                        getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh);
                        if (kfs_count > 0)
                        {
							// 单Frame情况下,如果是Attack。进行唯一敌人判定(X型拼接法术, 需要伤害只生效一次)
							//if (kfs_count == 1 && mInfo.HitIntervalMS == 0 && this.mInfo.KeyFrames.Count == 1 && kfs[0].Attack != null && this.mInfo.HitIntervalKeyFrame == null)
							if(this.LaunchData.PType == LaunchSpell.PosType.POS_TYPE_X)
							{
								if(this.refreshHitUnit(enemy_list))
								{
									for (int i = 0; i < kfs.Count; i++)
									{
										affectToMulti(enemy_list, kfs[i], true);
									}
								}								
							}
							else
							{
								for (int i = 0; i < kfs.Count; i++)
								{
									affectToMulti(enemy_list, kfs[i], true);
								}
							}                           
                        }

                        if (mInfo.HitOnExplosion)
                        {
                            if (enemy_list.Count > 0)
                            {
                                // 击中后爆炸
                                affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true);
                                Parent.RemoveObject(this);
                            }
                        }
                        else if(mInfo.HitIntervalKeyFrame != null)
						{
							if (mInfo.HitIntervalMS == 0)
							{
								// 只在接触后第一次产生效果
								if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list))
								{
									affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true);
								}
							}
							else if (is_interval_test)
							{
								// 间隔产生效果
								affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false);
							}
						}						
                    }
                }
            }
        }


        private void updateDarkHoleKeyFramesRanged(bool slowRefresh)
        {
            using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease())
            {
                using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                {
                    getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh);

                    for (int i = enemy_list.Count - 1; i >= 0; --i)
                    {
						if(enemy_list[i].IsIgnoreControl)
						{
							continue;
						}

                        float angle, distance, offset, dx, dy;
                        if (enemy_list[i].Weight < XmdsConstConfig.DEFAULT_WEIGHT)
                        {
                            offset = MoveHelper.GetDistance(Parent.UpdateIntervalMS, Info.SpecialEffectParam);
                            dx = this.X - enemy_list[i].X;
                            dy = this.Y - enemy_list[i].Y;
                            distance = dx * dx + dy * dy;
                            if (offset * offset < distance)
                            {
                                angle = MathVector.getDegree(dx, dy);
                                enemy_list[i].moveLinearMap(offset, angle);
                            }
                            else
                            {
                                enemy_list[i].moveLinearMap2(dx, dy);
                            }
                            enemy_list[i].SetStunTimeMS(100);
                        }
                    }
                    int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs);
                    bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS);
                    #region <<关键帧逻辑>>
                    if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0)
                    {
                        //getShapeAttackable(enemy_list, AttackReason.Attack);
                        if (kfs_count > 0)
                        {
                            for (int i = 0; i < kfs.Count; i++)
                            {
                                affectToMulti(enemy_list, kfs[i], false);
                            }
                        }
                        if (mInfo.HitOnExplosion)
                        {
                            if (enemy_list.Count > 0)
                            {
                                // 击中后爆炸
                                affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true);
                                Parent.RemoveObject(this);
                            }
                        }
						else if(mInfo.HitIntervalKeyFrame != null)
						{
							if (mInfo.HitIntervalMS == 0)
							{
								// 只在接触后第一次产生效果
								if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list))
								{
									affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true);
								}
							}
							else if (is_interval_test)
							{
								// 间隔产生效果
								affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false);
							}
						}                       
                    }
                    #endregion
                }
            }
        }

		public override String GetTemplateData()
		{
			return "Spell:" + this.Info.ID;
		}

		private bool refreshHitUnit(CommonLang.ListObjectPool<InstanceUnit>.AutoReleaseList<InstanceUnit> enemy_list)
		{
			if(enemy_list.Count <= 0)
			{
				return true;
			}

			foreach (InstanceUnit u in mHittedUnits.Values)
			{
				enemy_list.Remove(u);
			}

			if (enemy_list.Count > 0)
			{
				for (int i = enemy_list.Count - 1; i >= 0; --i)
				{
					InstanceUnit u = enemy_list[i];
					mHittedUnits.Add(u.ID, u);
				}

				return true;
			}

			return false;
		}


		public const float Pi = (float)Math.PI;
        public const float PiOver2 = (float)(Math.PI / 2.0);
        public const float PiOver36 = (float)(Math.PI / 36.0);
        public const float TwoPi = (float)(Math.PI * 2.0);

        private void updateDragPathKeyFramesRanged(bool slowRefresh)
        {
            using (var kfs = ListObjectPool<SpellTemplate.KeyFrame>.AllocAutoRelease())
            {
                using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                {
                    getShapeAttackable(enemy_list, AttackReason.Attack, slowRefresh);

                    float _a, _b, _angle, offset, dx, dy , dis;

                    for (int i = enemy_list.Count - 1; i >= 0; --i)
                    {
						if (enemy_list[i].IsIgnoreControl)
						{
							continue;
						}

						if (enemy_list[i].Weight < XmdsConstConfig.DEFAULT_WEIGHT)
                        {
                            dx = enemy_list[i].X - this.X;
                            dy = enemy_list[i].Y - this.Y;
                            _a = MathVector.getDegree(dx, dy);
                            _b = this.Direction - _a;
                            if (_b < -Pi)
                            {
                                _b += TwoPi;
                            }
                            else if(_b > Pi)
                            {
                                _b -= TwoPi;
                            }
                            if (Math.Abs(_b) < PiOver36)
                            {
                                continue;
                            }
                            if(_b > 0)
                            {
                                if(_b > PiOver2)
                                {
                                    continue;
                                }
                                _angle = this.Direction + PiOver2;
                            }
                            else
                            {
                                if (_b < -PiOver2)
                                {
                                    continue;
                                }
                                _angle = this.Direction - PiOver2;
                            }
                            dis = Math.Abs((float)(dy * Math.Sin(_angle) / Math.Sin(_a)));
                            offset = MoveHelper.GetDistance(Parent.UpdateIntervalMS, Info.SpecialEffectParam);
                            if(offset > dis)
                            {
                                offset = dis;
                            }
                            enemy_list[i].moveLinearMap(offset, _angle);
                            enemy_list[i].SetStunTimeMS(100);
                        }
                    }
                    int kfs_count = mKeyFrames.PopKeyFrames(PassTimeMS, kfs);
                    bool is_interval_test = mHitIntervalTicker.Update(Parent.UpdateIntervalMS);
                    #region <<关键帧逻辑>>
                    if (kfs_count > 0 || is_interval_test || mInfo.HitIntervalMS == 0)
                    {
                        //getShapeAttackable(enemy_list, AttackReason.Attack);
                        if (kfs_count > 0)
                        {
                            for (int i = 0; i < kfs.Count; i++)
                            {
                                affectToMulti(enemy_list, kfs[i], false);
                            }
                        }
                        if (mInfo.HitOnExplosion)
                        {
                            if (enemy_list.Count > 0)
                            {
                                // 击中后爆炸
                                affectToMulti(enemy_list, mInfo.HitOnExplosionKeyFrame, true);
                                Parent.RemoveObject(this);
                            }
                        }
						else if (mInfo.HitIntervalKeyFrame != null)
						{
							if (mInfo.HitIntervalMS == 0)
							{
								// 只在接触后第一次产生效果
								if (enemy_list.Count > 0 && this.refreshHitUnit(enemy_list))
								{
									affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, true);
								}
							}
							else if (is_interval_test)
							{
								// 间隔产生效果
								affectToMulti(enemy_list, mInfo.HitIntervalKeyFrame, false);
							}
						}                       
                    }
                    #endregion
                }
            }
        }
    }
}