using CommonAI.Data;
using CommonAI.RTS;
using CommonAI.Zone.EventTrigger;
using CommonAI.Zone.Formula;
using CommonAI.Zone.Helper;
using CommonAI.ZoneClient;
using CommonAI.ZoneEditor;
using CommonAI.ZoneServer.JSGModule;
using CommonLang;
using CommonLang.Concurrent;
using CommonLang.IO;
using CommonLang.Log;
using CommonLang.Property;
using CommonLang.Vector;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using static CommonAI.Zone.SpellTemplate;
using static CommonAI.Zone.UnitInfo;

namespace CommonAI.Zone.Instance
{
    /// <summary>
    /// 服务端场景
    /// </summary>
    public partial class InstanceZone : GameEntity
    {
        private static AtomicInteger s_alloc_zone_count = new AtomicInteger(0);
        private static AtomicInteger s_active_zone_count = new AtomicInteger(0);

        /// <summary>
        /// 分配实例数量
        /// </summary>
        public static int AllocZoneCount { get { return s_alloc_zone_count.Value; } }
        /// <summary>
        /// 未释放实例数量
        /// </summary>
        public static int ActiveZoneCount { get { return s_active_zone_count.Value; } }

        //------------------------------------------------------------------------
        // 基础数据

        private InstanceZoneListener mListener;
        private TemplateManager mTemplates;
        private IFormula mFormula = TemplateManager.Factory.Formula;
        private IQuestAdapter mQuestAdapter;

        private SyncMessageQueue<Action> mSyncActionQueue = new SyncMessageQueue<Action>();
        private ConcurrentQueue<Event> mSendingEvents = new ConcurrentQueue<Event>();
        private InstanceZoneObjectMap mObjects;

        public TemplateManager Templates { get { return mTemplates; } }
        public IQuestAdapter QuestAdapter { get { return mQuestAdapter; } }
        public int UpdateIntervalMS { get { return mLastInterval; } }

        private readonly int mMaxUnitCount;
        private int mLastInterval = 0;
        private ulong mTimer = 0;
        private long mCurPassTimeMS = 0;
        private long mQueryPassTimeMS = 0;
        private int mQueryPassTimeSEC = 0;
        private bool mHalfSync = false;
        readonly public long mCreateTime;
        readonly private Random random;
        readonly private Logger log;

        public string UUID { get; set; }

        //记录触发器总览,避免日志过多
        public long mTotalTimeUse;
        public long mTotalTrigers;

        //------------------------------------------------------------------------
        readonly private ZoneInfo m_TerrainSrc;
        readonly private SpaceDivision mSpaceDiv;

        public ZoneInfo TerrainSrc { get { return m_TerrainSrc; } }
        public ZoneInfo Terrain { get { return path_terrain_data.Data; } }

        public int SpaceDivSize { get; private set; }
        [Desc("单位当前帧位移的最小距离")]
        public float MinStep { get; private set; }
        [Desc("最大单位数量")]
        public int MaxUnitCount { get { return mMaxUnitCount; } }
        [Desc("场景中是否存在Area")]
        public bool HasArea { get { return mHasArea; } }

        //山大王id
        protected int mKingID;
        //绑定服务器
        protected string mBindGameSrvId;

        //------------------------------------------------------------------------
        /// <summary>
        /// 
        /// </summary>
        /// <param name="templates"></param>
        /// <param name="listener">消息接收者</param>
        /// <param name="data">场景数据</param>
        /// <param name="spaceDivSize">空间分割参数</param>
        /// <param name="maxUnitCount">最大单位数</param>
        /// <param name="randomSeed">随机种子</param>
        internal InstanceZone(TemplateManager templates, InstanceZoneListener listener, ZoneEditor.SceneData data, GSCreateAreaData gsData,
            int spaceDivSize, int maxUnitCount, int randomSeed)
        {
            mCreateTime = TimeUtil.GetTimestampMS();
            InstanceZone.s_alloc_zone_count++;
            InstanceZone.s_active_zone_count++;
            var info = data.ZoneData;
            this.log = LoggerFactory.GetLogger(string.Format("InstanceZone({0})", info.TemplateID));
            this.random = TemplateManager.Factory.CreateRandom(randomSeed);// new Random(randomSeed);
            this.mTemplates = templates;
            this.IsSyncZ = false;
            this.MinStep = MoveHelper.GetDistance(1000 / templates.CFG.SYSTEM_FPS, templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC);
            if (spaceDivSize < 1)
            {
                throw new Exception("SpaceDivSize must large than map 1 !");
            }
            this.mMaxUnitCount = maxUnitCount;
            this.mListener = listener;
            this.m_TerrainSrc = info;
            this.SpaceDivSize = spaceDivSize;
            this.mSpaceDiv = new SpaceDivision(
                m_TerrainSrc.TotalWidth,
                m_TerrainSrc.TotalHeight,
                spaceDivSize * m_TerrainSrc.GridCellW,
                spaceDivSize * m_TerrainSrc.GridCellH);
            this.mObjects = new InstanceZoneObjectMap("zoneId:" + this.GetSceneID());
            //             this.path_terrain_data = new InstanceZoneManhattanMap(templates, info.Clone() as ZoneInfo);
            //             this.path_finder = new AstarManhattan(path_terrain_data, true, spaceDivSize);
            //             this.path_terrain_area_gen = new ManhattanMapAreaGenerator(path_terrain_data.Data);
            this.InitTerrain(data, spaceDivSize, out this.path_terrain_data, out this.path_finder, out this.path_terrain_area_gen);
            this.mQuestAdapter = TemplateManager.Factory.CreateQuestAdapter(this);
            this.baseInit(data, gsData);
        }

        protected virtual void baseInit(ZoneEditor.SceneData data, GSCreateAreaData gsData)
        {

        }


        // -----------------------------------------------------------------------------------
        ~InstanceZone()
        {
            InstanceZone.s_alloc_zone_count--;
        }
        protected override void Disposing()
        {
            base.Disposing();
            this.ClearEvents();
            foreach (var obj in mObjects.Objects)
            {
                obj.Dispose();
            }
            this.mObjects.Dispose();
            this.mObjects = null;
            foreach (var flg in mFlags.Values)
            {
                flg.Dispose();
            }
            this.mFlags.Clear();
            this.DisposeTerrain();
            this.mSpaceDiv.Dispose();

            this.mQuestAdapter.Dispose();
            this.mQuestAdapter = null;
            this.mListener = null;
            this.mTemplates = null;
            this.mFormula = null;
            this.mSyncActionQueue = null;
            this.mSendingEvents = null;
            this.mTasks.Clear();
            this.mTasks = null;
            this.mTimeTasks.Dispose();
            this.mTimeTasks = null;
            this.EnvironmentVarMap.Clear();
            this.mFlags.Clear();

            this.m_UnitsPos.Clear();
            this.m_UnitsStates.Clear();

            InstanceZone.s_active_zone_count--;
        }
        // -----------------------------------------------------------------------------------
        protected Logger Log { get { return log; } }

        public int TotalWidth { get { return m_TerrainSrc.TotalWidth; } }
        public int TotalHeight { get { return m_TerrainSrc.TotalHeight; } }
        public int GridCellW { get { return m_TerrainSrc.GridCellW; } }
        public int GridCellH { get { return m_TerrainSrc.GridCellH; } }
        public int XCount { get { return m_TerrainSrc.XCount; } }
        public int YCount { get { return m_TerrainSrc.YCount; } }
        public ulong Tick { get { return mTimer; } }

        /// <summary>
        /// 是否发 SyncPosEvent 包
        /// </summary>
        public bool SyncPos
        {
            get { return sync_pos_list.Enable; }
            set { sync_pos_list.Enable = value; }
        }
        /// <summary>
        /// 是否为低速率网络同步(将优化为半字通信)
        /// </summary>
        public bool IsHalfSync
        {
            get { return mHalfSync; }
            set
            {
                if (value && TotalWidth <= 255 && TotalHeight <= 255)
                {
                    mHalfSync = value;
                }
                else
                {
                    mHalfSync = false;
                }
            }
        }
        /// <summary>
        /// 同步包是否包含Z
        /// </summary>
        public bool IsSyncZ
        {
            get;
            set;
        }
        public long PassTimeMS { get { return mQueryPassTimeMS; } }
        public int PassTimeSEC { get { return mQueryPassTimeSEC; } }
        public Random RandomN { get { return random; } }
        //         public void Trace(string text)
        //         {
        //             log.Info(text);
        //         }

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

        #region TIMING_AND_TASK

        private SyncMessageQueue<Action<InstanceZone>> mTasks = new SyncMessageQueue<Action<InstanceZone>>();
        private TimeTaskQueue mTimeTasks = new TimeTaskQueue();

        private void doTask(Action<InstanceZone> task)
        {
            task(this);
        }
        /// <summary>
        /// 【线程安全】向主线程排一个任务
        /// </summary>
        /// <param name="task"></param>
        public void queueTask(Action<InstanceZone> task)
        {
            mTasks.Enqueue(task);
        }

        public string GetInfo()
        {
            if (this.mObjects == null)
            {
                return "对象:-1";
            }

            return "对象:" + AllObjectsCount + ", 单位:" + AllUnitsCount + ", 法术:" + AllSpellsCount + ", 道具:" + AllItemsCount + ", 玩家:" + AllPlayersCount;
        }

        /// <summary>
        /// 【线程安全】增加时间任务
        /// </summary>
        /// <param name="intervalMS"></param>
        /// <param name="delayMS"></param>
        /// <param name="repeat"></param>
        /// <param name="handler"></param>
        public TimeTaskMS AddTimeTask(int intervalMS, int delayMS, int repeat, TickHandler handler)
        {
            return mTimeTasks.AddTimeTask(intervalMS, delayMS, repeat, handler);
        }
        /// <summary>
        /// 【线程安全】增加延时回调方法
        /// </summary>
        /// <param name="delayMS"></param>
        /// <param name="handler"></param>
        public TimeTaskMS AddTimeDelayMS(int delayMS, TickHandler handler)
        {
            return mTimeTasks.AddTimeDelayMS(delayMS, handler);
        }
        /// <summary>
        /// 【线程安全】增加定时回调方法
        /// </summary>
        /// <param name="intervalMS"></param>
        /// <param name="handler"></param>
        public TimeTaskMS AddTimePeriodicMS(int intervalMS, TickHandler handler)
        {
            if (intervalMS <= 0)
            {
                log.Error("增加定时任务异常:" + intervalMS);
                return null;
            }
            return mTimeTasks.AddTimePeriodicMS(intervalMS, handler);
        }

        #endregion

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

        /// <summary>
        /// 【线程安全】向当前场景输入指令
        /// </summary>
        /// <param name="act"></param>
        public void pushAction(Action act)
        {
            mSyncActionQueue.Enqueue(act);
        }
        protected internal void queueEventInternal(Event evt)
        {
            mSendingEvents.Enqueue(evt);
        }
        /// <summary>
        /// 【内部调用】输出消息
        /// </summary>
        public void queueEvent(ZoneEvent evt, GameEntity sender = null)
        {
            if (sender != null) { evt.sender = sender; }
            this.queueEventInternal(evt);
        }
        /// <summary>
        /// 【内部调用】输出消息
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="evt"></param>
        public void queueObjectEvent(InstanceZoneObject obj, ObjectEvent evt, bool force = false)
        {
            if (mSendingEvents == null)
            {
                if (mObjects == null)
                {
                    log.Warn("queueObjectEvent exception1: " + this.GetSceneID() + ", mObjects null, " + this.UUID + ", " + evt.GetType()
                        + ", " + new StackTrace().ToString());
                }
                else
                {
                    log.Warn("queueObjectEvent exception2: " + this.GetSceneID() + ", Players: " + this.AllPlayersCount + ", Items: "
                    + this.AllItemsCount + ", Units: " + this.AllUnitsCount + ", " + this.UUID + ", " + evt.GetType() + ", " + new StackTrace().ToString());
                }

                return;
            }

            if (obj.ID == 0)
                return;
            if (!force && !obj.IsInZone)
                return;
            obj.onSendingEvent(ref evt);
            if (evt != null)
            {
                evt.object_id = obj.ID;
                evt.sender = obj;
                mSendingEvents.Enqueue(evt);
            }
        }
        /// <summary>
        /// 立即发送指令
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="req"></param>
        /// <param name="rsp"></param>
        public void sendActorResponse(InstanceZoneObject obj, ActorRequest req, ActorResponse rsp)
        {
            if (obj.ID == 0)
                return;
            if (!obj.IsInZone)
                return;
            rsp.MessageID = req.MessageID;
            rsp.object_id = obj.ID;
            rsp.sender = obj;
            mSendingEvents.Enqueue(rsp);
        }

