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

namespace CommonAI.ZoneClient
{
    public partial class ZoneUnit
    {        //--------------------------------------------------------------------------------

        #region DamageDeadFly

        private TimeExpire<UnitDamageEvent> mDamageTime;
        private TimeExpire<UnitDeadEvent> mDeadTime;
        private FallingDown mHitFlyState;

        public FallingDown StartFly(float z_speed, float gravity, float z_limit)
        {
            if (!Parent.IsSyncZ && z_speed != 0)
            {
                mHitFlyState = new FallingDown(this, z_speed, gravity, z_limit);
                return mHitFlyState;
            }
            return null;
        }

        protected virtual void DoDamage(UnitDamageEvent e)
        {
            mDamageTime = new TimeExpire<UnitDamageEvent>(e, e.DamageTimeMS);
            if (e.HasFly)
            {
                StartFly(e.ZSpeedSEC, e.ZGravity, e.ZLimit);
            }
            if (Info.DamageEffect != null)
            {
                Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.DamageEffect));
            }
            SetCurrentState(UnitActionStatus.Damage, e);

        }
        protected virtual void DoJump(UnitJumpEvent e)
        {
            StartFly(e.ZSpeed, e.ZGravity, e.ZLimit);
        }

        protected virtual void DoDead(UnitDeadEvent me)
        {
            if (me.RebirthTimeMS > 0)
            {
                mDeadTime = new TimeExpire<UnitDeadEvent>(me, me.RebirthTimeMS);
            }
            //所有的怪都清了一遍,都是先下发UnitDeadEvent,然后在下发死亡状态,为了防止切换状态的时候清掉特效,逻辑层的死亡特效屏蔽掉,放在表现层下
            //if (Info.DeadEffect != null)
            //{
            //    Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.DeadEffect));
            //}
            if (me.Crushed)
            {
                if (Info.CrushEffect != null)
                {
                    Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.CrushEffect));
                }
            }
        }


        private void UpdateDamage(int intervalMS)
        {
            if (mDeadTime != null && mDeadTime.Update(intervalMS))
            {
                mDeadTime = null;
            }
            if (mDamageTime != null && mDamageTime.Update(intervalMS))
            {
                mDamageTime = null;
            }
        }


        /// <summary>
        /// 落体运动
        /// </summary>
        public class FallingDown
        {
            private readonly ZoneUnit unit;
            private readonly float gravity;
            private readonly float z_limit;
            private float z_speed;
            public FallingDown(ZoneUnit unit, float zspeed, float zgravity, float zlimit)
            {
                this.unit = unit;
                this.z_limit = zlimit;
                this.z_speed = zspeed;
                this.gravity = zgravity == 0 ? unit.Templates.CFG.GLOBAL_GRAVITY : zgravity;
                this.ExpectTimeMS = MoveHelper.CalculateFlyTimeMS(unit.Z, zspeed, zlimit, gravity, 10);
                this.IsEnd = false;
            }
            public bool Update(int intervalMS)
            {
                //向上受到ZLimit限制//
                unit.Z += MoveHelper.GetDistance(intervalMS, z_speed);
                unit.Z = Math.Max(0, unit.Z);
                this.IsEnd = unit.Z <= 0;
                //骤停//
                if (z_limit != 0 && z_speed > 0 && unit.Z > z_limit)
                {
                    z_speed = 0;
                }
                this.z_speed -= MoveHelper.GetDistance(intervalMS, gravity);

                return IsEnd;
            }
            public bool IsEnd { get; private set; }
            public int ExpectTimeMS { get; private set; }
            public float ZSpeedSEC { get { return z_speed; } }
            public float ZLimit { get { return z_limit; } }
        }

        #endregion


        //--------------------------------------------------------------------------------
        #region Skill

        public float GetSkillAttackRange(SkillTemplate skill)
        {
            return IsAttackRangeIncludeBodySize ? BodySize + skill.AttackRange : skill.AttackRange;
        }

        internal void SyncSkillStatus(ClientStruct.UnitSkillStatus[] skills)
        {
            if (skills != null)
            {
                foreach (ClientStruct.UnitSkillStatus st in skills)
                {
                    SkillState status = this.GetSkillState(st.SkillTemplateID);
                    if (status != null)
                    {
                        status.Sync(st);
                    }
                }
            }
        }

        public virtual void InitSkills()
        {
            mSkillStatus.Clear();
            if (Info.BaseSkillID != null)
            {
                BaseSkillID = Info.BaseSkillID.SkillID;
                if (BaseSkillID != 0)
                {
                    SkillTemplate skt = Templates.getSkill(BaseSkillID);
                    if(skt == null)
                    {
                        ClientLog.LogError("Skill template({0}@{1}) Not Found.", BaseSkillID, Info.Name);
                        return;
                    }
                    mSkillStatus.Put(new SkillState(skt, this));
                }
            }
            else
            {
                BaseSkillID = 0;
            }

            foreach (LaunchSkill skid in Info.Skills)
            {
                if (skid.SkillID != 0)
                {
                    SkillTemplate skt = Templates.getSkill(skid.SkillID);
                    if (skt == null)
                    {
                        ClientLog.LogError("Skill template({0}@{1}) Not Found.", skid.SkillID, Info.Name);
                        return;
                    }
                    mSkillStatus.Put(new SkillState(skt, this));
                }
            }

            if (mOnSkillChanged != null)
            {
                mOnSkillChanged.Invoke(SkillOption.Init, this, BaseSkillID, mSkillStatus.Keys.ToArray());
            }
        }
        protected virtual void DoSkillChanged(PlayerSkillChangedEvent e)
        {
            this.mFastCastRate = e.unitFastCastRate;

            //一项一项排查,是不是有不同
            bool dif = false;
            if(e.baseSkill != null && e.baseSkill.TemplateID == BaseSkillID)
            {
                if(e.skills.Count == mSkillStatus.Count - 1)
                {
                    foreach(var skt in e.skills)
                    {
                        if(!mSkillStatus.ContainsKey(skt.TemplateID))
                        {
                            dif = true;
                            break;
                        }
                    }
                }
                else
                {
                    dif = true;
                }
            }
            else
            {
                dif = true;
            }

            if (!dif)
            {
                //YXJDebug.logDebug(">skills are exactly the same, why you post >PlayerSkillChangedEvent");
                return;
            }

            mSkillStatus.Clear();
            if (e.baseSkill != null)
            {
                BaseSkillID = e.baseSkill.TemplateID;
                mSkillStatus.Put(new SkillState(e.baseSkill, this));
            }
            else
            {
                BaseSkillID = 0;
            }
            foreach (var skt in e.skills)
            {
                mSkillStatus.Put(new SkillState(skt, this));
            }

            if (mOnSkillChanged != null)
            {
                mOnSkillChanged.Invoke(SkillOption.Reset, this, BaseSkillID, mSkillStatus.Keys.ToArray());
            }
        }
        protected virtual void DoPlayerSkillActiveChangedEvent(PlayerSkillActiveChangedEvent e)
        {
            for (int i = e.Skills.Count - 1; i >= 0; --i)
            {
                PlayerSkillActiveChangedEvent.State sat = e.Skills[i];
                SkillState ss = GetSkillState(sat.SkillTemplateID);
                if (ss != null)
                {
                    ss.SetActive(sat.ST);
                }
            }
            if (mOnSkillChanged != null)
            {
                mOnSkillChanged.Invoke(SkillOption.ActiveChange, this, 0, null);
            }
        }
        protected void AddSkill(SkillTemplate skill, bool isDefault)
        {
            mSkillStatus.Put(new SkillState(skill, this));
            if (isDefault)
            {
                BaseSkillID = skill.ID;
            }
            if (mOnSkillChanged != null)
            {
                mOnSkillChanged.Invoke(SkillOption.Add, this, skill.ID, mSkillStatus.Keys.ToArray());
            }
        }
        protected void RemoveSkill(int skillTemplateID)
        {
            if (mSkillStatus.RemoveByKey(skillTemplateID) != null)
            {
                if (BaseSkillID == skillTemplateID)
                {
                    BaseSkillID = 0;
                }
                if (mOnSkillChanged != null)
                {
                    mOnSkillChanged.Invoke(SkillOption.Remove, this, skillTemplateID, mSkillStatus.Keys.ToArray());
                }
            }
        }

        private void UpdateSkills(int intervalMS)
        {
            for (int i = 0; i < mSkillStatus.Count; i++)
            {
                SkillState ss = mSkillStatus.GetAt(i);
                ss.Update(intervalMS);
            }
            if (mChantingSkill != null && mChantingSkill.Update(intervalMS))
            {
                mChantingSkill = null;
            }
        }

        //新增技能使用次数
        protected virtual void DoPlayerSkillUseTimeChangedEvent(PlayerSkillUseTimeChangedEvent evt)
        {
            SkillState ss = mSkillStatus.Get(evt.SkillTemplateID);
            if (ss != null)
            {
                //UnityEngine.Debug.LogError("PlayerSkillUseTimeChangedEvent usetimes = " + evt.useTimes);
                ss.SyncSkillUseTimes(evt.useTimes);
            }
        }

        protected virtual void DoSkillAdded(PlayerSkillAddedEvent e)
        {
            this.AddSkill(e.Skill, e.IsDefault);
        }
        protected virtual void DoSkillRemoved(PlayerSkillRemovedEvent e)
        {
            this.RemoveSkill(e.SkillID);
        }

        protected virtual void DoLaunchSkill(UnitLaunchSkillEvent me)
        {
            //ClientLog.LogWarning(">doLaunchSkillAction>{0} @ {1}", me.action_index, me.skill_id);
            SkillState ss = mSkillStatus.Get(me.skill_id);
            if (ss != null)
            {
                this.mLastLaunchSkill = ss;
                ss.Launch(me);
            }
            doLaunchSkillAction(me);
            if (mOnLaunchSkill != null)
            {
                mOnLaunchSkill.Invoke(this, ss, me);
            }
        }
        protected virtual void DoSkillCDChanged(PlayerCDEvent evt)
        {
            if (evt.is_all)
            {
                for (int i = 0; i < mSkillStatus.Count; i++)
                {
                    SkillState ss = mSkillStatus.GetAt(i);
                    if (evt.is_clear)
                        ss.ClearCD();
                    else if (evt.is_decrease_time)
                        ss.DecreaseCD(evt.decrease_timeMS);
                    else if (evt.is_decrease_pct)
                        ss.DecreaseCD_Pct(evt.decrease_pct);
                }
            }
            else
            {
                SkillState ss = mSkillStatus.Get(evt.skill_template_id);
                if (ss != null)
                {
                    if (evt.is_clear)
                        ss.ClearCD();
                    else if (evt.is_decrease_time)
                        ss.DecreaseCD(evt.decrease_timeMS);
                    else if (evt.is_decrease_pct)
                        ss.DecreaseCD_Pct(evt.decrease_pct);
                }
            }
        }
        protected virtual void DoChangeAction(UnitSkillActionChangeEvent e)
        {
            if (mLastLaunchSkill != null)
            {
                mLastLaunchSkill.ChangeAction(e.ActionIndex);
            }
            if (mCurrentSkillAction != null)
            {
                mCurrentSkillAction.onUnitSkillActionChangeEvent(e);
            }
            if (mLastLaunchSkill != null && mOnSkillActionChanged != null)
            {
                mOnSkillActionChanged.Invoke(this, mLastLaunchSkill, e.ActionIndex);
            }
        }
        protected virtual void DoPlayerSkillStopEvent(PlayerSkillStopEvent e)
        {
            SkillState ss = mSkillStatus.Get(e.SkillID);
            if (ss != null)
            {
                ss.PlayerStop(e);
            }
            mChantingSkill = null;
            clearSkillAction();
        }
        protected virtual void DoPlayerSkillTimeChangedEvent(PlayerSkillTimeChangedEvent e)
        {
            SkillState ss = mSkillStatus.Get(e.SkillTemplateID);
            if (ss != null)
            {
                ss.TimeChange(e);
            }
        }
        public int BaseSkillID { get; private set; }

        internal SkillMap mSkillStatus = new SkillMap();
        private SkillState mLastLaunchSkill;

        public class SkillMap
        {
            private HashMap<int, SkillState> Map = new HashMap<int, SkillState>();
            private List<SkillState> For = new List<SkillState>();

            public int Count { get { return For.Count; } }
            public ICollection<int> Keys { get { return Map.Keys; } }
            public IEnumerable<SkillState> Skills { get { return For; } }
            public SkillState[] SkillsArray { get { return For.ToArray(); } }

            public SkillState GetAt(int i)
            {
                return For[i];
            }
            public SkillState Get(int id)
            {
                return Map.Get(id);
            }
            public void Put(SkillState state)
            {
                SkillState old = Map.Get(state.Data.ID);
                if (old != null)
                {
                    For.Remove(old);
                }
                Map.Put(state.Data.ID, state);
                For.Add(state);
            }
            public SkillState RemoveByKey(int id)
            {
                SkillState ret = Map.RemoveByKey(id);
                if (ret != null)
                {
                    For.Remove(ret);
                }
                return ret;
            }
            public bool ContainsKey(int id)
            {
                return Map.ContainsKey(id);
            }
            public void Clear()
            {
                Map.Clear();
                For.Clear();
            }
        }
        public class SkillState
        {
            readonly public SkillTemplate Data;
            readonly public ZoneUnit Owner;

            private SkillActiveState current_state;
            private int all_action_time_ms;
            private int pass_time_ms;
            private int stop_time_ms;
            private float percent = 1f;
            private float action_speed = 1f;
            private int total_cd_time_ms;
            private bool is_in_mutil_time = false;
            private bool is_period_cd_end = false;

            //新增蓄力
            private ActionEnum action_type;
            //新增使用次数
            private int use_Times;
            private bool is_use_tiems = false;
            private bool is_skill_end = false;
            private bool is_Skill_Block = false;
            //新增技能描述类型
            private SkillDescType skill_desc_type = SkillDescType.None;

            //当前技能是否还从未触发过
            private bool is_skill_never_launch;

            //CD状态变化事件
            public System.Action OnCDStateChange;

            //蓄力状态发生了变化
            public System.Action OnChargeStateChange;
            //蓄力条进度
            public float ChargeProgress { private set; get; }
            public uint ChargeTimeMS { private set; get; }

            internal SkillState(SkillTemplate data, ZoneUnit owner)
            {
                this.Data = data;
                this.Owner = owner;
                this.all_action_time_ms = Data.ActionQueueTimeMS;
                this.total_cd_time_ms = data.CoolDownMS;
                this.pass_time_ms = stop_time_ms = FullCDTimeMS;
                this.pass_time_ms = data.CoolDownMS;
                this.is_in_mutil_time = Data.IsSingleAction;
                this.use_Times = (int)data.CurUseTimes;
                this.is_use_tiems = this.use_Times >= 0 ? true : false;
                //以服务器为主,服务器那边目前没有做具体细分
                if (data.ActionPriority < 0)
                {
                    skill_desc_type = SkillDescType.Displacement;
                }
                this.is_skill_never_launch = true;
                this.CurrentActionID = -1;
                ChargeProgress = 1f;
            }

            internal bool TryLaunch(UnitLaunchSkillAction act)
            {
                if (IsActive &&
                    Owner.MP >= Data.CostMP &&
                    Owner.HP >= Data.CostHP &&
                    (!IsCD))
                {
                    return true;
                }
                return false;
            }

            internal void Launch(UnitLaunchSkillEvent evt)
            {
                this.is_skill_never_launch = false;
                this.percent = 0;
                this.pass_time_ms = 0;
                this.is_skill_end = false;
                this.CurrentActionID = evt.action_index;
                this.action_type = Data.ActionQueue[evt.action_index].SigleActionType;
                this.is_Skill_Block = Data.ActionQueue[evt.action_index].IsAddSkillBtnBlock;
                //ClientLog.LogWarning(">launch SkillState >{0} @ {1}, isMulti:{2}", CurrentActionID, this.Data.TemplateID, this.is_in_mutil_time);

                if (CurrentActionID + 1 == Data.ActionQueue.Count)
                {
                    this.is_period_cd_end = this.Data.IsSingleAction;
                }
                if (evt.IsActionSpeedUP)
                {
                    this.action_speed = evt.action_speed;
                }
                else
                {
                    this.action_speed = 1f;
                }
                if (evt.IsChangeTotalCDTime)
                {
                    this.total_cd_time_ms = evt.TotalCDTimeMS;
                }
                else
                {
                    this.total_cd_time_ms = ToFullTimeCD();
                    this.is_skill_end = true;
                    //ClientLog.LogError("is_skill_end: true");
                }
                OnCDStateChange?.Invoke();
                if (OnChargeStateChange != null)
                {
                    if (Data.ActionQueue[CurrentActionID].ShowChargeTimeMS > 0)
                    {
                        ChargeProgress = 0f;
                        ChargeTimeMS = 0;
                        OnChargeStateChange.Invoke();
                    }
                    else if (ChargeProgress > 0 && ChargeProgress < 1)
                    {
                        ChargeProgress = 1f;
                        OnChargeStateChange.Invoke();
                    }
                }
            }
            public byte NextAction()
            {
                if (Data.IsSingleAction)
                {
                    int action_step = CurrentActionID;
                    int action_time = this.FullCDTimeMS;
                    if (Data.IsCoolDownWithAction)
                    {
                        action_time = Data.ActionQueue[action_step % Data.ActionQueue.Count].TotalTimeMS;
                    }
                    // 是放技能时,处于多段攻击连击冷却时间范围
                    if (pass_time_ms - stop_time_ms < Data.SingleActionCoolDownMS || pass_time_ms < (action_time + Data.SingleActionCoolDownMS))
                    {
                        action_step += 1;
                        action_step = (byte)(action_step % Data.ActionQueue.Count);
                        return (byte)action_step;
                    }
                }
                return 0;
            }

            private int ToFullTimeCD()
            {
                if (Data.IsCoolDownWithAction)
                {
                    if (Data.IsSingleAction)
                    {
                        return Data.ActionQueue[CurrentActionID].TotalTimeMS;
                    }
                    else
                    {
                        return all_action_time_ms;
                    }
                }
                return Data.ToUnitSkillTotalTime(Owner.mFastCastRate);
            }
            internal void SetActive(SkillActiveState state)
            {
                this.current_state = state;
            }
            internal void ChangeAction(int step)
            {
                var aq = Data.ActionQueue[CurrentActionID];
                if (aq.ShowChargeTimeMS > 0 && ChargeProgress < 1)
                {
                     ChargeProgress = 1f;
                     OnChargeStateChange?.Invoke();
                }

                this.CurrentActionID = step;
            }
            internal void ClearCD()
            {
                this.pass_time_ms = FullCDTimeMS;
            }
            internal void DecreaseCD(int ms)
            {
                this.pass_time_ms += ms;
            }
            internal void DecreaseCD_Pct(float pct)
            {
                this.pass_time_ms += (int)(FullCDTimeMS * pct);
            }
            internal void PlayerStop(PlayerSkillStopEvent stop)
            {
                this.stop_time_ms = pass_time_ms;
                this.is_skill_end = true;
                this.is_Skill_Block = false;
                if (ChargeProgress > 0 && ChargeProgress < 1)
                {
                    ChargeProgress = 1f;
                    OnChargeStateChange?.Invoke();
                }
            }
            internal void Sync(ClientStruct.UnitSkillStatus syn)
            {
                this.pass_time_ms = syn.PassTime;
                this.use_Times = syn.useTimes;
                this.is_use_tiems = syn.useTimes >= 0 ? true : false;
                internal_update(0);
            }
            internal void TimeChange(PlayerSkillTimeChangedEvent e)
            {
                if (this.is_skill_never_launch)
                {
                    //重新登录后,技能的状态会丢失,这里重新算一下
                    if(e.SkillPassTimeMS < e.SkillTotalTimeMS)
                    {
                        //CD未结束的技能,action都是在最后一段(不然不会有CD)
                        this.CurrentActionID = Data.ActionQueue.Count - 1;
                        if (OnChargeStateChange != null)
                        {
                            ChargeProgress = 1f;
                            OnChargeStateChange.Invoke();
                        }
                    }
                }

                this.pass_time_ms = e.SkillPassTimeMS;
                this.total_cd_time_ms = e.SkillTotalTimeMS;
                this.is_in_mutil_time = this.Data.IsSingleAction;
                this.is_period_cd_end = this.Data.IsSingleAction;
                this.is_Skill_Block = false;
                this.is_skill_end = true;
                internal_update(0);
                OnCDStateChange?.Invoke();
                if(OnChargeStateChange != null && ChargeProgress > 0 && ChargeProgress < 1)
                {
                    ChargeProgress = 1f;
                    OnChargeStateChange.Invoke();
                }
            }
            internal void Update(int intervalMS)
            {
                if (!IsActive && IsPauseOnDeactive) { return; }
                internal_update(intervalMS);
                if (this.isPeriodCDEnd)
                {
                    SetMutilTime();
                }

                if (OnChargeStateChange != null && CurrentActionID >= 0 && CurrentActionID < Data.ActionQueue.Count)
                {
                    var aq = Data.ActionQueue[CurrentActionID];
                    if (aq.ShowChargeTimeMS > 0)
                    {
                        if (ChargeProgress < 1)
                        {
                            ChargeTimeMS += (uint)intervalMS;
                            ChargeProgress = ChargeTimeMS / (float)aq.ShowChargeTimeMS;
                            if (ChargeProgress > 1) ChargeProgress = 1f;
                            OnChargeStateChange.Invoke();
                        }
                    }
                    else if (ChargeProgress > 0 && ChargeProgress < 1)
                    {
                        ChargeProgress = 1f;
                        OnChargeStateChange.Invoke();
                    }
                }
            }
            internal void SetPassTime(int passTime)
            {
                this.pass_time_ms = passTime;
                internal_update(0);
            }

            void SetMutilTime()
            {
                if (CDPercent < 1)
                {
                    this.is_in_mutil_time = false;
                }
                else
                {
                    this.is_in_mutil_time = true;
                    this.is_period_cd_end = false;
                }
            }

            private void internal_update(int intervalMS)
            {
                pass_time_ms += intervalMS;
                if (pass_time_ms >= FullCDTimeMS)
                {
                    if (percent < 1)
                    {
                        percent = 1f;
                        stop_time_ms = pass_time_ms;
                        OnCDStateChange?.Invoke();
                    }
                }
                else
                {
                    percent = (float)pass_time_ms / FullCDTimeMS;
                }
            }

            //新增技能使用次数
            internal void SyncSkillUseTimes(int usetimes)
            {
                this.use_Times = usetimes;
                this.is_use_tiems = true;
            }

            public int FullCDTimeMS
            {
                get
                {
                    return total_cd_time_ms;
                }
            }

            public bool isMutilTime
            {
                get { return this.is_in_mutil_time; }
            }

            public bool isPeriodCDEnd
            {
                get { return this.is_period_cd_end; }
            }

            public bool IsCD
            {
                get
                {
                    if (Data.IsCoolDownWithAction)
                    {
                        return false;
                    }
                    return (percent < 1f);
                }
            }
            public float CDPercent
            {
                get
                {
                    if (Data.IsCoolDownWithAction)
                    {
                        return 1f;
                    }
                    return percent;
                }
            }
            public float ActionSpeed { get { return action_speed; } }
            public int PassTimeMS { get { return pass_time_ms; } }
            public int StopTimeMS { get { return stop_time_ms; } }
            public int CurrentActionID { get; private set; }
            public SkillActiveState ActiveState { get { return current_state; } }
            public bool IsActive { get { return current_state == SkillActiveState.Active || current_state == SkillActiveState.ActiveAndHide; } }
            public bool IsPauseOnDeactive { get { return current_state == SkillActiveState.DeactiveAndPause; } }
            public ActionEnum ActionType { get { return action_type; } }
            public int UseTimes { get { return this.use_Times; } }
            public bool IsUseTimes { get { return this.is_use_tiems; } }
            //目前仅用于蓄力,后面如果有其它类型需要用到,注意TimeChange和StopSkill消息的顺序.......
            public bool IsSkillEnd {
                get { return this.is_skill_end; }
            }
            public bool IsSkillBlock { get { return this.is_Skill_Block; } }
            public SkillDescType SkillType { get { return this.skill_desc_type; } }
        }

        //[Obsolete]
        public List<SkillState> GetSkillStatus()
        {
            return new List<SkillState>(mSkillStatus.Skills);
        }
        public void GetSkillStatus(List<SkillState> ret)
        {
            ret.AddRange(mSkillStatus.Skills);
        }

        public SkillState GetSkillState(int templateID)
        {
            return mSkillStatus.Get(templateID);
        }

        #endregion

        //---------------------------------------------------------------------------
        // skill action
        //---------------------------------------------------------------------------
        #region SkillAction

        /// <summary>
        /// 是否正在吟唱
        /// </summary>
        public bool IsChanttingSkill
        {
            get { return mChantingSkill != null; }
        }
        public int ChantingSkillPassMS
        {
            get { return (mChantingSkill != null) ? mChantingSkill.PassTimeMS : 0; }
        }
        public int ChantingSkillTotalMS
        {
            get { return (mChantingSkill != null) ? mChantingSkill.TotalTimeMS : 0; }
        }
        public SkillTemplate ChantingSkillData
        {
            get { return (mChantingSkill != null) ? mChantingSkill.Tag : null; }
        }
        public SkillState ChantingSkill
        {
            get { if (mChantingSkill != null) { return GetSkillState(mChantingSkill.Tag.TemplateID); } return null; }
        }
        private TimeExpire<SkillTemplate> mChantingSkill;

        protected virtual void DoUnitChantSkillEvent(UnitChantSkillEvent e)
        {
            var temp = Templates.getSkill(e.skill_id);
            mChantingSkill = new TimeExpire<SkillTemplate>(temp, e.chant_ms);
            SkillState ss = GetSkillState(e.skill_id);
            if (ss != null)
            {
                if (mOnChantSkill != null)
                {
                    mOnChantSkill.Invoke(this, e, mChantingSkill);
                }
            }
        }
        protected virtual void doLaunchSkillAction(UnitLaunchSkillEvent me)
        {
            clearSkillAction();
            var temp = Templates.getSkill(me.skill_id);
            if (temp != null)
            {
                mCurrentSkillAction = new UnitSkillAction(this, temp);
                mCurrentSkillAction.onLaunch(me);
                invokeSkillActionStart(mCurrentSkillAction);
            }
        }
        protected virtual void invokeSkillActionStart(ISkillAction act)
        {
            if (mOnSkillActionStart != null)
            {
                mOnSkillActionStart.Invoke(this, act);
            }
        }
        public virtual ISkillAction CurrentSkillAction
        {
            get { return mCurrentSkillAction; }
        }
        public virtual UnitActionData CurrentSkillActionData
        {
            get
            {
                if (CurrentSkillAction != null)
                {
                    return CurrentSkillAction.CurrentAction;
                }
                return null;
            }
        }

        //-------------------------------------------------------------------------------------------
        // unit impl
        private UnitSkillAction mCurrentSkillAction;
        private void updateSkillAction(int intervalMS)
        {
            if (mCurrentSkillAction != null)
            {
                //UnityEngine.Debug.LogError("2 mCurrentSkillAction = " + mCurrentSkillAction.SkillData.TemplateID + "  actionIndex = " + mCurrentSkillAction.CurrentActionIndex);
                mCurrentSkillAction.onUpdate(intervalMS);
                if (mCurrentSkillAction.IsDone)
                {
                    clearSkillAction();
                }
            }
        }
        private void clearSkillAction()
        {
            if (mCurrentSkillAction != null)
            {
                mCurrentSkillAction.onStop();
                mCurrentSkillAction = null;
            }
        }

        public abstract class ISkillAction
        {
            private BitSet8 current_action_status = new BitSet8();
            public bool IsControlMoveable { get { return current_action_status.Get(0); } protected set { current_action_status.Set(0, value); } }
            public bool IsControlFaceable { get { return current_action_status.Get(1); } protected set { current_action_status.Set(1, value); } }
            public bool IsCancelableBySkill { get { return current_action_status.Get(2); } protected set { current_action_status.Set(2, value); } }
            public bool IsCancelableByMove { get { return current_action_status.Get(3); } protected set { current_action_status.Set(3, value); } }
            public bool IsNoneBlock { get { return current_action_status.Get(4); } protected set { current_action_status.Set(4, value); } }
            public bool IsNoneTouch { get { return current_action_status.Get(5); } protected set { current_action_status.Set(5, value); } }
            public bool IsFaceToTarget { get { return current_action_status.Get(6); } protected set { current_action_status.Set(6, value); } }

            public abstract SkillTemplate SkillData { get; }
            public abstract UnitLaunchSkillEvent LaunchEvent { get; }
            public abstract bool IsDone { get; }
            public abstract float ActionSpeed { get; }
            public abstract byte ActionStepIndex { get; }
            public abstract int[] ActionTimeArray { get; }
            public abstract int TotalTimeMS { get; }
            public abstract int CurrentActionIndex { get; }
            public abstract string CurrentActionName { get; }
            public abstract UnitActionData CurrentAction { get; }
            public abstract float ExpirePercent { get; }

            //把当前的执行时间抛出去给客户端用,很违和的做法,没办法数据配在扩展属性里面
            //CommonAI取不出来,强行操作会引发不可控的Bug
            public abstract int CurPassTimeMS { get; }

            public abstract bool IsJumpAction { get; }
            
            internal abstract void onLaunch(UnitLaunchSkillEvent e);
            internal abstract void onUnitSkillActionChangeEvent(UnitSkillActionChangeEvent evt);
            internal abstract void onUpdate(int intervalMS);
            internal abstract void onStop();
            internal abstract void RestJumpAction(bool isJumpAction);
        }

        public class UnitSkillAction : ISkillAction
        {
            readonly private ZoneUnit ownerUnit;
            readonly private SkillTemplate skill;
            private UnitLaunchSkillEvent launch_event;

            private int current_pass_time = 0;
            private int total_pass_time = 0;
            private int total_time_ms = 0;
            private bool is_done = false;
            private bool is_jump_action = false;
            private Queue<UnitActionData> action_queue;
            private int current_action_index = 0;
            private UnitActionData current_action = null;
            private PopupKeyFrames<UnitActionData.KeyFrame> current_frames = new PopupKeyFrames<UnitActionData.KeyFrame>();
            private int current_action_total_time = 0;
            private int[] action_time_array;

            public ZoneLayer Parent { get { return ownerUnit.Parent; } }

            public override int[] ActionTimeArray { get { return action_time_array; } }
            public override int TotalTimeMS { get { return total_time_ms; } }
            
            public override byte ActionStepIndex { get { return (launch_event != null) ? launch_event.action_index : (byte)0; } }
            public override int CurrentActionIndex { get { return current_action_index; } }
            public override string CurrentActionName { get { return (current_action != null) ? current_action.ActionName : null; } }
            public override UnitActionData CurrentAction { get { return current_action; } }
            public override float ExpirePercent { get { return total_pass_time / (float)TotalTimeMS; } }
            public override SkillTemplate SkillData { get { return skill; } }
            public override UnitLaunchSkillEvent LaunchEvent { get { return launch_event; } }
            public override bool IsDone { get { return is_done; } }
            public override int CurPassTimeMS { get { return current_pass_time; } }

            protected internal UnitSkillAction(ZoneUnit actor, SkillTemplate skill)
            {
                this.skill = skill;
                this.ownerUnit = actor;
                this.action_time_array = skill.ActionQueueTimeArray;
            }

            public override float ActionSpeed
            {
                get
                {
                    if (launch_event != null)
                    {
                        if(launch_event.action_speed_add != null && launch_event.action_index < launch_event.action_speed_add.Length)
                        {
                            return launch_event.action_speed + launch_event.action_speed_add[launch_event.action_index];
                        }

                        return launch_event.action_speed;
                    }

                    return 1f;
                }
            }

            public override bool IsJumpAction
            {
                get
                {
                    return is_jump_action;
                }
            }

            internal override void onLaunch(UnitLaunchSkillEvent e)
            {
                //UnityEngine.Debug.LogError("SkillID = " + e.skill_id + "   ActionSpeed = " + e.action_speed + "   action_index = " + e.action_index);
                this.launch_event = e;
                this.total_pass_time = 0;
                this.current_pass_time = 0;
                if (e.action_time_array != null && e.action_time_array.Length > 0)
                {
                    this.total_time_ms = e.TotalActionTimeMS;
                    this.action_time_array = e.action_time_array;
                }
                else if (skill.IsSingleAction)
                {
                    this.total_time_ms = skill.ActionQueue[launch_event.action_index].TotalTimeMS;
                    this.action_time_array = new int[] { TotalTimeMS };
                }
                else
                {
                    this.total_time_ms = skill.ActionQueueTimeMS;
                    this.action_time_array = skill.ActionQueueTimeArray;
                }
                if (skill.IsSingleAction)
                {
                    var sa = skill.ActionQueue[launch_event.action_index];
                    this.action_queue = new Queue<UnitActionData>(1);
                    this.action_queue.Enqueue(sa);
                    this.current_action_index = launch_event.action_index;
                }
                else
                {
                    this.action_queue = new Queue<UnitActionData>(skill.ActionQueue);
                    this.current_action_index = launch_event.action_index;
                }

                this.current_action = null;
                this.nextAction(this.current_action_index);
            }
            internal override void onUnitSkillActionChangeEvent(UnitSkillActionChangeEvent e)
            {
                while (this.CurrentActionIndex < e.ActionIndex)
                {
                    if (!nextAction(current_action_index + 1))
                    {
                        this.total_pass_time = TotalTimeMS;
                        this.is_done = true;
                        break;
                    }
                }
            }
            internal override void onStop()
            {
                this.is_done = true;
                this.total_pass_time = TotalTimeMS;
            }
            internal override void onUpdate(int intervalMS)
            {
                int time_pass = (int)(intervalMS * ActionSpeed);
                this.total_pass_time += time_pass;
                this.total_pass_time = Math.Min(total_pass_time, TotalTimeMS);
                this.current_pass_time += time_pass;

                if (current_action == null)
                {
                    nextAction(current_action_index + 1);
                }
                if (current_action == null)
                {
                    this.is_done = true;
                    this.total_pass_time = TotalTimeMS;
                }
                else
                {
                    // 关键帧 //
                    using (var kfs = ListObjectPool<UnitActionData.KeyFrame>.AllocAutoRelease())
                    {
                        if (current_frames.PopKeyFrames(current_pass_time, kfs) > 0)
                        {
                            for (int i = 0; i < kfs.Count; i++)
                            {
                                doKeyFrame(kfs[i]);
                            }
                        }
                    }
                    // 下段动作 //
                    if ((current_action != null) && (current_pass_time >= current_action_total_time))
                    {
                        current_action = null;
                        if (action_queue.Count == 0)
                        {
                            this.is_done = true;
                            this.total_pass_time = TotalTimeMS;
                        }
                    }
                }
            }

            private bool nextAction(int index)
            {
                if (action_queue != null && action_queue.Count > 0)
                {
                    this.current_action_index = index;
                    this.current_action = action_queue.Dequeue();
                    if (current_action != null)
                    {
                        this.current_frames.AddRange(current_action.KeyFrames);
                        this.current_pass_time = 0;
                        //NewAction//
                        if (launch_event.action_time_array != null)
                        {
                            if (skill.IsSingleAction)
                            {
                                this.current_action_total_time = launch_event.TotalActionTimeMS;
                            }
                            else if (current_action_index < launch_event.action_time_array.Length)
                            {
                                this.current_action_total_time = launch_event.action_time_array[current_action_index];
                            }
                        }
                        else
                        {
                            this.current_action_total_time = current_action.TotalTimeMS;
                        }
                        this.IsCancelableBySkill = current_action.IsCancelableBySkill;
                        this.IsNoneBlock = current_action.IsNoneBlock;
                        this.IsNoneTouch = current_action.IsNoneTouch;
                        this.IsFaceToTarget = current_action.IsFaceToTarget;
                        this.IsCancelableByMove = current_action.IsCancelable;
                        this.IsControlMoveable = current_action.IsControlMoveable;
                        this.IsControlFaceable = current_action.IsControlFaceable;
                        return true;
                    }
                }
                current_action = null;
                return false;
            }

            private void doKeyFrame(UnitActionData.KeyFrame kf)
            {
                // 关键帧改变状态
                if (kf.ChangeStatus != null)
                {
                    this.IsNoneBlock = kf.ChangeStatus.IsNoneBlock;
                    this.IsNoneTouch = kf.ChangeStatus.IsNoneTouch;
                    this.IsFaceToTarget = kf.ChangeStatus.IsFaceToTarget;
                    this.IsCancelableByMove = kf.ChangeStatus.IsCancelable;
                    this.IsCancelableBySkill = kf.ChangeStatus.IsCancelableBySkill;
                    this.IsControlMoveable = kf.ChangeStatus.IsControlMoveable;
                    this.IsControlFaceable = kf.ChangeStatus.IsControlFaceable;
                }
            }

            internal override void RestJumpAction(bool isJumpAction)
            {
                is_jump_action = isJumpAction;
            }
        }

        //-------------------------------------------------------------------------------------------

        #endregion

        //--------------------------------------------------------------------------------

        #region Buff

        internal BuffMap mBuffStatus = new BuffMap();
        public BuffMap AllBuffs { get { return mBuffStatus; } }

        internal void SyncBuffStatus(ClientStruct.UnitBuffStatus[] buffs)
        {
            if (buffs != null)
            {
                for (int i = 0; i < buffs.Length; i++)
                {
                    ClientStruct.UnitBuffStatus st = buffs[i];
                    TryAddBuff(st.BuffTemplateID, st.SenderID, st.IsEquip, st.TotalTime, st.OverlayLevel, st.PassTime);
                }
            }
        }

        protected void SyncBuffOverlayLevel(BuffDataNotify data)
        {
            BuffState bs = mBuffStatus.Get(data.buffId, data.senderId);
            if (bs != null)
            {
                bs.SyncBuffExtData(data.overlayLevel, bs.BuffExtendData);
                //YXJDebug.logError(data.buffId + " , level:" + bs.OverlayLevel + " time = " + bs.BuffTotalTime + ", per:"+bs.CDPercent);
                if (mOnBuffChanged != null)
                {
                    mOnBuffChanged.Invoke(this, bs);
                }
            }
        }

        protected virtual void DoLaunchBuff(UnitLaunchBuffEvent me)
        {
            //YXJDebug.logError(me.buffTemplateID +  " overlayLevel = " + me.overlayLevel + " time = " + me.buffTimeMS);
            TryAddBuff(me.buffTemplateID, me.senderID, me.IsEquip, me.buffTimeMS, me.overlayLevel, 0, me.buffExtData);
        }

        protected virtual void DoStopBuff(UnitStopBuffEvent e)
        {
            TryRemoveBuff(e.buffTemplateID, e.senderID);
        }

        //新增Buff接口
        protected virtual void DoRemoveOverlayLevelBuff(SyncUnitBuffState e)
        {
            //UnityEngine.Debug.LogError("DoRemoveOverlayLevelBuff overlayLevel = " + e.overlayLevel + "   buffExtData = " + e.buffExtData);
            if (e.buffExtData > 0)
            {
                var bs = mBuffStatus.Get(e.buffTemplateID, e.ObjectID);
                if (bs != null && mOnRemoveBuffByOverlayLeveled != null)
                {
                    mOnRemoveBuffByOverlayLeveled(this, bs);
                    bs.SyncBuffExtData(e.overlayLevel, e.buffExtData);
                }
            }
            else
            {
                TryRemoveBuff(e.buffTemplateID, e.ObjectID);
            }
        }

        private void TryAddBuff(int buffTempalteID, uint senderID, bool isEquip, int totalTime, byte overlayLevel, int passTime = 0, int buffExtData = 0)
        {
            BuffTemplate buff = Templates.getBuff(buffTempalteID);
            if (buff != null)
            {
                BuffState bs = mBuffStatus.Get(buffTempalteID, senderID);
                if (bs != null)
                {
                    bs.Sync(overlayLevel, totalTime, passTime, buffExtData);
                    if (mOnBuffChanged != null)
                    {
                        mOnBuffChanged.Invoke(this, bs);
                    }
                }
                else
                {
                    bs = new BuffState(buff, this, senderID, isEquip);
                    bs.Sync(overlayLevel, totalTime, passTime, buffExtData);
                    mBuffStatus.Put(bs);
                    if (mOnBuffAdded != null)
                    {
                        mOnBuffAdded.Invoke(this, bs);
                    }
                }

                //UnityEngine.Debug.LogError("Server buffID = " + buffTempalteID + "  LockStateAction = " + buff.LockStateAction);
                //新增单独状态
                if (!string.IsNullOrEmpty(buff.LockStateAction))
                {
                    this.SetCurrentState(UnitActionStatus.BuffLockStateAction, CurrentSubState, buff);
                }
            }
        }
        private void TryRemoveBuff(int buffTempalteID, uint senderID)
        {
            BuffState bs = mBuffStatus.RemoveByKey(buffTempalteID, senderID);
            if (bs != null)
            {
                bs.OnEnd();
                if (mOnBuffRemoved != null)
                {
                    mOnBuffRemoved.Invoke(this, bs);
                }

                //UnityEngine.Debug.LogError("RemoveBuff  CurrentState= " + CurrentState);
                //置回单独状态
                if (CurrentState == UnitActionStatus.BuffLockStateAction)
                {
                    this.SetCurrentState(UnitActionStatus.Idle);
                }
            }
        }
        private void UpdateBuffs(int intervalMS)
        {
            mBuffStatus.ForEachUpdate(intervalMS);
            //mBuffStatus.ForEach((bs) =>
            //{
            //    bs.OnUpdate(intervalMS);
            //});
        }

        //返回当前buff列表中,是否含有变身buff
        public bool HasMakeAvatarBuff()
        {
            bool has = false;
            mBuffStatus.ForEach((BuffState buf) => {
                if (buf.Data.MakeAvatar)
                {
                    has = true;
                }
            });
            return has;
        }
        //返回当前buff列表中,是否含有切换技能buff
        public bool HasChangeSkillBuff()
        {
            bool has = false;
            mBuffStatus.ForEach((BuffState buf) => {
                if (buf.Data.UnitChangeSkills)
                {
                    has = true;
                }
            });
            return has;
        }

        public class BuffMap
        {
            private HashMap<int, BuffList> Map = new HashMap<int, BuffList>();
            private List<BuffList> For = new List<BuffList>();

            public BuffState Get(int id, uint senderID)
            {
                var list = Map.Get(id);
                if (list != null)
                {
                    return list.Get(senderID);
                }
                return null;
            }
            internal void Put(BuffState state)
            {
                var list = Map.Get(state.BuffID);
                if (list == null)
                {
                    list = new BuffList(state.Data);
                    Map.Add(state.BuffID, list);
                    For.Add(list);
                }
                list.Add(state);
            }
            internal BuffState RemoveByKey(int id, uint senderID)
            {
                var list = Map.Get(id);
                if (list != null)
                {
                    var st = list.Get(senderID);
                    if (st != null)
                    {
                        var ret = list.Remove(senderID);
                        if (list.Count == 0)
                        {
                            For.Remove(list);
                            Map.Remove(id);
                        }
                        return ret;
                    }
                }
                return null;
            }
            internal void Clear()
            {
                For.Clear();
                Map.Clear();
            }

            public void ForEach(Action<BuffState> action)
            {
                for (int i = For.Count - 1; i >= 0; --i)
                {
                    For[i].ForEach(action);
                }
            }

            public int GetBuffCount()
            {
                return For.Count;
            }


            public void ForEachUpdate(int intervalMS)
            {
                for (int i = For.Count - 1; i >= 0; --i)
                {
                    For[i].ForEachUpdate(intervalMS);
                }
            }


            public class BuffList
            {
                public readonly BuffTemplate Data;
                private HashMap<uint, BuffState> Map = new HashMap<uint, BuffState>();
                private List<BuffState> For = new List<BuffState>();
                public int Count { get { return Map.Count; } }
                internal BuffList(BuffTemplate data)
                {
                    this.Data = data;
                }
                internal BuffState Remove(uint senderID)
                {
                    BuffState ret;
                    if (Data.IsDuplicating)
                    {
                        ret = Map.RemoveByKey(senderID);
                    }
                    else
                    {
                        ret = Map.RemoveByKey(0);
                    }
                    if (ret != null)
                    {
                        For.Remove(ret);
                    }
                    return ret;
                }
                internal void Add(BuffState state)
                {
                    if (Data.IsDuplicating)
                    {
                        Map.Add(state.SenderID, state);
                    }
                    else
                    {
                        Map.Add(0, state);
                    }
                    For.Add(state);
                }
                internal BuffState Get(uint senderID)
                {
                    if (Data.IsDuplicating)
                    {
                        if (senderID == 0 && Map.Count > 0)
                        {
                            return Map.First().Value;
                        }
                        else
                        {
                            return Map.Get(senderID);
                        }
                    }
                    else
                    {
                        return Map.Get(0);
                    }
                }

                internal void ForEach(Action<BuffState> action)
                {
                    for (int i = For.Count - 1; i >= 0; --i)
                    {
                        action(For[i]);
                    }
                }

                public void ForEachUpdate(int intervalMS)
                {
                    for (int i = For.Count - 1; i >= 0; --i)
                    {
                        For[i].OnUpdate(intervalMS);
                    }
                }
            }
        }
        public class BuffState
        {
            readonly public BuffTemplate Data;
            readonly public bool isEquip;
            readonly public ZoneUnit Owner;
            readonly public uint SenderID;

            private byte overlay_level;
            private int total_time;
            private int pass_time = 0;
            private float percent = 1f;
            private int buff_ext_data = 0;
            private int buff_ext_data_index = -1;

            private TimeInterval<BuffTemplate.KeyFrame> interval;
            private PopupKeyFrames<BuffTemplate.KeyFrame> keyframes;

            internal BuffState(BuffTemplate data, ZoneUnit owner, uint senderID, bool isEquip)
            {
                this.Data = data;
                this.isEquip = isEquip;
                this.Owner = owner;
                this.SenderID = senderID;
            }

            internal void Sync(byte overlayLevel, int totalTime, int passTime, int buffExtData = 0)
            {
                this.overlay_level = overlayLevel;
                this.total_time = totalTime;
                if (passTime < this.pass_time || this.pass_time == 0)
                {
                    this.interval = new TimeInterval<BuffTemplate.KeyFrame>(Data.HitIntervalMS);
                    this.interval.Tag = Data.HitKeyFrame;
                    this.keyframes = new PopupKeyFrames<BuffTemplate.KeyFrame>();
                    this.keyframes.AddRange(Data.KeyFrames);
                    this.pass_time = 0;
                }
                if (passTime > this.pass_time)
                {
                    this.interval.SetPassTime(passTime);
                    this.keyframes.PopKeyFrames(passTime, null);
                    this.pass_time = passTime;
                }
                this.percent = Math.Min(1, pass_time / (float)total_time);
                this.buff_ext_data = buffExtData;
                SetBuffExtendDataIndex(buffExtData);
            }

            internal void OnEnd()
            {
                if (Data.EndKeyFrame != null && Data.EndKeyFrame.Effect != null)
                {
                    Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, Data.EndKeyFrame.Effect));
                }
            }

            internal void OnUpdate(int intervalMS)
            {
                using (var kfs = ListObjectPool<BuffTemplate.KeyFrame>.AllocAutoRelease())
                {
                    if (keyframes.PopKeyFrames(pass_time, kfs) > 0)
                    {
                        for (int i = 0; i < kfs.Count; i++)
                        {
                            BuffTemplate.KeyFrame kf = kfs[i];
                            if (kf.Effect != null)
                            {
                                Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, kf.Effect));
                            }
                        }
                    }
                }
                if (interval.Update(intervalMS))
                {
                    if (Data.HitKeyFrame != null && Data.HitKeyFrame.Effect != null)
                    {
                        Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, Data.HitKeyFrame.Effect));
                    }
                }
                this.pass_time += intervalMS;
                if (percent < 1f)
                {
                    this.percent = Math.Min(1, pass_time / (float)total_time);
                }
            }

            public void SyncBuffExtData(byte overlayLevel, int buffExtData)
            {
                overlay_level = overlayLevel;
                buff_ext_data = buffExtData;
            }

            private void SetBuffExtendDataIndex(int curBuffExtData)
            {
                if (curBuffExtData < 1) return;
                if (curBuffExtData < 10)
                {
                    buff_ext_data_index = curBuffExtData;
                }
                else
                {
                    string curString = Convert.ToString(curBuffExtData);
                    var str = curString.Substring(curString.Length - 1, 1);
                    if (!string.IsNullOrEmpty(str))
                    {
                        int.TryParse(str, out buff_ext_data_index);
                    }
                }
            }
            
            public int BuffID { get { return Data.ID; } }
            public bool IsDone { get { return (isEquip) ? false : (percent >= 1f); } }
            public float CDPercent { get { return percent; } }
            public byte OverlayLevel { get { return overlay_level; } }

            public int BuffTotalTime { get { return total_time; } }
            public int BuffLeftTime { get { return total_time - pass_time; } }
            public int BuffExtendData { get { return buff_ext_data; } }
            public int BuffExtendDataIndex { get { return buff_ext_data_index; } }
        }

        [Obsolete]
        public List<BuffState> GetBuffStatus()
        {
            var list = new List<BuffState>();
            GetBuffStatus(list);
            return list;
        }
        public void GetBuffStatus(List<BuffState> ret)
        {
            mBuffStatus.ForEach((bs) => { ret.Add(bs); });
        }

        public int GetBuffStatusCount()
        {
            return mBuffStatus.GetBuffCount();
        }

        public BuffState GetBuff(int tempID, uint senderID)
        {
            return mBuffStatus.Get(tempID, senderID);
        }
        public BuffState GetBuff(int tempID)
        {
            return mBuffStatus.Get(tempID, 0);
        }

        #endregion

        //--------------------------------------------------------------------------------
        #region PickObject

        private TimeExpire<UnitStartPickObjectEvent> mPickEvent;
        public TimeExpire<UnitStartPickObjectEvent> PickEvent { get { return mPickEvent; } }

        protected virtual void DoStartPick(UnitStartPickObjectEvent pick)
        {
            mPickEvent = new TimeExpire<UnitStartPickObjectEvent>(pick, pick.PickTimeMS);
            SetCurrentState(UnitActionStatus.Pick, pick);
            if (mOnStartPickObject != null)
            {
                mOnStartPickObject.Invoke(this, mPickEvent);
            }
        }
        protected virtual void DoStopPick(UnitStopPickObjectEvent pick)
        {
            if (mPickEvent != null)
            {
                mPickEvent.End();
                mPickEvent = null;
            }
            if (mOnStopPickObject != null)
            {
                mOnStopPickObject.Invoke(this, pick);
            }
        }

        private void UpdatePickEvent(int intervalMS)
        {
            if (mPickEvent != null)
            {
                mPickEvent.Update(intervalMS);
            }
        }

        #endregion
        //-------------------------------------------------------------------------------- 

        #region 本地技能预演
        protected virtual void DoJumpToTargetEnd()
        {
            if (mOnClientSkillSimulation != null)
            {
                mOnClientSkillSimulation.Invoke(this, SimulationSkillEnum.JumpEnd);
            }
        }
        #endregion 本地技能预演

        #region MultiTimeLine


        /// <summary>
        /// 是否可被发现
        /// </summary>
        virtual public bool IsVisible { get { return !IsTimeLineEnable(2); } }
        /// <summary>
        /// 是否无敌
        /// </summary>
        virtual public bool IsInvincible { get { return IsTimeLineEnable(3); } }
        /// <summary>
        /// 此单位是否霸体
        /// </summary>
        virtual public bool IsNoneBlock { get { return IsTimeLineEnable(0); } }
        /// <summary>
        /// 是否为眩晕
        /// </summary>
        virtual public bool IsStun { get { return IsTimeLineEnable(1); } }
        /// <summary>
        /// 是否沉默
        /// </summary>
        virtual public bool IsSilent { get { return IsTimeLineEnable(4); } }
        //--------------------------------------------------------------------------------

        private UnitSyncMultiTimeLine mMultiTimeLine;

        private void DoUnitSyncMultiTimeLine(UnitSyncMultiTimeLine e)
        {
            this.mMultiTimeLine = e;
        }

        /// <summary>
        /// 指定TimeLine是否还有任务
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool IsTimeLineEnable(int index)
        {
            if (mMultiTimeLine != null && mMultiTimeLine.timelines != null && index < mMultiTimeLine.timelines.Count && index >= 0)
            {
                return mMultiTimeLine.timelines[index];
            }
            return false;
        }

        public void GetMultiTimeLineStatus(List<bool> list)
        {
            if (mMultiTimeLine != null && mMultiTimeLine.timelines != null)
            {
                list.AddRange(mMultiTimeLine.timelines);
            }
        }

        #endregion
    }
}