        public void sendMessageBox(string msg)
        {
            queueEventInternal(new TestMessageBox(msg));
        }

        public virtual int GetSceneID() { return 0; }


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

        public void RecvMessageFromGameServer(ZoneServer.SendMessageR2B msg)
        {
            LastRecvMessageR2B = msg;
            if (mOnRecvFromGS != null)
            {
                mOnRecvFromGS.Invoke(this, msg);
            }
        }
        public void SendMessageToGameServer(string msg)
        {
            ZoneServer.SendMessageB2R b2r = new ZoneServer.SendMessageB2R();
            b2r.Message = msg;
            LastSentMessageB2R = b2r;
            if (mOnSendToGS != null)
            {
                mOnSendToGS.Invoke(this, b2r);
            }
        }

        public void SendEventToGameServer(IExternalizable evt)
        {
            ZoneServer.SendEventB2R b2r = new ZoneServer.SendEventB2R();
            b2r.evt = evt;
            if (mOnSendMessageB2REvent != null)
            {
                mOnSendMessageB2REvent.Invoke(this, b2r);
            }
        }

        public bool CheckAutoDropItem()
        {
            return this.mListener.CheckDropItem();
        }

        //-----------------------------------------------------------------------------------
        public virtual void Update(int intervalMS, bool slowRefresh)
        {
            beginEventsRecord();
            this.mLastInterval = intervalMS;
            this.MinStep = MoveHelper.GetDistance(intervalMS, Templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC);
            if (mTimer == 0)
            {
                mCurPassTimeMS = 0;
                mQueryPassTimeMS = 0;
                mQueryPassTimeSEC = 0;
                foreach (InstanceFlag f in mFlags.Values)
                {
                    f.OnStart();
                }
                if (mOnInit != null)
                    mOnInit.Invoke(this);
            }

            mTasks.ProcessMessages(doTask);
            mSyncActionQueue.ProcessMessages(updateAction);

            mObjects.Refresh();
            var objetes = mObjects.Objects;
            {
                // clean space div state
                foreach (InstanceZoneObject u in objetes)
                {
                    u.onUpdate(this, slowRefresh);
                }
                mSpaceDiv.ClearSpaceNearChanges();
                bool dirty = false;
                foreach (InstanceZoneObject u in objetes)
                {
                    if (u.Enable)
                    {
                        dirty = u.updatePos(this);
                        u.mCurCellNode.MarkPosDirty(dirty);
                        if (dirty && u.ClientVisible && u.SyncPos)
                        {
                            if (mOnObjectPosChanged != null)
                            {
                                mOnObjectPosChanged.Invoke(this, u);
                            }
                            sync_pos_list.Add(u);
                        }
                    }
                }
            }
            foreach (InstanceFlag f in mFlags.Values)
            {
                f.update();
            }
            if (mOnUpdate != null)
            {
                mOnUpdate.Invoke(this);
            }

            updateEvents();

            mTimeTasks.Update(intervalMS);

            mCurPassTimeMS += intervalMS;
            mQueryPassTimeMS = mCurPassTimeMS;
            mQueryPassTimeSEC = (int)(mCurPassTimeMS / 1000);
            mTimer++;
        }

        private void updateAction(Action act)
        {
            if (act is SystemMessage)
            {
                processSystemMessage(act as SystemMessage);
            }
            else if (act is ObjectAction)
            {
                ObjectAction oa = (ObjectAction)act;
                InstanceUnit unit = act.sender as InstanceUnit;
                if (unit == null)
                {
                    unit = mObjects.GetObject<InstanceUnit>(oa.object_id);
                }
                if (unit != null)
                {
                    unit.doAction(oa);
                    if (mOnHandleObjectAction != null)
                    {
                        mOnHandleObjectAction.Invoke(unit, oa);
                    }
                    mFormula.OnUnitHandleNetMessage(unit, oa);
                }
                else
                {
                    log.Info("Drop message : " + oa);
                }
            }
            else
            {
                if (mOnHandleAction != null)
                {
                    mOnHandleAction.Invoke(this, act);
                }
                mFormula.OnZoneHandleNetMessage(this, act);
            }
        }
        private void updateEvents()
        {
            if (sync_pos_list.Enable)
            {
                queueEvent(sync_pos_list.AsEvent(this));
            }
            sync_pos_list.Clear();

            while (mSendingEvents.TryDequeue(out Event evt))
            {
                //log.Info("---->onEvent:" + evt.ToString());
                mListener.onEventHandler(evt);
                if (mOnPostEvent != null)
                {
                    mOnPostEvent.Invoke(this, evt);
                }
                if (evt is GameOverEvent && mOnGameOver != null)
                {
                    mOnGameOver.Invoke(this, evt as GameOverEvent);
                }
            }
        }

        virtual protected void processSystemMessage(SystemMessage msg)
        {
            if (msg is Ping)
            {
                queueEventInternal(new Pong(msg as Ping));
            }
        }

        //-------------------------------------------------------------------------------------------
        #region OBJECTS

        private uint mObjectIDIndexer = 0;

        internal uint genObjectID()
        {
            return ++mObjectIDIndexer;
        }

        // 获取一个单位
        public T getObject<T>(uint obj_id) where T : InstanceZoneObject
        {
            if (obj_id == 0) return null;
            T go = mObjects.GetObject<T>(obj_id);
            if (go != null)
            {
                return go;
            }
            return null;
        }

        public InstanceUnit getUnit(uint obj_id)
        {
            if (obj_id == 0) return null;
            return mObjects.GetObject<InstanceUnit>(obj_id);
        }


        public InstanceUnit getUnitByTemplateID(int templateId)
        {
            if (templateId == 0) return null;
            foreach (InstanceUnit u in mObjects.Units)
            {
                if (u.Info.TemplateID == templateId)
                {
                    return u;
                }
            }
            return null;
        }
        public InstanceUnit getTheWeightOneAtForce(int force)
        {
            float weightest = -999;
            InstanceUnit theone = null;
            foreach (InstanceUnit u in mObjects.Units)
            {
                if (u.Force == force && u.Weight > weightest)
                {
                    theone = u;
                    weightest = u.Weight;
                }
            }
            return theone;
        }


        public InstanceUnit getUnitByName(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            foreach (InstanceUnit u in mObjects.Units)
            {
                if (name.Equals(u.Name))
                {
                    return u;
                }
            }
            return null;
        }
        public InstancePlayer getPlayerByUUID(string playerUUID)
        {
            if (string.IsNullOrEmpty(playerUUID))
            {
                return null;
            }

            return mObjects.GetPlayer(playerUUID);
        }
        public InstanceItem getItemByName(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            foreach (InstanceItem u in mObjects.Items)
            {
                if (name.Equals(u.Name))
                {
                    return u;
                }
            }
            return null;
        }

        public InstanceUnit getUnitByID(int UnitID)
        {
            foreach (InstanceUnit obj in mObjects.Units)
            {
                if (obj.ID == UnitID)
                {
                    return obj;
                }
            }

            return null;
        }

        public int getDeadUnit(int count, int force, ref List<InstanceUnit> list)
        {
            int n = 0;
            foreach(var obj in mObjects.Units)
            {
                if(obj.is_dead() && obj.Force == force && !list.Exists(u => u.ID == obj.ID))
                {
                    list.Add(obj);
                    if (++n >= count) break;
                }
            }
            return n;
        }

        public bool IsObjectMapNull()
        {
            return this.mObjects == null;
        }


        public IEnumerable<InstancePlayer> AllPlayers { get { return mObjects.Players; } }
        public int AllPlayersCount { get { return mObjects.PlayersCount; } }

        public IEnumerable<InstanceUnit> AllUnits { get { return mObjects.Units; } }
        public int AllUnitsCount { get { return mObjects.UnitsCount; } }

        public IEnumerable<InstanceSpell> AllSpells { get { return mObjects.Spells; } }
        public int AllSpellsCount { get { return mObjects.SpellsCount; } }

        public IEnumerable<InstanceItem> AllItems { get { return mObjects.Items; } }
        public int AllItemsCount { get { return mObjects.ItemsCount; } }

        public IEnumerable<InstanceZoneObject> AllObjects { get { return mObjects.Objects; } }
        public int AllObjectsCount { get { return mObjects.ObjectsCount; } }


        [Obsolete("use {@link #AllUnits} {@link #AllSpells} {@link #AllItems} for best performance!")]
        public IEnumerable<InstanceZoneObject> allObjs()
        {
            return mObjects.Objects;
        }
        [Obsolete("use {@link #AllUnitsCount} {@link #AllSpellsCount} {@link #AllItemsCount}  for best performance!")]
        public int allObjsCount()
        {
            return mObjects.ObjectsCount;
        }


        [Obsolete]
        public List<T> selectUnits<T>(Predicate<T> select) where T : InstanceUnit
        {
            List<T> ret = new List<T>(mObjects.UnitsCount);
            foreach (InstanceUnit obj in mObjects.Units)
            {
                if (select(obj as T))
                {
                    ret.Add(obj as T);
                }
            }
            return ret;
        }
        public void selectUnits<T>(Predicate<T> select, List<T> ret) where T : InstanceUnit
        {
            foreach (InstanceUnit obj in mObjects.Units)
            {
                if (select(obj as T))
                {
                    ret.Add(obj as T);
                }
            }
        }
        public T selectRandomUnit<T>(Predicate<T> select) where T : InstanceUnit
        {
            T ret = null;
            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
            {
                foreach (InstanceUnit obj in mObjects.Units)
                {
                    if (obj is T && select(obj as T))
                    {
                        list.Add(obj as T);
                    }
                }
                if (list.Count > 0)
                {
                    ret = CUtils.GetRandomInArray<InstanceUnit>(list, random) as T;
                }
            }
            return ret;
        }
        public T selectUnit<T>(Predicate<T> select) where T : InstanceUnit
        {
            foreach (InstanceUnit obj in mObjects.Units)
            {
                if (select(obj as T))
                {
                    return obj as T;
                }
            }
            return null;
        }

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

        /// <summary>
        /// 创建一个单位实体
        /// </summary>
        virtual public InstanceZoneObject CreateUnit(UnitInfo info, string name, int force, int alliesForce, int level)
        {
            InstanceUnit unit = TemplateManager.Factory.CreateUnit(this, info, name, force, alliesForce, level);
            if (unit != null)
            {
                return unit;
            }
            switch (info.UType)
            {
                case UnitInfo.UnitType.TYPE_PLAYER:
                    return new InstancePlayer(this, info, name, force, level, alliesForce);
                case UnitInfo.UnitType.TYPE_MANUAL:
                    return new InstanceManual(this, info, name, force, level);
                case UnitInfo.UnitType.TYPE_PET:
                    return new InstancePet(this, info, name, force, level);
                case UnitInfo.UnitType.TYPE_SUMMON:
                    return new InstanceSummon(this, info, name, force, level);
                case UnitInfo.UnitType.TYPE_BUILDING:
                    return new InstanceBuilding(this, info, name, force, level);
                case UnitInfo.UnitType.TYPE_MONSTER:
                    return new InstanceGuard(this, info, name, force, level);
                case UnitInfo.UnitType.TYPE_NPC:
                    return new InstanceGuard(this, info, name, force, level);
                default:
                    return new InstanceGuard(this, info, name, force, level);
            }
        }

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

        public InstanceUnit AddUnit(int unitTemplateID, string name, int force, int level, float x, float y, float direction, bool pointLv = false, uint CustomId = 0, string alias = null)
        {
            UnitInfo info = Templates.getUnit(unitTemplateID);
            if (info != null)
            {
                return AddUnit(info, name, force, level, x, y, direction, null, "", 0, pointLv, CustomId, alias);
            }
            return null;
        }

        /// <summary>
        /// 添加一个单位
        /// </summary>
        /// <param name="info">单位模板</param>
        /// <param name="name">场景中名字(Key)</param>
        /// <param name="force">单位阵营</param>
        /// <param name="level">单位等级</param>
        /// <param name="x">坐标</param>
        /// <param name="y">坐标</param>
        /// <param name="direction">方向</param>
        /// <param name="summoner">召唤者</param>
        /// <returns></returns>
        public InstanceUnit AddUnit(UnitInfo info, string name, int force, int level, float x, float y, float direction,
            InstanceUnit summoner = null, String clientShowName = "", int gsFlag = 0, bool pointLv = false, uint CustomId = 0, string alias = null)
        {
            AddUnitEvent add;
            return AddUnit(info, name, force, level, x, y, direction, out add, summoner, clientShowName, gsFlag, 0, pointLv, CustomId, alias);
        }
        /// <summary>
        /// 添加一个单位
        /// </summary>
        /// <param name="info">单位模板</param>
        /// <param name="name">场景中名字(Key)</param>
        /// <param name="force">单位阵营</param>
        /// <param name="level">单位等级</param>
        /// <param name="x">坐标</param>
        /// <param name="y">坐标</param>
        /// <param name="direction">方向</param>
        /// <param name="add">输出事件</param>
        /// <param name="summoner">召唤者</param>
        /// <returns></returns>
        /// 

        private long mAddUnitPrintTime = 0;

        public InstanceUnit AddUnit(UnitInfo info, string name, int force, int level, float x, float y, float direction,
            out AddUnitEvent add, InstanceUnit summoner = null, String clientShowName = "", int gsFlag = 0, int alliesForce = 0, bool pointLv = false, uint CustomId = 0, string alias = null)
        {
            add = null;
            if (mObjects.UnitsCount >= mMaxUnitCount)
            {
                if (mAddUnitPrintTime < CommonLang.CUtils.localTimeMS)
                {
                    mAddUnitPrintTime = CommonLang.CUtils.localTimeMS + 3000;
                    log.Warn(string.Format("--Zone AddUnit to max: {0}, UUID: {1}, ID: {2} 单位数量:{3}", this.GetSceneID(), this.UUID, info.TemplateID, GetZoneUnitInfo()));
                }

                return null;
            }

            if (mTryAddUnit != null && !mTryAddUnit.Invoke(info))
            {
                //log.Info(string.Format("Zone Unit TryAdd max: {0}, {1}, 单位数量:{2}", this.TerrainSrc.ID, info.TemplateID, mObjects.UnitsCount));
                return null;
            }

            // 创建实体单位
            InstanceZoneObject ret = CreateUnit(info, name, force, alliesForce, level);
            // 存入单位列表
            if (ret is InstanceUnit)
            {
                InstanceUnit unit = ret as InstanceUnit;
                unit.Alias = alias;
                unit.gameServerFlag = gsFlag;
                if(CustomId > 0 && mObjects.ContainsObjectByKey(CustomId))
                {
                    log.Warn(string.Format("--Zone AddUnit failed, ID Exist: id:{0} template:{1}", CustomId, info.TemplateID));
                    return null;
                }
                // 存入单位列表
                if (unit.tryAdd(x, y, direction, CustomId))
                {
                    mObjects.AddObject(unit);
                    this.LastAddedUnit = unit;
                    if (unit is ISummonedUnit)
                    {
                        ((ISummonedUnit)unit).SummonerUnit = summoner;
                    }

                    try
                    {
                        unit.onAdded(this, pointLv);
                        if (mOnUnitAdded != null)
                            mOnUnitAdded.Invoke(this, unit);

                        add = new AddUnitEvent();
                        add.sender = unit;
                        if (unit.ClientVisible)
                        {
                            if (clientShowName != null && !unit.IsPlayer && clientShowName.Length > 0)
                            {
                                unit.SetDiaplayerName(clientShowName);
                            }

                            //// 阵营判断
                            //if(info.UType == UnitType.TYPE_MONSTER && force > 1)
                            //{
                            //	add.flag = 1;
                            //}

                            queueEvent(add);
                        }
                    }
                    catch (Exception err)
                    {
                        log.Warn("AddUnit catch :" + name + ", e:" + err);
                        add.ErrorMessage = err.Message;
                        return null;
                    }
                    finally
                    {
                        add.Sync = unit.GenSyncUnitInfo(true);
                    }
                    return unit;
                }
            }
            return null;
        }

        /** 获取场景单位异常信息 */
        private string GetZoneUnitInfo(int nMinNums = 50)
        {
            HashMap<int, int> mInfo = new HashMap<int, int>();
            foreach (InstanceUnit obj in mObjects.Units)
            {
                mInfo.Put(obj.Info.ID, mInfo.Get(obj.Info.ID) + 1);
            }

            string finalStr = "";
            foreach (int unitID in mInfo.Keys)
            {
                int count = mInfo.Get(unitID);
                if (count < nMinNums)
                {
                    continue;
                }
                finalStr = finalStr + (unitID) + "-" + count + ", ";
            }

            return finalStr;
        }


        public InstanceItem AddItem(ItemTemplate template, string name, float x, float y, float direction, int force, string disPlayName,
            out AddItemEvent add, InstanceUnit creater, int from)
        {
            add = null;
            if (mTryAddItem != null && !mTryAddItem.Invoke(template))
            {
                //log.Info(string.Format("Zone item TryAdd max: {0}, {1}", this.mSceneType, template.TemplateID));
                return null;
            }

            InstanceItem ret = TemplateManager.Factory.CreateItem(this, template, name, force, creater, disPlayName, from);
            ret.setPos(x, y);
            if (ret.tryAdd(x, y, direction))
            {
                mObjects.AddObject(ret);
                this.LastCreatedInstanceItem = ret;
                add = new AddItemEvent();
                add.sender = ret;
                if (ret.ClientVisible)
                {
                    queueEvent(add);
                }
                try
                {
                    ret.onAdded(this);
                    if (mItemAdded != null)
                        mItemAdded.Invoke(this, ret, creater);
                }
                catch (Exception err)
                {
                    log.Warn("AddItem: " + template.ID + ", catch: " + err);
                    add.ErrorMessage = err.Message;
                    return null;
                }
                finally
                {
                    add.Sync = ret.GenSyncItemInfo(true, null);
                }
                return ret;
            }
            return null;
        }

        public InstanceItem AddItem(ItemTemplate template, string name, float x, float y, float direction, int force, string disPlayName, InstanceUnit creater, int from = 0)
        {
            AddItemEvent add;
            return AddItem(template, name, x, y, direction, force, disPlayName, out add, creater, from);
        }


        /// <summary>
        /// 添加一个法术或飞行道具
        /// </summary>
        /// <param name="template"></param>
        /// <param name="launch"></param>
        /// <param name="sender"></param>
        /// <param name="launcher">此法术的最初发起者</param>
        /// <param name="target_obj_id"></param>
        /// <param name="targetPos"></param>
        /// <param name="startX"></param>
        /// <param name="startY"></param>
        /// <param name="direction"></param>
        /// <param name="chain"></param>
        /// <returns></returns>
        public InstanceSpell AddSpell(
            XmdsSkillType fromSkillType,
            SpellTemplate template,
            LaunchSpell launch,
            InstanceZoneObject sender,
            InstanceUnit launcher,
            uint target_obj_id,
            Vector2 targetPos,
            float startX,
            float startY,
            float direction,
            SpellChainLevelInfo chain = null,
            int actionIndex = -1,
            int maxAffectUnit = 0,
            Dictionary<uint, InstanceUnit> damageList = null,
            int spellIndex = 0,
            JSGCreateSpellData createData = null)
        {
            if (template != null && sender != null)
            {
                InstanceUnit target = getUnit(target_obj_id);
                switch (template.MType)
                {
                    //                     case SpellTemplate.MotionType.Missile:
                    //                         if (target == null)
                    //                         {
                    //                             return null;
                    //                         }
                    //                         break;
                    case SpellTemplate.MotionType.BindingTarget:
                        if (target == null)
                        {
                            return null;
                        }
                        startX = target.X;
                        startY = target.Y;
                        break;
                    case SpellTemplate.MotionType.SelectTarget:
                        if (targetPos != null)
                        {
                            startX = targetPos.X;
                            startY = targetPos.Y;
                        }
                        break;
                    case SpellTemplate.MotionType.SeekerMissile:
                    case SpellTemplate.MotionType.SeekerSelectTarget:
                        {
                            target = null;
                            target_obj_id = 0;
                        }
                        break;
                    case SpellTemplate.MotionType.Chain:
                        if (target == null)
                        {
                            return null;
                        }
                        break;
                    case SpellTemplate.MotionType.Cannon:
                        if (target == null && targetPos == null)
                        {
                            targetPos = new Vector2(startX, startY);
                        }
                        break;
                }
                // 创建实体单位
                //InstanceSpell ret = new InstanceSpell(this, template, launch, launcher, sender, chainLevel);
                InstanceSpell ret = TemplateManager.Factory.CreateSpell(this, template, launch, launcher, sender, fromSkillType, damageList, spellIndex, createData);
                //ret.Direction = direction;
                ret.BindActionIndex = actionIndex;
                ret.MaxAffectUnit = (maxAffectUnit == 0) ? ret.Info.MaxAffectUnit : maxAffectUnit;
                ret.faceTo(direction);
                ret.setChainInfo(chain);
                ret.setTargetPos(targetPos);
                ret.setTarget(target);

                // 存入单位列表
                float xChanage, yChange;
                launch.GetXModify(launcher, out xChanage, out yChange);
                if (ret.tryAdd(startX + xChanage, startY + yChange, direction))
                {
                    //System.Console.WriteLine("--AddSpell: " + ret.Info.ID + ", " + ret.ID);
                    launcher.Virtual.DispatchSendSpellOverEvent(launch, template, ret.X, ret.Y);

                    mObjects.AddObject(ret);
                    this.LastLaunchSpell = ret.Info;
                    ret.onAdded(this);

                    if (ret.ClientVisible)
                    {
                        // 产生事件
                        AddSpellEvent evt = new AddSpellEvent(ret);
                        evt.sender = ret;

                        queueEvent(evt);
                    }

                    return ret;
                }
            }
            return null;
        }

        // 移除一个单位
        public bool RemoveObject(InstanceZoneObject obj)
        {
            //Console.WriteLine("RemoveObject : " + obj.ID);
            if (mObjects.RemoveObject(obj))
            {
                obj.onRemoved(this);
                if (obj is InstanceUnit)
                {
                    InstanceUnit unit = obj as InstanceUnit;
                    mFormula.OnUnitRemoved(unit);
                    if (mOnUnitRemoved != null)
                        mOnUnitRemoved.Invoke(this, unit);
                }
                else if (obj is InstanceItem)
                {
                    if (this.mOnItemRemoved != null)
                        this.mOnItemRemoved.Invoke(this, (InstanceItem)obj);
                }


                if (obj.ClientVisible)
                {
                    RemoveObjectEvent remove = new RemoveObjectEvent(obj.ID);
                    remove.sender = obj;
                    queueEvent(remove);
                }
                obj.Dispose();
                return true;
            }
            return false;
        }

        public InstanceZoneObject RemoveObjectByID(uint oid)
        {
            var obj = mObjects.GetObject<InstanceZoneObject>(oid);
            if (obj != null && RemoveObject(obj))
            {
                return obj;
            }
            return null;
        }

        public InstanceZoneObject RemoveUnitByID(uint unitTemplateId)
        {
            InstanceUnit obj = mObjects.GetUnit(unitTemplateId);
            if (obj != null && RemoveObject(obj))
            {
                return obj;
            }
            return null;
        }

        public InstanceZoneObject RemoveSpellByLaunchIDAndSpellID(int launchId, int spellId)
        {
            InstanceSpell spell = mObjects.GetSpllByLanuchAndSpellId(launchId, spellId);
            if (spell != null && RemoveObject(spell))
            {
                return spell;
            }
            return null;
        }


        public InstanceZoneObject RemoveItemByID(uint unitTemplateId)
        {
            InstanceItem obj = mObjects.GetItem(unitTemplateId);
            if (obj != null && RemoveObject(obj))
            {
                Console.WriteLine("成功移除单位:" + unitTemplateId);
                return obj;
            }
            return null;
        }

        public int GetZoneKingID()
        {
            return this.mKingID;
        }

        public string GetBindGameSrvID()
        {
            return this.mBindGameSrvId;
        }

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

        private class InstanceZoneObjectMap
        {
            private class DirtyList<K, T> where T : InstanceZoneObject
            {
                private static Logger log = LoggerFactory.GetLogger("DirtyList");

                private bool dirty = true;
                private List<T> mObjectsCollection = new List<T>();
                private HashMap<K, T> mObjects = new HashMap<K, T>();

                public int Count { get { return mObjects.Count; } }

                public void Add(K key, T obj)
                {
                    dirty = true;
                    mObjects.Add(key, obj);
                }
                public void Put(K key, T obj)
                {
                    dirty = true;
                    mObjects.Put(key, obj);
                }
                public bool Remove(K key)
                {
                    if (mObjects.Remove(key))
                    {
                        dirty = true;
                        return true;
                    }
                    return false;
                }

                public void Dispose()
                {
                    mObjectsCollection.Clear();
                    mObjects.Clear();
                }
                public T Get(K key)
                {
                    return mObjects.Get(key);
                }
                public bool ContainsKey(K key)
                {
                    return mObjects.ContainsKey(key);
                }
                public IEnumerable<T> GetCollection(string logFlag)
                {
                    try
                    {
                        if (dirty)
                        {
                            dirty = false;
                            mObjectsCollection.Clear();
                            mObjectsCollection.AddRange(mObjects.Values);
                        }
                        return mObjectsCollection;
                    }
                    catch (Exception err)
                    {
                        log.Warn("GetCollectioncatch:" + logFlag + ", CollectCnt: " + mObjectsCollection.Count +
                            ", mObjectsCnt: " + mObjects.Count + ", e:" + err);
                    }
                    return mObjects.Values;
                }
            }

            private DirtyList<uint, InstanceZoneObject> mObjects = new DirtyList<uint, InstanceZoneObject>();
            private DirtyList<uint, InstanceUnit> mObjects_MirrorUnits = new DirtyList<uint, InstanceUnit>();
            private DirtyList<uint, InstanceSpell> mObjects_MirrorSpells = new DirtyList<uint, InstanceSpell>();
            private DirtyList<uint, InstanceItem> mObjects_MirrorItems = new DirtyList<uint, InstanceItem>();
            private DirtyList<string, InstancePlayer> mObjects_MirrorPlayers = new DirtyList<string, InstancePlayer>();

            private string logFlag = "";


            public InstanceZoneObjectMap(string logFlag)
            {
                this.logFlag = logFlag;
            }

            internal void Refresh()
            {
                //mObjects.Refresh();
                //mObjects_MirrorUnits.Refresh();
                //mObjects_MirrorSpells.Refresh();
                //mObjects_MirrorItems.Refresh();
                //mObjects_MirrorPlayers.Refresh();
            }

            public void AddObject(InstanceZoneObject obj)
            {
                mObjects.Add(obj.ID, obj);
                if (obj is InstanceUnit)
                {
                    mObjects_MirrorUnits.Add(obj.ID, obj as InstanceUnit);
                }
                else if (obj is InstanceSpell)
                {
                    mObjects_MirrorSpells.Add(obj.ID, obj as InstanceSpell);
                }
                else if (obj is InstanceItem)
                {
                    mObjects_MirrorItems.Add(obj.ID, obj as InstanceItem);
                }
                if (obj is InstancePlayer)
                {
                    var p = obj as InstancePlayer;
                    mObjects_MirrorPlayers.Put(p.PlayerUUID, p);
                }
            }
            public bool RemoveObject(InstanceZoneObject obj)
            {
                if (mObjects.Remove(obj.ID))
                {
                    if (obj is InstanceUnit)
                    {
                        mObjects_MirrorUnits.Remove(obj.ID);
                    }
                    else if (obj is InstanceSpell)
                    {
                        mObjects_MirrorSpells.Remove(obj.ID);
                    }
                    else if (obj is InstanceItem)
                    {
                        mObjects_MirrorItems.Remove(obj.ID);
                    }
                    if (obj is InstancePlayer)
                    {
                        var p = obj as InstancePlayer;
                        mObjects_MirrorPlayers.Remove(p.PlayerUUID);
                    }
                    return true;
                }
                return false;
            }
            private InstanceZoneObject GetObject(uint id)
            {
                return mObjects.Get(id);
            }
            public bool ContainsObject(InstanceZoneObject obj)
            {
                return mObjects.ContainsKey(obj.ID);
            }
            public bool ContainsObjectByKey(uint id)
            {
                return mObjects.ContainsKey(id);
            }
            public void Dispose()
            {
                mObjects.Dispose();
                mObjects_MirrorUnits.Dispose();
                mObjects_MirrorSpells.Dispose();
                mObjects_MirrorItems.Dispose();
                mObjects_MirrorPlayers.Dispose();
            }

            public T GetObject<T>(uint id) where T : InstanceZoneObject
            {
                Type type = typeof(T);
                if (type.IsSubclassOf(typeof(InstanceUnit)))
                {
                    return mObjects_MirrorUnits.Get(id) as T;
                }
                else if (type.IsSubclassOf(typeof(InstanceSpell)))
                {
                    return mObjects_MirrorSpells.Get(id) as T;
                }
                else if (type.IsSubclassOf(typeof(InstanceItem)))
                {
                    return mObjects_MirrorItems.Get(id) as T;
                }
                else
                {
                    return mObjects.Get(id) as T;
                }
            }

            public InstanceUnit GetUnit(uint unitTemplateId)
            {
                foreach (InstanceUnit obj in mObjects_MirrorUnits.GetCollection(logFlag))
                {
                    if (obj.Info.ID == unitTemplateId)
                    {
                        return obj;
                    }
                }

                return null;
            }

            public InstanceSpell GetSpllByLanuchAndSpellId(int launchId, int spellId)
            {
                foreach (InstanceSpell obj in mObjects_MirrorSpells.GetCollection(logFlag))
                {
                    if (obj.Info.ID == spellId && obj.LauncherID == launchId)
                    {
                        return obj;
                    }
                }
                return null;
            }

            public InstanceItem GetItem(uint itemId)
            {
                return mObjects_MirrorItems.Get(itemId);
            }


            public InstancePlayer GetPlayer(string uuid)
            {
                return mObjects_MirrorPlayers.Get(uuid);
            }

            public int ObjectsCount { get { return mObjects.Count; } }
            public IEnumerable<InstanceZoneObject> Objects { get { return mObjects.GetCollection(logFlag); } }

            public int UnitsCount { get { return mObjects_MirrorUnits.Count; } }
            public IEnumerable<InstanceUnit> Units { get { return mObjects_MirrorUnits.GetCollection(logFlag); } }

            public int SpellsCount { get { return mObjects_MirrorSpells.Count; } }
            public IEnumerable<InstanceSpell> Spells { get { return mObjects_MirrorSpells.GetCollection(logFlag); } }

            public int ItemsCount { get { return mObjects_MirrorItems.Count; } }
            public IEnumerable<InstanceItem> Items { get { return mObjects_MirrorItems.GetCollection(logFlag); } }

            public int PlayersCount { get { return mObjects_MirrorPlayers.Count; } }
            public IEnumerable<InstancePlayer> Players { get { return mObjects_MirrorPlayers.GetCollection(logFlag); } }
        }

        #endregion
        //------------------------------------------------------------------------------------
        #region CALL_BACK

        internal void cb_unitDamageCallBack(InstanceUnit target, InstanceUnit attacker, int reduceHP, AttackSource source)
        {
            LastHittedUnit = target;
            LastAttackUnit = attacker;

            if (attacker != null)
                attacker.callback_onAttack(this, target, reduceHP, source);
            if (target != null)
                target.callback_onDamage(this, attacker, reduceHP, source);
            if (mOnUnitDamage != null)
                mOnUnitDamage.Invoke(this, target, attacker, reduceHP, source);
        }
        internal void cb_unitDeadCallBack(InstanceUnit obj, InstanceUnit attacker)
        {
            try
            {
                if (obj.mProcessDeadCallbackTime > CommonLang.CUtils.localTimeMS)
                {
                    long processTime = CommonLang.TimeUtil.GetTimestampMS() - (CommonLang.CUtils.localTimeMS - (obj.mProcessDeadCallbackTime - 60000));
                    log.Warn("cb_unitDeadCallBack 跳过:" + processTime + ", " + obj.Parent.GetSceneID() + ", ID:" + obj.Info.ID + ", UID:" + obj.PlayerUUID);
                    return;
                }

                if (obj.IsMonster && obj.Virtual.GetMaType() >= 4 && obj.Level >= 80)
                {
                    log.Info("boss死亡:" + obj.Parent.GetSceneID() + ", 单位id=" + obj.Info.ID + ", 处理死亡时间:" + obj.mProcessDeadCallbackTime
                         + ", SceneUID: " + obj.mZone.UUID + ", " + (attacker == null ? "null" : attacker.PlayerUUID));

                    if (attacker != null && attacker.IsPlayerUnit)
                    {
                        JSGHackerModule.OnPlayerKillMonster(attacker, obj);
                    }
                }
                if (obj.mProcessDeadCallbackTime > 0)
                {
                    log.Warn("单位多次回调死亡逻辑:" + this.GetSceneID() + ", " + this.UUID + ", " + obj.Info.ID + ", attackID:" +
                        (attacker == null ? -1 : attacker.Info.ID) + ", UUID" + (attacker == null ? "null" : attacker.PlayerUUID) + ", " + obj.mProcessDeadCallbackTime);
                }
                obj.mProcessDeadCallbackTime = CommonLang.CUtils.localTimeMS + 60000;
                obj.mDeadTime = this.PassTimeMS;

                statisticForceDead(obj);
                LastHittedUnit = obj;
                LastKilledUnit = obj;

                //if (attacker == null)
                //    attacker = obj;
                LastAttackUnit = attacker;
                obj.doDead(attacker);

                if (attacker != null)
                {
                    obj.deadDropItems(attacker.Force);
                    attacker.CurrentMoney += obj.Info.DropMoney;
                }

                mFormula.OnUnitDead(obj, attacker);

                obj.callback_onDead(this, attacker);
                if (mOnUnitDead != null)
                {
                    mOnUnitDead.Invoke(this, obj, attacker);
                }
            }
            catch (Exception e)
            {
                log.Error("cb_unitDeadCallBackcatch: " + this.GetSceneID() + ", " + (obj == null ? -1 : obj.Info.ID) + ", " + (attacker == null ? -1 : attacker.Info.ID)
                    + ",UUID: " + (obj == null ? "null" : obj.PlayerUUID) + ", " + (attacker == null ? "null" : attacker.PlayerUUID) + ", e: " + e);
            }
        }

        internal void cb_unitActivatedCallBack(InstanceUnit obj)
        {
            nearChange(obj);

            LastActivatedUnit = obj;
            obj.callback_onActivated(this);
            if (mOnUnitActivated != null)
                mOnUnitActivated.Invoke(this, obj);
        }
        internal void cb_unitRebirthCallBack(InstanceUnit obj)
        {
            LastRebirthUnit = obj;
            obj.callback_onRebirth(this);
            if (mOnUnitRebirth != null)
                mOnUnitRebirth.Invoke(this, obj);

        }
        internal void cb_unitGotInventoryItemCallBack(InstanceUnit obj, ItemTemplate item, int count)
        {
            LastUnitGotInventoryItem = item;
            obj.callback_onGotInventoryItem(this, item);
            if (mOnUnitGotInventoryItem != null)
                mOnUnitGotInventoryItem.Invoke(this, obj, item, count);
        }
        internal void cb_unitLostInventoryItemCallBack(InstanceUnit obj, ItemTemplate item, int count)
        {
            LastUnitLostInventoryItem = item;
            obj.callback_onLostInventoryItem(this, item);
            if (mOnUnitLostInventoryItem != null)
                mOnUnitLostInventoryItem.Invoke(this, obj, item, count);
        }
        internal int cb_unitGotInstanceItemCallBack(InstanceUnit obj, InstanceItem item)
        {
            LastUnitGotInstanceItem = item;
            obj.callback_onGotInstanceItem(this, item);
            short times = 0;
            if (mOnUnitGotInstanceItem != null)
            {
                InstancePlayer player = (obj as InstancePlayer);
                if (player == null)
                {
                    return 0;
                }

                if (item.unitPickInfo != null)
                {
                    if (item.Info.maxPickTimes > 0)
                    {
                        times = item.unitPickInfo.Get(player.PlayerUUID);
                        if (times >= item.Info.maxPickTimes)
                        {
                            player.Virtual.SendMsgToClient(XmdsConstConfig.TIPS_PICK_MAX);
                            return item.Info.maxPickTimes;
                        }
                    }
                    item.unitPickInfo.Put(player.PlayerUUID, ++times);
                    item.TotalPickTimes++;
                }

                mOnUnitGotInstanceItem.Invoke(this, obj, item);
            }

            return times;
        }

        internal void cb_unitUseItemCallBack(InstanceUnit obj, ItemTemplate item, InstanceUnit item_creater)
        {
            LastUnitUseItem = item;
            obj.callback_onUseItem(this, item, item_creater);
            if (mOnUnitUseItem != null)
                mOnUnitUseItem.Invoke(this, obj, item, item_creater);
        }
        internal void cb_unitGotBuffCallBack(InstanceUnit obj, InstanceUnit.BuffState buff)
        {
            LastUnitGotBuff = buff.Data;
            obj.callback_onGotBuff(this, buff);
            if (mOnUnitGotBuff != null)
                mOnUnitGotBuff.Invoke(this, obj, buff);
        }
        internal void cb_unitLostBuffCallBack(InstanceUnit obj, InstanceUnit.BuffState buff)
        {
            obj.callback_onLostBuff(this, buff);
            if (mOnUnitLostBuff != null)
                mOnUnitLostBuff.Invoke(this, obj, buff);
        }
        internal void cb_unitGotMoneyCallBack(InstanceUnit obj, int add_money)
        {
            if (mOnUnitGotMoney != null)
            {
                mOnUnitGotMoney.Invoke(obj, add_money);
            }
        }
        internal void cb_unitPickUnitCallBack(InstanceUnit src, InstanceUnit pickable)
        {
            LastPickableUnit = pickable;
            if (mOnUnitPickUnit != null)
            {
                mOnUnitPickUnit.Invoke(this, src, pickable);
            }
        }

        public void cb_unitOutBattleCallBack(InstanceUnit obj)
        {
            if (mOnUnitOutBattle != null)
            {
                mOnUnitOutBattle.Invoke(this, obj);
            }
        }


        internal bool cb_unitTryPickItem(InstanceUnit unit, InstanceItem item)
        {
            LastPickingItem = item;
            LastPickingItemUnit = unit;
            bool ret = true;
            if (mTryPickItem != null)
            {
                foreach (TryPickItemHandler trypick in mTryPickItem.GetInvocationList())
                {
                    if (!trypick.Invoke(this, unit, item))
                    {
                        ret = false;
                    }
                }
            }
            return ret;
        }
        internal void cb_unitFinishPickItem(InstanceUnit unit, InstanceItem item)
        {
            if (mFinishPickItem != null)
            {
                mFinishPickItem.Invoke(this, unit, item);
            }
        }
        internal void cb_unitLaunchSkill(InstanceUnit unit, CommonAI.Zone.Instance.InstanceUnit.SkillState ss)
        {
            LastLaunchSkill = ss.Data;
            LastLaunchSkillUnit = unit;
            if (mOnUnitLaunchSkill != null)
            {
                mOnUnitLaunchSkill.Invoke(this, unit, ss);
            }
        }
        internal void cb_playerReady(InstancePlayer player)
        {
            if (mPlayerReady != null)
            {
                mPlayerReady.Invoke(player);
            }
        }

        #endregion

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

        #region UNIT_ATTACK

        /// <summary>
        /// 获得两个对象是否是队友
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        /// <returns></returns>
        public virtual bool IsTeammates(InstanceUnit src, InstanceUnit dst)
        {
            return false;
        }

        /// <summary>
        /// 测试是否可见,单位间可交互(AOI)
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dst"></param>
        /// <returns></returns>
        public virtual bool IsVisibleAOI(InstanceZoneObject src, InstanceZoneObject dst)
        {
            if (src.AoiStatus == dst.AoiStatus)
            {
                if (dst is InstanceUnit)
                {
                    return (dst as InstanceUnit).IsVisible;
                }
                return true;
            }
            else if (src.AoiStatus != null)
            {
                return src.AoiStatus.CanSeeOther;
            }
            else if (dst.AoiStatus != null)
            {
                return dst.AoiStatus.CanSeeMe;
            }
            return false;
        }


        /// <summary>
        /// 测试是否可攻击
        /// </summary>
        /// <param name="src"></param>
        /// <param name="target"></param>
        /// <param name="expectTarget"></param>
        /// <param name="reason"></param>
        /// <param name="weapon"></param>
        /// <returns></returns>
        public virtual bool IsAttackable(InstanceUnit src, InstanceUnit target, SkillTemplate.CastTarget expectTarget, AttackReason reason, ITemplateData weapon)
        {
            if (src == null || target == null)
            {
                return false;
            }

            if (!IsVisibleAOI(src, target))
            {
                return false;
            }
            if (target.CanWhiplashDeadBody)
            {
                if (!target.IsAttackable)
                    return false;
                if (!target.IsVisible)
                    return false;
                if (target.IsInvincible)
                    return false;
            }
            else
            {
                if (!target.IsActive)
                    return false;
                if (!target.IsVisible)
                    return false;
                if (target.IsInvincible)
                    return false;
            }
            switch (expectTarget)
            {
                case SkillTemplate.CastTarget.Enemy:
                    return src.Force != target.Force;
                case SkillTemplate.CastTarget.PetForMaster:
                    if (src is InstancePet)
                    {
                        return (src as InstancePet).Master == target;
                    }
                    return false;
                case SkillTemplate.CastTarget.Alias:
                    return (src != target) && (src.Force == target.Force);

                case SkillTemplate.CastTarget.AlliesIncludeSelf:
                    return (src.Force == target.Force);
                case SkillTemplate.CastTarget.AlliesExcludeSelf:
                    return (src != target) && (src.Force == target.Force);

                case SkillTemplate.CastTarget.EveryOne:
                    return true;
                case SkillTemplate.CastTarget.EveryOneExcludeSelf:
                    return (src != target);

                case SkillTemplate.CastTarget.Self:
                    return src == target;
                case SkillTemplate.CastTarget.EnemyAndSelf:
                    return src.Force != target.Force || src == target;
                case SkillTemplate.CastTarget.NA:
                default:
                    return false;
            }
        }

        /// <summary>
        /// 扫描所有可攻击对象
        /// </summary>
        /// <param name="src"></param>
        /// <param name="list"></param>
        /// <param name="expectTarget"></param>
        /// <param name="reason"></param>
        /// <param name="weapon"></param>
        public void getAttackableUnits(InstanceUnit src, List<InstanceUnit> list, SkillTemplate.CastTarget expectTarget, AttackReason reason, ITemplateData weapon)
        {
            for (int i = list.Count - 1; i >= 0; --i)
            {
                InstanceUnit o = list[i];
                if (!IsAttackable(src, o, expectTarget, reason, weapon))
                {
                    list.RemoveAt(i);
                }
            }
        }

        /// <summary>
        /// 对单个单位攻击。
        /// </summary>
        /// <param name="src"></param>
        /// <param name="attack"></param>
        /// <param name="target"></param>
        /// <param name="expectTarget">判断IsAttackable</param>
        /// <returns></returns>
        public bool unitAttackSingle(
            InstanceUnit src,
            AttackSource attack,
            InstanceUnit target,
            SkillTemplate.CastTarget expectTarget)
        {
            if (IsAttackable(src, target, expectTarget, AttackReason.Attack, attack.Weapon))
            {
                target.doHitAttack(src, attack);
                return true;
            }
            return false;
        }

        /// <summary>
        /// 某个单位对指定列表里的 IsAttackable 单位发起攻击。
        /// 此操作会修改List
        /// </summary>
        /// <param name="src"></param>
        /// <param name="attack"></param>
        /// <param name="list">调用完成后,列表中未命中单位会自动移除。</param>
        /// <param name="expectTarget">判断IsAttackable</param>
        /// <returns></returns>
        public int unitAttack(
            InstanceUnit src,
            AttackSource attack,
            List<InstanceUnit> list,
            SkillTemplate.CastTarget expectTarget)
        {
            int count = 0;
            for (int i = list.Count - 1; i >= 0; --i)
            {
                InstanceUnit o = list[i];
                if (IsAttackable(src, o, expectTarget, AttackReason.Attack, attack.Weapon))
                {
                    o.doHitAttack(src, attack);
                    count++;
                }
                else
                {
                    list.RemoveAt(i);
                }
            }
            return count;
        }

        /// <summary>
        /// 直接对列表中的单位攻击,不做任何判断。
        /// </summary>
        /// <param name="src"></param>
        /// <param name="attack"></param>
        /// <param name="list"></param>
        public int unitAttackDirect(
           InstanceUnit src,
           AttackSource attack,
           List<InstanceUnit> list)
        {
            int count = 0;
            for (int i = list.Count - 1; i >= 0; --i)
            {
                InstanceUnit o = list[i];
                o.doHitAttack(src, attack);
                count++;
            }
            return count;
        }


        /// <summary>
        /// 某个单位发起周身攻击
        /// </summary>
        /// <param name="src"></param>
        /// <param name="attack"></param>
        /// <param name="range"></param>
        /// <param name="expectTarget"></param>
        /// <returns></returns>
        public int unitAttackRound(
            InstanceUnit src,
            AttackSource attack,
            float range,
            SkillTemplate.CastTarget expectTarget)
        {
            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
            {
                getObjectsRoundRange<InstanceUnit>(
                    Collider.Object_HitBody_TouchRound,
                    src.X, src.Y, range,
                    list, src.AoiStatus);
                return unitAttack(src, attack, list, expectTarget);
            }
        }

        /// <summary>
        /// 某个单位发起扇形范围攻击
        /// </summary>
        /// <param name="src"></param>
        /// <param name="attack"></param>
        /// <param name="direction"></param>
        /// <param name="range"></param>
        /// <param name="angle"></param>
        /// <param name="expectTarget"></param>
        /// <returns></returns>
        public int unitAttackFan(
            InstanceUnit src,
            AttackSource attack,
            float direction,
            float range,
            float angle,
            SkillTemplate.CastTarget expectTarget)
        {
            float dr = angle / 2;
            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
            {
                getObjectsFanRange<InstanceUnit>(
                    Collider.Object_HitBody_TouchFan,
                    src.X, src.Y, range,
                    direction - dr,
                    direction + dr,
                    list, src.AoiStatus);
                return unitAttack(src, attack, list, expectTarget);
            }
        }

        //-------------------------------------------------------------------------------------------------------
        // 单位释放法术
        //-------------------------------------------------------------------------------------------------------

        public void unitLaunchSpell(
            XmdsSkillType fromSkillType,
            InstanceUnit launcher,
            LaunchSpell launch,
            float startX,
            float startY,
            uint targetUnitID = 0,
            Vector2 targetPos = null, int actionIndex = -1, int maxAffectUnit = 0, float pointDir = 0)
        {
            if (launch == null)
            {
                log.Error("单位释放技能异常: " + launcher.Info.ID + ", " + new StackTrace().ToString());
                return;
            }

            if (CUtils.RandomPercent(RandomN, launch.LaunchPercent))
            {
                float direction = pointDir == 0 ? launcher.Direction : pointDir;
                if (targetPos != null)
                {
                    //direction = MathVector.getDegree(startX, startY, targetPos.x, targetPos.y);
                    targetPos = (Vector2)targetPos.Clone();
                }
                SpellTemplate spell = Templates.getSpell(launch.SpellID);
                if (spell == null)
                {
                    log.Error("unitLaunchSpell找不到法术模板:" + launcher.PlayerUUID + ", " + launch.SpellID + ", SN: " + launch.SerialNumber);
                    return;
                }
                JSGCreateSpellData createData;
                if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(launcher, launch, ref spell, out createData, ref startX, ref startY))
                {
                    SpellChainLevelInfo chain = null;
                    if (launch.ChainLevel > 0)
                    {
                        chain = new SpellChainLevelInfo(launch);
                    }
                    switch (launch.PType)
                    {
                        case LaunchSpell.PosType.POS_TYPE_FAN:
                            {
                                float startAngle = direction - launch.Angle / 2f;// + launch.StartAngle;
                                float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0;
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, launcher, launcher,
                                        targetUnitID, targetPos, startX, startY,
                                        startAngle + interAngle * i, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_TOSAMETARGET:
                            {
                                InstanceUnit target = SeekSpellAttackable(launcher, spell, launcher.AoiStatus, startX, startY, launch.SeekingTargetRange,
                                    spell.ExpectTarget, SeekingExpect.Nearest, null);
                                if (target != null)
                                {
                                    launcher.faceTo(target.X, target.Y);
                                    launcher.SendForceSync();
                                    targetPos = new Vector2(target.X, target.Y);
                                    //Console.WriteLine("-------------targetPos 1 - " + target.X + ", " + target.Y);
                                    //Console.WriteLine("-------------targetPos 2 - " + targetPos);
                                    int[] index = { 0, -1, 1 };
                                    float testAngle = (float)Math.Atan2(targetPos.Y - launcher.Y, targetPos.X - launcher.X);
                                    float xBase = 3.0f;
                                    float xAdd = (float)(xBase * Math.Sin(testAngle));                 //角Dir的对边
                                    float yAdd = (float)(xBase * Math.Cos(testAngle));                 //Dir的邻边                                               

                                    for (int i = 0; i < launch.Count; i++)
                                    {
                                        float tempX = startX - xAdd * index[i];
                                        float tempY = startY + yAdd * index[i];
                                        float angle = (float)Math.Atan2(targetPos.Y - tempY, targetPos.X - tempX);
                                        AddSpell(fromSkillType, spell, launch, launcher, launcher,
                                            targetUnitID, targetPos, tempX, tempY,
                                            angle, chain, actionIndex, maxAffectUnit);
                                    }
                                }
                                else
                                {
                                    int[] index = { 0, -1, 1 };
                                    float xBase = 3.0f;
                                    float xAdd = (float)(xBase * Math.Sin(direction));                 //角Dir的对边
                                    float yAdd = (float)(xBase * Math.Cos(direction));                 //Dir的邻边
                                    float[] startAngleTemp = { 0, launch.Angle, launch.Angle * 1.2f, launch.Angle * 1.4f, launch.Angle * 1.5f };

                                    for (int i = 0; i < launch.Count; i++)
                                    {
                                        float tempX = startX - xAdd * index[i];
                                        float tempY = startY + yAdd * index[i];
                                        AddSpell(fromSkillType, spell, launch, launcher, launcher,
                                            targetUnitID, targetPos, tempX, tempY,
                                             direction - startAngleTemp[i] * index[i], chain, actionIndex, maxAffectUnit);
                                    }
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_CYCLE:
                            {
                                float startAngle = direction + launch.StartAngle;
                                float interAngle = CMath.PI_MUL_2 / launch.Count;

                                InstanceSpell[] spellBrothers = null;
                                if (launch.Count > 1 && spell.MType == SpellTemplate.MotionType.Foxfire)
                                {
                                    spellBrothers = new InstanceSpell[launch.Count];
                                }

                                for (int i = 0; i < launch.Count; i++)
                                {
                                    var sb = AddSpell(fromSkillType, spell, launch, launcher, launcher,
                                        targetUnitID, targetPos, startX, startY,
                                        startAngle + interAngle * i, chain, actionIndex, maxAffectUnit);
                                    if (spellBrothers != null)
                                    {
                                        spellBrothers[i] = sb;
                                        sb.brothers = spellBrothers;
                                    }
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_X:
                            {
                                float startAngle = direction + launch.StartAngle;
                                float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle };
                                Dictionary<uint, InstanceUnit> damageList = (spell.HitIntervalMS <= 0 ? new Dictionary<uint, InstanceUnit>() : null);

                                for (int i = 0; i < 4; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos,
                                        startX, startY, startAngle + interAngle[i], chain, actionIndex, maxAffectUnit, damageList);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION:
                            {
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    float d = (float)(random.NextDouble() * CMath.PI_MUL_2);
                                    AddSpell(fromSkillType, spell, launch, launcher,
                                        launcher, targetUnitID, targetPos, startX,
                                        startY, d, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_AREA:
                            using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                            {
                                SeekSpellAttackable(enemy_list, startX, startY, launcher, spell, launch, chain);
                                for (int i = 0; i < launch.Count && i < enemy_list.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, launcher, launcher, enemy_list[i].ID, null,
                                        startX, startY, direction, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL:
                            launch_randomTypeSpell(fromSkillType, spell, launcher, launch, chain, launcher, actionIndex);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_POS:
                            launch_randomPosSpell(fromSkillType, spell, launcher, launch, chain, launcher, actionIndex);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_MANY_CANNON:
                            launch_ManyCannon(fromSkillType, spell, launcher, launch, chain, launcher, targetUnitID, actionIndex, createData);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE:
                        default:
                            {
                                AddSpell(fromSkillType, spell, launch, launcher, launcher,
                                    targetUnitID, targetPos, startX, startY,
                                    direction + launch.StartAngle, chain, actionIndex, maxAffectUnit);
                            }
                            break;
                    }

                }
            }
        }


        // 单位释放法术
        public void spellLaunchSpell(
            XmdsSkillType fromSkillType,
            InstanceSpell sender,
            LaunchSpell launch,
            float startX,
            float startY,
            uint targetUnitID = 0,
            Vector2 targetPos = null, int actionIndex = -1, int maxAffectUnit = 0)
        {
            switch (launch.SenderUnit)
            {
                case LaunchSpell.LaunchSpllSenderUnit.Launcher:
                    this.unitLaunchSpell(fromSkillType, sender.Launcher, launch, startX, startY, targetUnitID, targetPos);
                    return;
            }
            if (CUtils.RandomPercent(RandomN, launch.LaunchPercent))
            {
                SpellChainLevelInfo chain = sender.ChainInfo;
                if (chain != null)
                {
                    if (!chain.TryLaunch(launch.SpellID))
                    {
                        // chain is end //
                        return;
                    }
                }
                SpellTemplate spell = Templates.getSpell(launch.SpellID);
                JSGCreateSpellData createData;
                if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(sender.Launcher, launch, ref spell, out createData, ref startX, ref startY))
                {
                    float direction = sender.Direction;
                    if (targetPos != null)
                    {
                        direction = MathVector.getDegree(startX, startY, targetPos.X, targetPos.Y);
                        targetPos = (Vector2)targetPos.Clone();
                    }

                    switch (launch.PType)
                    {
                        case LaunchSpell.PosType.POS_TYPE_FAN:
                            {
                                float startAngle = direction - launch.Angle / 2f + launch.StartAngle;
                                float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0;
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, sender.Launcher,
                                        targetUnitID, targetPos, startX, startY,
                                        startAngle + interAngle * i, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_CYCLE:
                            {
                                float startAngle = direction + launch.StartAngle;
                                float interAngle = CMath.PI_MUL_2 / launch.Count;
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, sender.Launcher,
                                        targetUnitID, targetPos, startX, startY,
                                        startAngle + interAngle * i, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_X:
                            {
                                float startAngle = direction + launch.StartAngle;
                                float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle };
                                Dictionary<uint, InstanceUnit> damageList = (spell.HitIntervalMS <= 0 ? new Dictionary<uint, InstanceUnit>() : null);

                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID,
                                        targetPos, startX, startY, startAngle + interAngle[i], chain, actionIndex, maxAffectUnit, damageList);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL:
                            launch_randomTypeSpell(fromSkillType, spell, sender, launch, chain, sender.Launcher, actionIndex);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_MANY_CANNON:
                            launch_ManyCannon(fromSkillType, spell, sender, launch, chain, sender.Launcher, targetUnitID, actionIndex, createData);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_POS:
                            launch_randomPosSpell(fromSkillType, spell, sender, launch, chain, sender.Launcher, actionIndex);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION:
                            {
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    float d = (float)(random.NextDouble() * CMath.PI_MUL_2);
                                    AddSpell(fromSkillType, spell, launch, sender, sender.Launcher,
                                        targetUnitID, targetPos, startX, startY, d, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_AREA:
                            using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                            {
                                SeekSpellAttackable(enemy_list, startX, startY, sender.Launcher, spell, launch, chain);
                                for (int i = 0; i < launch.Count && i < enemy_list.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, sender.Launcher,
                                        enemy_list[i].ID, targetPos, startX, startY, direction, chain, actionIndex, maxAffectUnit);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE:
                        default:
                            {
                                AddSpell(fromSkillType, spell, launch, sender, sender.Launcher,
                                    targetUnitID, targetPos, startX, startY, direction, chain, actionIndex, maxAffectUnit);
                            }
                            break;
                    }

                }
            }
        }


        public void attackLaunchSpell(
            XmdsSkillType fromSkillType,
            InstanceUnit attacker,
            InstanceUnit damage,
            AttackSource source,
            int serverExt = 0)
        {
            LaunchSpell launch = source.Attack.Spell;
            if (launch == null)
                return;
            InstanceZoneObject sender;
            if (source.FromSpellUnit != null)
            {
                sender = source.FromSpellUnit;
            }
            else
            {
                sender = attacker;
            }
            switch (launch.SenderUnit)
            {
                case LaunchSpell.LaunchSpllSenderUnit.Launcher:
                    sender = attacker;
                    break;
                case LaunchSpell.LaunchSpllSenderUnit.DamagedUnit:
                    sender = damage;
                    break;
            }
            if (CUtils.RandomPercent(RandomN, launch.LaunchPercent))
            {
                float startX = damage.X;
                float startY = damage.Y;

                SpellTemplate spell = Templates.getSpell(launch.SpellID);
                JSGCreateSpellData createData;
                if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(attacker, launch, ref spell, out createData, ref startX, ref startY))
                {
                    SpellChainLevelInfo chain = null;
                    if (source.FromSpellUnit != null && source.FromSpellUnit.ChainInfo != null)
                    {
                        chain = source.FromSpellUnit.ChainInfo;
                        if (!chain.TryLaunch(launch.SpellID))
                        {
                            // chain is end //
                            return;
                        }
                    }
                    switch (launch.PType)
                    {
                        case LaunchSpell.PosType.POS_TYPE_FAN:
                            {
                                float startAngle = sender.Direction - launch.Angle / 2f + launch.StartAngle;
                                float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0;
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null,
                                        startX, startY, startAngle + interAngle * i, chain, serverExt);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_CYCLE:
                            {
                                float startAngle = sender.Direction + launch.StartAngle;
                                float interAngle = CMath.PI_MUL_2 / launch.Count;
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null,
                                        startX, startY, startAngle + interAngle * i, chain, serverExt);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_X:
                            {
                                float startAngle = sender.Direction + launch.StartAngle;
                                float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle };
                                Dictionary<uint, InstanceUnit> damageList = (spell.HitIntervalMS <= 0 ? new Dictionary<uint, InstanceUnit>() : null);

                                for (int i = 0; i < launch.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null,
                                        startX, startY, startAngle + interAngle[i], chain, serverExt, 0, damageList);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL:
                            launch_randomTypeSpell(fromSkillType, spell, sender, launch, chain, attacker, serverExt);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_MANY_CANNON:
                            launch_ManyCannon(fromSkillType, spell, sender, launch, chain, attacker, damage.ID, serverExt, createData);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_POS:
                            launch_randomPosSpell(fromSkillType, spell, sender, launch, chain, attacker, serverExt);
                            break;
                        case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION:
                            {
                                for (int i = 0; i < launch.Count; i++)
                                {
                                    float d = (float)(random.NextDouble() * CMath.PI_MUL_2);
                                    AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null,
                                        startX, startY, d, chain, serverExt);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_AREA:
                            using (var enemy_list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
                            {
                                SeekSpellAttackable(enemy_list, startX, startY, attacker, spell, launch, chain);
                                for (int i = 0; i < launch.Count && i < enemy_list.Count; i++)
                                {
                                    AddSpell(fromSkillType, spell, launch, sender, attacker, enemy_list[i].ID, null,
                                        startX, startY, 0, chain, serverExt);
                                }
                            }
                            break;
                        case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE:
                        default:
                            {
                                AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null,
                                    startX, startY, sender.Direction, chain, serverExt);
                            }
                            break;
                    }
                }
            }
        }


        private void launch_randomTypeSpell(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender,
            LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, int serverExt = 0)
        {
            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
            {
                attacker.Parent.getObjectsRoundRange<InstanceUnit>(
                    (obj, dx, dy, dr) =>
                    {
                        var u = obj as InstanceUnit;

                        //己方单位.
                        if ((attacker.IsPlayer && !u.IsPlayer) || (!attacker.IsPlayer && u.IsPlayer))
                        {
                            return CMath.includeRoundPoint(dx, dy, dr, u.X, u.Y);
                        }

                        return false;
                    },
                    attacker.X,
                    attacker.Y,
                    launch.SeekingTargetRange,
                    list, attacker.AoiStatus);

                int maxCount = launch.Count == 0 ? list.Count : Math.Min(launch.Count, list.Count);
                for (int i = 0; i < maxCount; i++)
                {
                    float r = (float)(random.NextDouble() * spell.BodySize);
                    float a = (float)(random.NextDouble() * CMath.PI_MUL_2);
                    float x = (float)(list[i].X + Math.Cos(a) * r);
                    float y = (float)(list[i].Y + Math.Sin(a) * r);
                    float d = (float)(random.NextDouble() * CMath.PI_MUL_2);
                    AddSpell(fromSkillType, spell, launch, sender, attacker, 0, null,
                        x, y, d, chain, serverExt);
                }
            }
        }

        private void launch_randomPosSpell(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender,
            LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, int serverExt = 0)
        {
            for (int i = 0; i < launch.Count; i++)
            {
                float x = (float)(random.NextDouble() * launch.SpellRange / 2);
                float y = (float)(random.NextDouble() * launch.SpellRange / 2);
                float d = (float)(random.NextDouble() * CMath.PI_MUL_2);
                int randX = random.Next() % 2 == 0 ? 1 : -1;
                int randY = random.Next() % 2 == 0 ? 1 : -1;
                AddSpell(fromSkillType, spell, launch, sender, attacker, 0, null,
                    sender.X + x * randX, sender.Y + y * randY, d, chain, serverExt);
            }
        }

        public InstanceSpell launch_ManyCannon(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender,
            LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, uint targetUnitID, int serverExt, JSGCreateSpellData createData)
        {
            //float angleOffset = targetUnitID == 0 ? launch.StartAngle / 3 : launch.StartAngle;
            float angleOffset = JSGModule.GetCurveStartAngleOffect(attacker, getUnit(targetUnitID), launch.StartAngle, spell.SeekingRange);
            float startAngle = CMath.OpitimizeRadians(attacker.Direction) + angleOffset;
            float launchX = attacker.X - (float)(1.0f * Math.Cos(attacker.Direction));
            float launchY = attacker.Y - (float)(1.0f * Math.Sin(attacker.Direction));

            Vector2 targetPos = JSGModule.GetCurveDefaultTargetPos(attacker, spell.SeekingRange);
            //System.Console.WriteLine("targetPos: " + targetPos.X + ", " + targetPos.Y);

            int totalCount = createData == null ? launch.Count : createData.mMaxSpellCount;
            return AddSpell(fromSkillType, spell, launch, sender, attacker, targetUnitID, targetPos,
                    launchX, launchY, startAngle, chain, serverExt, 0, null, totalCount - 1, createData);
        }

        public void spellSummonUnit(InstanceSpell summoner, SummonUnit summon)
        {
            float x = summoner.X;
            float y = summoner.Y;
            float radius = summoner.BodyBlockSize;

            UnitInfo info = Templates.getUnit(summon.UnitTemplateID);
            if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0)
            {
                log.Warn("召唤非召唤类单位1:" + summoner.ID + ", 召唤id: " + summon.UnitTemplateID);
                return;
            }
            //UnitInfo un = (UnitInfo)info.Clone();
            //un.UType = UnitInfo.UnitType.TYPE_SUMMON;
            string name = null;
            if (TemplateManager.Formula.TrySummonUnit(summoner.Launcher, summon, ref info, ref name))
            {
                for (int i = 0; i < summon.Count; i++)
                {
                    float dx, dy, dr;

                    //float angle = (float)RandomN.NextDouble() * CMath.PI_MUL_2;
                    //float lengt = (float)RandomN.NextDouble() * radius;
                    //dx = x + (float)Math.Cos(angle) * lengt;
                    //dy = y + (float)Math.Sin(angle) * lengt;
                    //dr = (float)RandomN.NextDouble() * CMath.PI_MUL_2;

                    //召唤单位时判断当前位置能否行走,防止被召唤单位卡在地图里.Editor by Alex.
                    var pos = PathFinder.FindNearRandomMoveableNode(RandomN, x, y, radius, true);
                    if (pos == null)
                    {
                        dx = summoner.Launcher.X;
                        dy = summoner.Launcher.Y;
                    }
                    else
                    {
                        dx = pos.PosX;
                        dy = pos.PosY;
                    }

                    dr = summoner.Direction;
                    InstanceUnit unit = null;
                    if (!summoner.Launcher.IsActive)
                    {
                        unit = AddUnit(info, name, summoner.Launcher.Force, summon.UnitLevel, dx, dy, dr, null);
                    }
                    else
                    {
                        unit = AddUnit(info, name, summoner.Launcher.Force, summon.UnitLevel, dx, dy, dr, summoner.Launcher);
                    }

                    if (unit != null)
                    {
                        if (info.LifeTimeMS > 0)
                        {
                            AddTimeDelayMS(info.LifeTimeMS, (task) =>
                            {
                                unit.kill();
                            });
                        }
                        if (summon.Effect != null)
                        {
                            queueEvent(new AddEffectEvent(unit.ID, dx, dy, dr, summon.Effect));
                        }
                    }

                }
            }
        }

        public void unitSummonUnit(InstanceUnit summoner, SummonUnit summon)
        {
            float x = summoner.X;
            float y = summoner.Y;
            MathVector.movePolar(ref x, ref y, summoner.Direction, summoner.BodyBlockSize * 4);
            float radius = summoner.BodyBlockSize * 2;

            UnitInfo info = Templates.getUnit(summon.UnitTemplateID);
            if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0)
            {
                log.Warn("召唤非召唤类单位2:" + summoner.ID + ", 召唤id: " + summon.UnitTemplateID);
                return;
            }

            //UnitInfo un = (UnitInfo)info.Clone();
            //un.UType = UnitInfo.UnitType.TYPE_SUMMON;
            string name = null;
            if (TemplateManager.Formula.TrySummonUnit(summoner, summon, ref info, ref name))
            {
                for (int i = 0; i < summon.Count; i++)
                {
                    float angle = (float)RandomN.NextDouble() * CMath.PI_MUL_2;
                    float lengt = (float)RandomN.NextDouble() * radius;
                    float dx = x + (float)Math.Cos(angle) * lengt;
                    float dy = y + (float)Math.Sin(angle) * lengt;
                    float dr = (float)RandomN.NextDouble() * CMath.PI_MUL_2;

                    InstanceUnit unit = AddUnit(info, name, summoner.Force, summon.UnitLevel, dx, dy, dr, summoner);
                    if (unit != null)
                    {
                        if (info.LifeTimeMS > 0)
                        {
                            AddTimeDelayMS(info.LifeTimeMS, (task) =>
                            {
                                unit.kill();
                            });
                        }
                        if (summon.Effect != null)
                        {
                            queueEvent(new AddEffectEvent(unit.ID, dx, dy, dr, summon.Effect));
                        }
                    }
                }
            }
        }

        public InstanceUnit summonUnit(InstanceUnit summoner, UnitInfo info, string name, int level, float x, float y, float direction)
        {
            if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0)
            {
                log.Warn("召唤非召唤类单位3:" + summoner.ID + ", 召唤id: " + info.ID);
                return null;
            }

            InstanceUnit unit = AddUnit(info, name, summoner.Force, level, x, y, direction, summoner);
            if (unit != null)
            {
                if (info.LifeTimeMS > 0)
                {
                    AddTimeDelayMS(info.LifeTimeMS, (task) =>
                    {
                        unit.kill();
                    });
                }
            }
            return unit;
        }


        /// <summary>
        /// 锁定范围内指定目标
        /// </summary>
        /// <param name="spell"></param>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="range"></param>
        /// <param name="expectTarget"></param>
        /// <param name="expectSeeking"></param>
        /// <param name="chain"></param>
        /// <returns></returns>
        public virtual InstanceUnit SeekSpellAttackable(
            InstanceUnit launcher,
            SpellTemplate spellData,
            ObjectAoiStatus aoiStatus,
            float X, float Y, float range,
            SkillTemplate.CastTarget expectTarget,
            SpellTemplate.SeekingExpect expectSeeking,
            SpellChainLevelInfo chain)
        {
            using (var list = ListObjectPool<InstanceUnit>.AllocAutoRelease())
            {
                getObjectsRoundRange<InstanceUnit>((InstanceZoneObject o, float x, float y, float r) =>
                {
                    InstanceUnit u = o as InstanceUnit;
                    if (chain != null)
                    {
                        switch (expectSeeking)
                        {
                            case SpellTemplate.SeekingExpect.RandomIgnoreInChain:
                            case SpellTemplate.SeekingExpect.NearestIgnoreInChain:
                            case SpellTemplate.SeekingExpect.FarthestIgnoreInChain:
                                if (chain.ContainsTarget(u))
                                {
                                    return false;
                                }
                                break;
                            default:
                                if (chain.LastTarget == u)
                                {
                                    return false;
                                }
                                break;
                        }
                    }
                    if (IsAttackable(launcher, u, expectTarget, AttackReason.Look, spellData))
                    {
                        return CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize);
                    }
                    return false;
                }, X, Y, range, list, aoiStatus);
                switch (expectSeeking)
                {
                    case SpellTemplate.SeekingExpect.Random:
                    case SpellTemplate.SeekingExpect.RandomIgnoreInChain:
                        CUtils.RandomList(RandomN, list);
                        break;
                    case SpellTemplate.SeekingExpect.Nearest:
                    case SpellTemplate.SeekingExpect.NearestIgnoreInChain:
                        list.Sort(new ObjectSorterNearest<InstanceUnit>(X, Y));
                        break;
                    case SpellTemplate.SeekingExpect.Farthest:
                    case SpellTemplate.SeekingExpect.FarthestIgnoreInChain:
                        list.Sort(new ObjectSorterFarthest<InstanceUnit>(X, Y));
                        break;
                    case SpellTemplate.SeekingExpect.TheOne:
                        list.Sort((a, b) => { return (int)(b.Weight - a.Weight); });
                        break;
                }
                if (list.Count > 0)
                {
                    return list[0];
                }
            }
            return null;
        }

        public virtual void SeekSpellAttackable(List<InstanceUnit> ret, float X, float Y,
            InstanceUnit Launcher,
            SpellTemplate spell,
            LaunchSpell launch,
            SpellChainLevelInfo chain)
        {
            getObjectsRoundRange<InstanceUnit>((InstanceZoneObject o, float x, float y, float r) =>
            {
                InstanceUnit u = o as InstanceUnit;
                if (chain != null)
                {
                    switch (launch.SeekingTargetExpect)
                    {
                        case SpellTemplate.SeekingExpect.RandomIgnoreInChain:
                        case SpellTemplate.SeekingExpect.NearestIgnoreInChain:
                        case SpellTemplate.SeekingExpect.FarthestIgnoreInChain:
                            if (chain.ContainsTarget(u))
                            {
                                return false;
                            }
                            break;
                        default:
                            if (chain.LastTarget == u)
                            {
                                return false;
                            }
                            break;
                    }
                }
                if (IsAttackable(Launcher, u, spell.ExpectTarget, AttackReason.Look, spell))
                {
                    return CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize);
                }
                return false;
            }, X, Y, launch.SeekingTargetRange, ret, Launcher.AoiStatus);

            switch (launch.SeekingTargetExpect)
            {
                case SpellTemplate.SeekingExpect.Random:
                case SpellTemplate.SeekingExpect.RandomIgnoreInChain:
                    CUtils.RandomList(RandomN, ret);
                    break;
                case SpellTemplate.SeekingExpect.Nearest:
                case SpellTemplate.SeekingExpect.NearestIgnoreInChain:
                    ret.Sort(new ObjectSorterNearest<InstanceUnit>(X, Y));
                    break;
                case SpellTemplate.SeekingExpect.Farthest:
                case SpellTemplate.SeekingExpect.FarthestIgnoreInChain:
                    ret.Sort(new ObjectSorterFarthest<InstanceUnit>(X, Y));
                    break;
            }
        }

        /**游戏服场景标识通知*/
        public virtual void GSZoneFlagNotifyMsg(int value1)
        {

        }

        #endregion

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

        #region EDITOR_AND_SCRIPT

        readonly private HashMap<string, InstanceFlag> mFlags = new HashMap<string, InstanceFlag>();

        private bool mHasArea = false;

        protected bool addFlag(InstanceFlag flag)
        {
            if (!mFlags.ContainsKey(flag.Name))
            {
                mFlags.Add(flag.Name, flag);
                flag.onAdded();
                if (flag is ZoneArea)
                {
                    mHasArea = true;
                }
                return true;
            }
            return false;
        }

        public InstanceFlag getFlag(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                return null;
            }
            return mFlags.Get(name);
        }

        public T getFlagAs<T>(string name) where T : InstanceFlag
        {
            return mFlags.Get(name) as T;
        }

        public bool setFlag(string name, bool enable)
        {
            InstanceFlag flag = getFlag(name);
            if (flag != null)
            {
                flag.Enable = enable;
                return true;
            }
            return false;
        }

        #endregion
        //-------------------------------------------------------------------------------------------------------//
        #region Environment

        private HashMap<string, EnvironmentVar> EnvironmentVarMap = new HashMap<string, EnvironmentVar>();

        public void AddEnvironmentVar(string key, object value, bool syncToClient)
        {
            if (!string.IsNullOrEmpty(key))
            {
                EnvironmentVar var = EnvironmentVarMap.Get(key);
                if (var != null)
                {
                    if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || (var.SyncToClient && var.Value != value))
                    {
                        queueEvent(new SyncEnvironmentVarEvent(key, value));
                    }
                    var.Value = value;
                }
                else
                {
                    var = new EnvironmentVar(key, syncToClient, value);
                    EnvironmentVarMap.Add(key, var);
                    if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || var.SyncToClient)
                    {
                        queueEvent(new SyncEnvironmentVarEvent(key, value));
                    }
                }
            }
        }

        public void SetEnvironmentVar(string key, object value)
        {
            if (!string.IsNullOrEmpty(key))
            {
                EnvironmentVar var = EnvironmentVarMap.Get(key);
                if (var != null)
                {
                    if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || (var.SyncToClient && var.Value != value))
                    {
                        queueEvent(new SyncEnvironmentVarEvent(key, value));
                    }
                    var.Value = value;
                }
            }
        }
        public T GetEnvironmentVarAs<T>(string key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                EnvironmentVar var = EnvironmentVarMap.Get(key);
                if (var != null)
                {
                    try
                    {
                        return (T)var.Value;
                    }
                    catch (Exception err)
                    {
                        log.Warn("GetEnvironmentVarAs : " + key + ", catch: " + err);
                    }
                }
            }
            return default(T);
        }
        public int ListEnvironmentVars(List<EnvironmentVar> list)
        {
            list.AddRange(EnvironmentVarMap.Values);
            return EnvironmentVarMap.Count;
        }

        public ClientStruct.ZoneEnvironmentVar[] GetCurrentZoneVars()
        {
            ClientStruct.ZoneEnvironmentVar[] ret = new ClientStruct.ZoneEnvironmentVar[EnvironmentVarMap.Count];
            int i = 0;
            foreach (EnvironmentVar var in EnvironmentVarMap.Values)
            {
                ret[i] = new ClientStruct.ZoneEnvironmentVar();
                ret[i].Key = var.Key;
                ret[i].Value = var.Value;
                ret[i].SyncToClient = var.SyncToClient;
                i++;
            }
            return ret;
        }

        protected void BindZoneVar(ZoneVar var, BindValuesAdapter api)
        {
            if (string.IsNullOrEmpty(var.Key))
            {
                // Error
                return;
            }
            if (var.Value is IEditorValue)
            {
                IEditorValue evalue = (var.Value as IEditorValue);
                AddEnvironmentVar(var.Key, evalue.GetEnvValue(api), var.SyncToClient);
            }
            else if (var.Value is Array)
            {
                Array array = var.Value as Array;
                for (int i = 0; i < array.Length; i++)
                {
                    object e = array.GetValue(i);
                    if (e is IEditorValue)
                    {
                        string key = string.Format(var.Key + "[{0}]", i);
                        IEditorValue evalue = (e as IEditorValue);
                        AddEnvironmentVar(key, evalue.GetEnvValue(api), var.SyncToClient);
                    }
                }
            }
        }


        #endregion
        //-------------------------------------------------------------------------------------------------------//
        #region QUEST
        /// <summary>
        /// 【第三方系统通知】任务已接受
        /// </summary>
        /// <param name="playerUUID"></param>
        /// <param name="quest"></param>
        internal void gs_OnQuestAccepted(string playerUUID, string quest)
        {
            queueTask((InstanceZone zone) =>
            {
                InstancePlayer p = getPlayerByUUID(playerUUID);
                if (p != null)
                {
                    p.doQuestAccepted(quest);
                    if (mOnQuestAccepted != null)
                        mOnQuestAccepted.Invoke(p, quest);
                }
            });
        }
        /// <summary>
        /// 【第三方系统通知】任务已完成
        /// </summary>
        /// <param name="playerUUID"></param>
        /// <param name="quest"></param>
        internal void gs_OnQuestCommitted(string playerUUID, string quest)
        {
            queueTask((InstanceZone zone) =>
            {
                InstancePlayer p = getPlayerByUUID(playerUUID);
                if (p != null)
                {
                    if (mOnQuestCommitted != null)
                        mOnQuestCommitted.Invoke(p, quest);
                    p.doQuestCommitted(quest);
                }
            });
        }
        /// <summary>
        /// 【第三方系统通知】任务已放弃
        /// </summary>
        /// <param name="playerUUID"></param>
        /// <param name="quest"></param>
        internal void gs_OnQuestDropped(string playerUUID, string quest)
        {
            queueTask((InstanceZone zone) =>
            {
                InstancePlayer p = getPlayerByUUID(playerUUID);
                if (p != null)
                {
                    if (mOnQuestDropped != null)
                        mOnQuestDropped.Invoke(p, quest);
                    p.doQuestDropped(quest);
                }
            });
        }
        /// <summary>
        /// 【第三方系统通知】任务状态已改变
        /// </summary>
        /// <param name="playerUUID"></param>
        /// <param name="quest"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        internal void gs_OnQuestStatusChanged(string playerUUID, string quest, string key, string value)
        {
            queueTask((InstanceZone zone) =>
            {
                InstancePlayer p = getPlayerByUUID(playerUUID);
                if (p != null)
                {
                    p.doQuestStatusChanged(quest, key, value);
                    if (mOnQuestStatusChanged != null)
                        mOnQuestStatusChanged.Invoke(p, quest, key, value);
                }
            });
        }

        //给单个玩家发送消息
        internal void doSendMsgToPlayer(InstancePlayer player, CommonLang.Protocol.IMessage msg)
        {
            mQuestAdapter.DoSendMsgToPlayer(player.PlayerUUID, msg);
        }

        /// <summary>
        /// 本地通知游戏服
        /// </summary>
        /// <param name="player"></param>
        /// <param name="quest"></param>
        /// <param name="args"></param>
        internal void doAcceptQuest(InstancePlayer player, string quest, string args)
        {
            mQuestAdapter.DoAcceptQuest(player.PlayerUUID, quest, args);
        }
        /// <summary>
        /// 本地通知游戏服
        /// </summary>
        /// <param name="player"></param>
        /// <param name="quest"></param>
        /// <param name="args"></param>
        internal void doCommitQuest(InstancePlayer player, string quest, string args)
        {
            mQuestAdapter.DoCommitQuest(player.PlayerUUID, quest, args);
        }
        /// <summary>
        /// 本地通知游戏服
        /// </summary>
        /// <param name="player"></param>
        /// <param name="quest"></param>
        /// <param name="args"></param>
        internal void doDropQuest(InstancePlayer player, string quest, string args)
        {
            mQuestAdapter.DoDropQuest(player.PlayerUUID, quest, args);
        }
        /// <summary>
        /// 本地通知游戏服
        /// </summary>
        /// <param name="player"></param>
        /// <param name="quest"></param>
        /// <param name="key"></param>
        /// <param name="value"></param>
        internal void doUpdateQuestStatus(InstancePlayer player, string quest, string key, string value)
        {
            mQuestAdapter.DoUpdateQuestStatus(player.PlayerUUID, quest, key, value);
        }


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

    }


}