using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Runtime.Serialization;
using CommonAI.RTS.Manhattan;
using CommonAI.RTS;
using CommonLang.Vector;
using CommonLang;
using CommonLang.ByteOrder;
using CommonLang.IO;
using CommonLang.IO.Attribute;
using CommonLang.Property;
using CommonAI.Zone.Attributes;
using CommonAI.Zone.UnitTriggers;
using CommonAI.Zone.Instance;
using CommonAI.Zone.ZoneEditor;
using CommonAI.Zone.Helper;
using CommonLang.Xml;
using static CommonAI.Zone.AttackProp;
using CommonLang.Log;
using CommonAI.Data;

namespace CommonAI.Zone
{
    //---------------------------------------------------------------------------------//
    [MessageType(0x4000)]
    public class Config
    {
        [DescAttribute("系统帧率(服务端)", "系统")]
        public int SYSTEM_FPS = 30;

        [DescAttribute("全局重力", "全局")]
        public float GLOBAL_GRAVITY = 9.8f;
        [DescAttribute("全局挤开移动最大递归深度", "全局")]
        public uint GLOBAL_MOVE_IMPACT_DEPTH = 10;

        [DescAttribute("超出此同步范围,立即将坐标修正", "客户端")]
        public float CLIENT_UNIT_MOVE_MODIFY_MAX_RANGE = 20f;

        [DescAttribute("单位移动最慢速度(低于最慢速度表示不能移动)", "移动")]
        public float OBJECT_MOVE_TO_MIN_STEP_SEC = 0.3f;
        [DescAttribute("单位攻击范围是否包含身体半径", "战斗")]
        public bool OBJECT_ATTACK_RANGE_INCLUDE_BODYSIZE = false;

        [DescAttribute("单位间无碰撞总开关", "战斗")]
        public bool OBJECT_NONE_TOUCH = false;
        [DescAttribute("玩家无碰撞", "战斗")]
        public bool PLAYER_NONE_TOUCH = false;

        [DescAttribute("宠物跟随距离(靠近主人多少停止)", "战斗")]
        public float PET_FOLLOW_DISTANCE_MIN = 1.5f;
        [DescAttribute("宠物跟随距离(离主人多远时跟随)", "战斗")]
        public float PET_FOLLOW_DISTANCE_MAX = 2.5f;
        [DescAttribute("宠物强制跟随距离(距离太远,直接瞬移到主人)", "战斗")]
        public float PET_FOLLOW_DISTANCE_LIMIT = 12f;

        [DescAttribute("队员跟随距离(靠近主人多少停止)", "战斗")]
        public float TEAMER_FOLLOW_DISTANCE_MIN = 2f;
        [DescAttribute("队员跟随距离(离主人多远时跟随)", "战斗")]
        public float TEAMER_FOLLOW_DISTANCE_MAX = 4f;
        [DescAttribute("队员强制跟随距离(距离太远,直接瞬移到主人)", "战斗")]
        public float TEAMER_FOLLOW_DISTANCE_LIMIT = 8f;

        [DescAttribute("单位默认转身速度(弧度/秒)", "战斗")]
        public float UNIT_TURN_SPEED_SEC = 3.14f;
        [DescAttribute("单位允许被鞭尸", "战斗")]
        public bool UNIT_CAN_WHIPLASH_BODY = false;

        [DescAttribute("单位被击飞向上速度", "受击")]
        public float OBJECT_DAMAGE_FLY_ZSPEED_SEC = 10.0f;
        [DescAttribute("单位被击飞速度(距离/秒)", "受击")]
        public float OBJECT_DAMAGE_FLY_SPEED_SEC = 10.0f;
        [DescAttribute("单位被击飞加速度(距离/秒) => (速度=速度+加速度)", "受击")]
        public float OBJECT_DAMAGE_FLY_SPEED_ADD = 0f;
        [DescAttribute("单位被击飞阻力(每秒递减速度百分比)", "受击")]
        public float OBJECT_DAMAGE_FLY_SPEED_ACC = 0f;
        [DescAttribute("单位被击飞各项参数浮动值百分比", "受击")]
        public float OBJECT_DAMAGE_FLY_ARGS_FACTOR_PCT = 0f;
        [DescAttribute("单位受击时间(毫秒)", "受击")]
        public int OBJECT_DAMAGE_TIME_MS = 1000;


        [DescAttribute("每隔一段时间检测一下是否要追(防止抖动)", "AI")]
        public int AI_FOLLOW_AND_ATTACK_HOLD_TIME_MS = 1000;
        [DescAttribute("尝试追击过程中,调整射击范围(逃离)的比率", "AI")]
        public float AI_FOLLOW_AND_ATTACK_ADJUST_ESCAPE_PCT = 50;
        [DescAttribute("观察一定范围的触发器,固定时间固定检测周围变化", "AI")]
        public int AI_VIEW_TRIGGER_CHECK_TIME_MS = 1000;
        [DescAttribute("移动过程中每次AI变化的停顿时间", "AI")]
        public int AI_MOVE_AI_HOLD_TIME_MS = 1000;
        [DescAttribute("NPC每隔多长毫秒检测一次最大警戒距离", "AI")]
        public int AI_NPC_CHECK_IN_GUARD_LIMIT_TIME_MS = 500;
        [DescAttribute("NPC攻击间歇时,有多少几率检测旁边有人,并散开,防止堆积在一个点。(百分比)", "AI")]
        public float AI_NPC_ATTACK_IDLE_SCATTER_PCT = 300;
		[DescAttribute("NPC散开检测间隔", "AI")]
		public int AI_NPC_ATTACK_IDLE_INTERVAL = 3000;

		[DescAttribute("仇恨列表上限", "AI")]
        public int AI_HATE_SYSTEM_CAPACITY_MS = 50;
    }


    [MessageType(0x410B)]
    [DescAttribute("地块定义信息")]
    [Expandable]
    public class TerrainDefinitionMap : IExternalizable, ICloneable
    {
        [DescAttribute("地块定义")]
        [Expandable]
        public class MapBlockBrush : ICloneable
        {
            [ColorValueAttribute]
            [Desc("颜色值ARGB", "Key")]
            public int Value = ColorValueAttribute.COLOR_GREEN;
            [Desc("是否阻挡", "Key")]
            public bool IsBlock = true;
            [Desc("名字")]
            public string Name = "";
            [Desc("注释")]
            public string Desc = "";
            [Desc("Tag")]
            public string Tag = "";

            public MapBlockBrush() { }
            public MapBlockBrush(int value, bool is_block, string name)
            {
                this.Value = value;
                this.IsBlock = is_block;
                this.Name = name;
            }

            public object Clone()
            {
                MapBlockBrush ret = new MapBlockBrush();
                ret.Value = this.Value;
                ret.IsBlock = this.IsBlock;
                ret.Name = this.Name;
                ret.Desc = this.Desc;
                ret.Tag = this.Tag;
                return ret;
            }

            public override string ToString()
            {
                return string.Format("[{1}] {2} : {0}", Value.ToString("X8"), IsBlock ? "Block" : "Cross", Name);
            }
        }

        [NotNull]
        [ListAttribute(typeof(MapBlockBrush))]
        [Desc("地块定义列表")]
        public List<MapBlockBrush> Brushes = new List<MapBlockBrush>();

        public TerrainDefinitionMap()
        {
            Brushes.Add(new MapBlockBrush(0, false, "NULL"));
            Brushes.Add(new MapBlockBrush(ColorValueAttribute.COLOR_GREEN, true, "Block"));
            Brushes.Add(new MapBlockBrush(ColorValueAttribute.COLOR_DARK_GRAY, false, "Safe"));
        }

        /// <summary>
        /// 去从
        /// </summary>
        public bool ContainsValue(int value)
        {
            foreach (var b in Brushes)
            {
                if (b.Value == value)
                {
                    return true;
                }
            }
            return false;
        }
        public MapBlockBrush GetMapBlockBrush(int value)
        {
            foreach (var b in Brushes)
            {
                if (b.Value == value)
                {
                    return b;
                }
            }
            return null;
        }

        public object Clone()
        {
            TerrainDefinitionMap ret = new TerrainDefinitionMap();
            ret.Brushes = CUtils.CloneList<MapBlockBrush>(this.Brushes);
            return ret;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutS32(Brushes.Count);
            foreach (var v in this.Brushes)
            {
                output.PutS32(v.Value);
                output.PutBool(v.IsBlock);
                output.PutUTF(v.Name);
                output.PutUTF(v.Desc);
                output.PutUTF(v.Tag);
            }
        }

        public void ReadExternal(IInputStream input)
        {
            Brushes.Clear();
            int count = input.GetS32();
            for (int i = 0; i < count; i++)
            {
                var v = new MapBlockBrush();
                v.Value = input.GetS32();
                v.IsBlock = input.GetBool();
                v.Name = input.GetUTF();
                v.Desc = input.GetUTF();
                v.Tag = input.GetUTF();
                Brushes.Add(v);
            }
        }
    }


    [MessageType(0x410C)]
    [DescAttribute("动作定义信息")]
    [Expandable]
    public class UnitActionDefinitionMap : IExternalizable, ICloneable
    {
        [DescAttribute("动作定义")]
        [Expandable]
        public class UnitAction : ICloneable, IExternalizable
        {
            [DescAttribute("动作", "动作")]
            public UnitActionStatus Action = UnitActionStatus.Idle;

            [ListAttribute(typeof(UnitActionKeyFrame))]
            [DescAttribute("动作序列", "动作")]
            public List<UnitActionKeyFrame> ActionQueue = new List<UnitActionKeyFrame>();

            public void WriteExternal(IOutputStream output)
            {
                output.PutEnum8(this.Action);
                output.PutStructList(this.ActionQueue);
            }
            public void ReadExternal(IInputStream input)
            {
                this.Action = input.GetEnum8<UnitActionStatus>();
                this.ActionQueue = input.GetStructList<UnitActionKeyFrame>();
            }
            public object Clone()
            {
                UnitAction ret = new UnitAction();
                ret.Action = this.Action;
                ret.ActionQueue = CUtils.CloneList(this.ActionQueue);
                return ret;
            }
            public override string ToString()
            {
                return string.Format("{0} : {1}", Action, CUtils.ListToString(ActionQueue));
            }
        }
        [DescAttribute("动作帧定义")]
        [Expandable]
        public class UnitActionKeyFrame : ICloneable, IExternalizable
        {
            [DescAttribute("动作名")]
            public string ActionName = "f_idle";
            [DescAttribute("播放时间(如果多段动作,则需要指定每段时间)")]
            public int TimeMS = 0;
            [DescAttribute("是否循环")]
            public bool Cycle = false;
            [DescAttribute("是否淡出")]
            public bool CrossFade = false;
            [DescAttribute("播放速度")]
            public float Speed = 1f;
            [DescAttribute("Tag")]
            public string Tag = "";

            public override string ToString()
            {
                return ActionName;
            }
            public void WriteExternal(IOutputStream output)
            {
                output.PutUTF(this.ActionName);
                output.PutS32(this.TimeMS);
                output.PutBool(this.Cycle);
                output.PutBool(this.CrossFade);
                output.PutF32(this.Speed);
                output.PutUTF(this.Tag);
            }
            public void ReadExternal(IInputStream input)
            {
                this.ActionName = input.GetUTF();
                this.TimeMS = input.GetS32();
                this.Cycle = input.GetBool();
                this.CrossFade = input.GetBool();
                this.Speed = input.GetF32();
                this.Tag = input.GetUTF();
            }
            public object Clone()
            {
                UnitActionKeyFrame ret = new UnitActionKeyFrame();
                ret.ActionName = this.ActionName;
                ret.TimeMS = this.TimeMS;
                ret.Cycle = this.Cycle;
                ret.CrossFade = this.CrossFade;
                ret.Speed = this.Speed;
                ret.Tag = this.Tag;
                return ret;
            }
        }

        [ListAttribute(typeof(UnitAction))]
        [DescAttribute("动作集合")]
        public List<UnitAction> ActionMap = new List<UnitAction>();

        public void WriteExternal(IOutputStream output)
        {
            output.PutStructList(this.ActionMap);
        }
        public void ReadExternal(IInputStream input)
        {
            this.ActionMap = input.GetStructList<UnitAction>();
        }
        public object Clone()
        {
            UnitActionDefinitionMap ret = new UnitActionDefinitionMap();
            ret.ActionMap = CUtils.CloneList(this.ActionMap);
            return ret;
        }

    }



    //---------------------------------------------------------------------------------//
    /// <summary>
    /// 场景基础数据
    /// </summary>
    [MessageType(0x4001)]
    public class ZoneInfo : IExternalizable, ICloneable
    {
        public int TemplateID { get { return ID; } }

        [DescAttribute("ID", "", false)]
        public int ID;
        /// <summary>
        /// 地图总宽
        /// </summary>
        public int TotalWidth { get { return mXCount * mGridCellW; } }
        /// <summary>
        /// 地图总高
        /// </summary>
        public int TotalHeight { get { return mYCount * mGridCellH; } }
        /// <summary>
        /// 所有地图碰撞块信息
        /// </summary>
        public int[,] TerrainMatrix { get { return mTerrainMatrix; } }
        /// <summary>
        /// 地图碰撞格宽
        /// </summary>
        public int GridCellW { get { return mGridCellW; } }
        /// <summary>
        /// 地图碰撞格高
        /// </summary>
        public int GridCellH { get { return mGridCellH; } }
        /// <summary>
        /// 地图横向格子数
        /// </summary>
        public int XCount { get { return mXCount; } }
        /// <summary>
        /// 地图纵向格子数
        /// </summary>
        public int YCount { get { return mYCount; } }

        //------------------------------------------------------------
        [DescAttribute("", "", false)]
        public int[,] mTerrainMatrix;

        [DescAttribute("", "", false)]
        public int mXCount;
        [DescAttribute("", "", false)]
        public int mYCount;

        [DescAttribute("", "", false)]
        public int mGridCellW;
        [DescAttribute("", "", false)]
        public int mGridCellH;
        //------------------------------------------------------------

        public ZoneInfo(int xcount, int ycount, int gridW, int gridH)
        {
            this.mXCount = xcount;
            this.mYCount = ycount;
            this.mGridCellW = gridW;
            this.mGridCellH = gridH;
            this.mTerrainMatrix = new int[xcount, ycount];
        }
        public ZoneInfo() { }
        public int GetID()
        {
            return ID;
        }

        public object Clone()
        {
            ZoneInfo ret = new ZoneInfo();
            ret.ID = this.ID;
            ret.mXCount = this.mXCount;
            ret.mYCount = this.mYCount;
            ret.mGridCellW = this.mGridCellW;
            ret.mGridCellH = this.mGridCellH;
            ret.mTerrainMatrix = new int[mXCount, mYCount];
            for (int x = 0; x < mXCount; x++)
            {
                for (int y = 0; y < mYCount; y++)
                {
                    ret.mTerrainMatrix[x, y] = this.mTerrainMatrix[x, y];
                }
            }
            return ret;
        }
        public bool TryGetFlag(int bx, int by, out int flag)
        {
            if (bx < mXCount && bx >= 0 && by < mYCount && by >= 0)
            {
                flag = mTerrainMatrix[bx, by];
                return true;
            }
            flag = 0;
            return false;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutS32(ID);
            output.PutS32(mXCount);
            output.PutS32(mYCount);
            output.PutS32(mGridCellW);
            output.PutS32(mGridCellH);
            for (int x = 0; x < mXCount; x++)
            {
                for (int y = 0; y < mYCount; y++)
                {
                    output.PutS32(mTerrainMatrix[x, y]);
                }
            }
        }
        public void ReadExternal(IInputStream input)
        {
            this.ID = input.GetS32();
            this.mXCount = input.GetS32();
            this.mYCount = input.GetS32();
            this.mGridCellW = input.GetS32();
            this.mGridCellH = input.GetS32();
            this.mTerrainMatrix = new int[mXCount, mYCount];
            for (int x = 0; x < mXCount; x++)
            {
                for (int y = 0; y < mYCount; y++)
                {
                    this.mTerrainMatrix[x, y] = input.GetS32();
                }
            }
        }
    }



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

    #region __Templates_Data__

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

    /// <summary>
    /// 单位模板数据
    /// </summary>
    [MessageType(0x4002)]
    [DescAttribute("单位模板数据")]
    [TableClassAttribute("ID")]
    public class UnitInfo : ICloneable, ITemplateData, IExternalizable
    {
		protected static readonly Logger log = LoggerFactory.GetDefLogger();
		public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }
        /// <summary>
        /// 单位类型
        /// </summary>
        public enum UnitType : byte
        {
            [DescAttribute("玩家")]
            TYPE_PLAYER = 1,
            [DescAttribute("NPC")]
            TYPE_NPC = 2,
            [DescAttribute("怪物")]
            TYPE_MONSTER = 3,
            [DescAttribute("宠物")]
            TYPE_PET = 4,
            [DescAttribute("召唤物")]
            TYPE_SUMMON = 5,
            [DescAttribute("建筑")]
            TYPE_BUILDING = 6,
            [DescAttribute("脚本控制")]
            TYPE_MANUAL = 7,
            [DescAttribute("触发机关")]
            TYPE_TRIGGER = 8,
            [DescAttribute("中立无敌意")]
            TYPE_NEUTRALITY = 9,
        }
        //------------------------------------
        /// <summary>
        /// 单位类型
        /// </summary>
        [DescAttribute("单位类型", "基础")]
        public UnitType UType = UnitType.TYPE_MONSTER;
        [DescAttribute(
            "单位类型"
            + "\n PLAYER=1"
            + "\n NPC=2"
            + "\n MONSTER=3"
            + "\n PET=4"
            + "\n SUMMON=5"
            + "\n BUILDING=6"
            + "\n MANUAL=7"
            + "\n TRIGGER=8"
            + "\n NEUTRALITY=9",
            "基础")]
        public int UTypeAsInt { get { return (int)UType; } }


		public bool IsSummon { get { return UType == UnitType.TYPE_SUMMON; } }
		[DescAttribute("召唤类型", "基础")]
		[DependOnProperty("IsSummon")]
		public SummonType SumType = SummonType.none;

		/// <summary>
		/// 模板ID
		/// </summary>
		[DescAttribute("ID", "", false)]
        public int ID;
        /// <summary>
        /// 单位名字
        /// </summary>
        [LocalizationTextAttribute]
        [DescAttribute("单位名字", "基础")]
        public string Name;

        [DescAttribute("是否为精英怪", "基础")]
        public bool IsElite;

        //------------------------------------
        /// <summary>
        /// 对应的模型文件名
        /// </summary>
        [DescAttribute("对应的模型文件名", "资源")]
        [ResourceIDAttribute]
        public string FileName;

        [DescAttribute("怪物产生时触发的特效", "资源")]
        public LaunchEffect SpawnEffect;

        [DescAttribute("怪物脚底的特效", "资源")]
        public LaunchEffect FootCircleEffect;

        [DescAttribute("怪物死亡时触发的特效", "资源")]
        public LaunchEffect DeadEffect;

        [DescAttribute("怪物死亡动作播放结束后触发的特效", "资源")]
        public LaunchEffect DeadAnimationEndEffect;

        [DescAttribute("怪物烂掉时触发的特效", "资源")]
        public LaunchEffect RemovedEffect;

        [DescAttribute("怪物击碎时触发的特效", "资源")]
        public LaunchEffect CrushEffect;

        [DescAttribute("怪物受击时触发的特效", "资源")]
        public LaunchEffect DamageEffect;

        [DescAttribute("缩放比率", "资源")]
        public float BodyScale = 1;

		[DescAttribute("切换阵营特效", "资源")]
		public LaunchEffect ChangeForceEffect;

		[DescAttribute("破碎比例", "资源")]
		public int HitBreakPrecent;

		[DescAttribute("破碎特效", "资源")]
		public LaunchEffect HitBreakEffect;

        [DescAttribute("预加载资源列表", "资源")]
        public string PreloadResources;

        //------------------------------------
        /// <summary>
        /// 身体高度
        /// </summary>
        [DescAttribute("身体高度", "战斗")]
        public float BodyHeight;
        /// <summary>
        /// 身体尺寸(半径)
        /// </summary>
        [DescAttribute("身体尺寸(半径)", "战斗")]
        public float BodySize = 1;
        /// <summary>
        /// 警戒距离范围
        /// </summary>
        [DescAttribute("身体受攻击尺寸(半径)", "战斗")]
        public float BodyHitSize = 1;
        //------------------------------------
        /// <summary>
        /// 警戒距离范围
        /// </summary>
        [DescAttribute("警戒距离范围", "战斗 - 警戒")]
        public float GuardRange = 5;
        [DescAttribute("最大警戒范围,超过此范围不追击,0表示无限", "战斗 - 警戒")]
        public float GuardRangeLimit = 20;
        [DescAttribute("分组警戒范围,如果此单位进入战斗,则传给相邻单位Add,0表示不传递", "战斗 - 警戒")]
        public float GuardRangeGroup = 5;

        public bool IdleRecover { get { return RecoveryIntervalMS > 0; } }

        [DescAttribute("恢复间隔(毫秒),脱离战斗后回血间隔时间", "战斗 - 恢复")]
        public int RecoveryIntervalMS = 10000;
        [DescAttribute("血量恢复/秒", "战斗 - 恢复")]
        [DependOnProperty("IdleRecover")]
        public int HealthRecoveryPoint = 0;
        [DescAttribute("法力恢复/秒", "战斗 - 恢复")]
        [DependOnProperty("IdleRecover")]
        public int ManaRecoveryPoint = 0;
        //------------------------------------

        [DescAttribute("体重(>=100不能被击飞)", "战斗")]
        public int Weight = 1;

        [DescAttribute("是否可移动", "战斗")]
        public bool IsMoveable = true;

        [DescAttribute("是否可转向", "战斗")]
        public bool IsTurnable = true;

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

        /// <summary>
        /// 移动距离,像素或者码
        /// </summary>
        [DescAttribute("移动速度(距离/每秒)", "战斗")]
        public float MoveSpeedSEC = 6f;

        [DescAttribute("转身速度(弧度/秒)", "战斗")]
        public float TurnSpeedSEC = float.NaN;

        [DescAttribute("受击时间(毫秒)", "战斗")]
        public int DamageTimeMS = 10;

        //------------------------------------
        /// <summary>
        /// 此单位普通攻击技能
        /// </summary>
        [DescAttribute("此单位普通攻击技能", "技能")]
        public LaunchSkill BaseSkillID = new LaunchSkill();
        /// <summary>
        /// 此单位绑定的所有技能ID
        /// </summary>
        [ListAttribute(typeof(LaunchSkill))]
        [DescAttribute("此单位绑定的所有技能ID", "技能")]
        public List<LaunchSkill> Skills = new List<LaunchSkill>();
        /// <summary>
        /// 触发类特效
        /// </summary>
        [ListAttribute(typeof(LaunchTrigger))]
        [DescAttribute("触发类", "技能")]
        public List<LaunchTrigger> Triggers = new List<LaunchTrigger>();
        //------------------------------------

        //------------------------------------
        [DescAttribute("法术发射高度(炮口高度)", "炮口")]
        public float LaunchSpellHeight;
        [DescAttribute("法术发射半径(炮口半径)", "炮口")]
        public float LaunchSpellRadius = 0;

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

        [DescAttribute("死亡时释放法术(自爆)", "战斗")]
        public LaunchSpell DeadLaunchSpell = null;

        [DescAttribute("死亡时释放法术(灵魂出窍)", "战斗")]
        public LaunchSpell DeadDelayLaunchSpell = null;

        //------------------------------------
        [DescAttribute("血量", "战斗")]
        public int HealthPoint = 100;
        [DescAttribute("法力值", "战斗")]
        public int ManaPoint = 100;
        [DescAttribute("耐力值", "战斗")]
        public int StaminaPoint = 100;
        [DescAttribute("产生经验", "战斗")]
        public int GenExp;
        //------------------------------------
        /// <summary>
        /// 掉落道具列表
        /// </summary>
        [ListAttribute(typeof(DropItemList))]
        [DescAttribute("掉落道具", "掉落")]
        public List<DropItemList> DropItemsSet = new List<DropItemList>();

        [DescAttribute("被杀死直接获得金币", "掉落")]
        public int DropMoney;
        //------------------------------------

        [DescAttribute("单位背包数量", "背包")]
        public int InventorySize = 1;

        [ListAttribute(typeof(InventoryItem))]
        [DescAttribute("进入战斗携带的道具列表(比如血瓶)", "背包")]
        public List<InventoryItem> InventoryList = new List<InventoryItem>();

        //------------------------------------
        /// <summary>
        /// 若是召唤类,则表示存活时间
        /// </summary>
        [DescAttribute("若是召唤类,则表示存活时间(毫秒)", "战斗")]
        public int LifeTimeMS;
        /// <summary>
        /// 单位出生延时时间,通常播放出生动画
        /// </summary>
        [DescAttribute("单位出生延时时间,通常播放出生动画(毫秒)", "战斗")]
        public int SpawnTimeMS;
        /// <summary>
        /// 持续时间
        /// </summary>
        [DescAttribute("单位死亡持续时间,即死亡后多长时间内可继续鞭尸(毫秒)", "战斗")]
        public int DeadTimeMS;
        /// <summary>
        /// 
        /// </summary>
        [DescAttribute("单位死亡之后延迟时间(毫秒),播放 DeadDelayLaunchSpell 特效", "战斗")]
        public int DeadDelayTimeMS;
        /// <summary>
        /// 
        /// </summary>
        [DescAttribute("播放 DeadDelayLaunchSpell 特效的数量", "战斗")]
        public int DeadDelaySpellNumber;

        public bool HasNumbers
        {
            get { return DeadDelaySpellNumber > 1; }
        }

        /// <summary>
        /// 
        /// </summary>
        [DescAttribute("播放每一个 DeadDelayLaunchSpell 间隔的时间(单位:毫秒)", "战斗")]
        [DependOnProperty("HasNumbers")]
        public int DeadDelaySpellIntervalMS;
        /// <summary>
        /// 复活时间
        /// </summary>
        [DescAttribute("死亡后多久复活,0表示不能复活(毫秒)", "战斗")]
        public int RebirthTimeMS;
        //------------------------------------
        [DescAttribute("用户自定义标志", "脚本")]
        public string UserTag;
        //------------------------------------

        [DescAttribute("是否为动态创建单位,比如PVP或者根据玩家等级创建的单位", "扩展")]
        public bool IsDynamic = false;

		[DescAttribute("是否随机名字", "扩展")]
		public bool IsRandomName = false;
		//------------------------------------

		[DescAttribute("单位绑定触发事件ID", "事件")]
        [TemplatesIDAttribute(typeof(UnitEventTemplate))]
        public List<int> Events = new List<int>();

        //------------------------------------
        /// <summary>
        /// 用户自定义扩展属性
        /// </summary>
        [DescAttribute("扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public IUnitProperties Properties;
        //------------------------------------

        [DescAttribute("自定义字段", "属性", true)]
        public string[] Attributes;
        //------------------------------------

        public UnitInfo()
        {
            Properties = TemplateManager.Factory.CreateUnitProperties();
        }
        public int GetID()
        {
            return ID;
        }
        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }
        public object Clone()
        {
            UnitInfo ret = new UnitInfo();
            ret.EditorPath = this.EditorPath;

            ret.UType = this.UType;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.IsElite = this.IsElite;
            ret.FileName = this.FileName;

            ret.SpawnEffect = CUtils.TryClone<LaunchEffect>(this.SpawnEffect);
            ret.FootCircleEffect = CUtils.TryClone<LaunchEffect>(this.FootCircleEffect);
            ret.DeadEffect = CUtils.TryClone<LaunchEffect>(this.DeadEffect);
            ret.RemovedEffect = CUtils.TryClone<LaunchEffect>(this.RemovedEffect);
            ret.DeadAnimationEndEffect = CUtils.TryClone<LaunchEffect>(this.DeadAnimationEndEffect);
            ret.CrushEffect = CUtils.TryClone<LaunchEffect>(this.CrushEffect);
            ret.DamageEffect = CUtils.TryClone<LaunchEffect>(this.DamageEffect);
            ret.BodyScale = this.BodyScale;

            ret.BodyHeight = this.BodyHeight;
            ret.BodySize = this.BodySize;
            ret.BodyHitSize = this.BodyHitSize;

            ret.GuardRange = this.GuardRange;
            ret.GuardRangeLimit = this.GuardRangeLimit;
            ret.GuardRangeGroup = this.GuardRangeGroup;
            ret.HealthRecoveryPoint = this.HealthRecoveryPoint;
            ret.ManaRecoveryPoint = this.ManaRecoveryPoint;
            ret.RecoveryIntervalMS = this.RecoveryIntervalMS;

            ret.Weight = this.Weight;
            ret.MoveSpeedSEC = this.MoveSpeedSEC;
            ret.TurnSpeedSEC = this.TurnSpeedSEC;
            ret.DamageTimeMS = this.DamageTimeMS;
            ret.IsMoveable = this.IsMoveable;
            ret.IsTurnable = this.IsTurnable;

            ret.BaseSkillID = CUtils.TryClone<LaunchSkill>(this.BaseSkillID);

            ret.Skills = CUtils.CloneList<LaunchSkill>(this.Skills);
            ret.Triggers = CUtils.CloneList<LaunchTrigger>(this.Triggers);

            ret.LaunchSpellHeight = this.LaunchSpellHeight;
            ret.LaunchSpellRadius = this.LaunchSpellRadius;
            ret.DeadLaunchSpell = this.DeadLaunchSpell;
            ret.DeadDelayLaunchSpell = this.DeadDelayLaunchSpell;

            ret.HealthPoint = this.HealthPoint;
            ret.ManaPoint = this.ManaPoint;
            ret.StaminaPoint = this.StaminaPoint;
            ret.GenExp = this.GenExp;

            ret.DropItemsSet = CUtils.CloneList<DropItemList>(this.DropItemsSet);

            ret.DropMoney = this.DropMoney;

            ret.InventorySize = this.InventorySize;
            ret.InventoryList = CUtils.CloneList<InventoryItem>(this.InventoryList);

            ret.LifeTimeMS = this.LifeTimeMS;
            ret.SpawnTimeMS = this.SpawnTimeMS;
            ret.DeadTimeMS = this.DeadTimeMS;
            ret.DeadDelayTimeMS = this.DeadDelayTimeMS;
            ret.DeadDelaySpellNumber = this.DeadDelaySpellNumber;
            ret.DeadDelaySpellIntervalMS = this.DeadDelaySpellIntervalMS;
            ret.RebirthTimeMS = this.RebirthTimeMS;
            ret.UserTag = this.UserTag;
            ret.IsDynamic = this.IsDynamic;

            ret.Events = new List<int>(this.Events);

            ret.Properties = CUtils.TryClone<IUnitProperties>(this.Properties);// (IUnitProperties)this.Properties.Clone();
            ret.Attributes = CUtils.CloneArray<string>(this.Attributes);
			ret.HitBreakPrecent = this.HitBreakPrecent;
			ret.HitBreakEffect = CUtils.TryClone<LaunchEffect>(this.HitBreakEffect);
			ret.ChangeForceEffect = CUtils.TryClone<LaunchEffect>(this.ChangeForceEffect);
			ret.HitBreakPrecent = this.HitBreakPrecent;
			ret.HitBreakEffect = this.HitBreakEffect;
			ret.IsRandomName = this.IsRandomName;
			ret.SumType = this.SumType;
            ret.PreloadResources = this.PreloadResources;
			return ret;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutEnum8(this.UType);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutBool(this.IsElite);
            output.PutUTF(this.FileName);

            output.PutExt(this.SpawnEffect);
            output.PutExt(this.FootCircleEffect);
            output.PutExt(this.DeadEffect);
            output.PutExt(this.RemovedEffect);
            output.PutExt(this.DeadAnimationEndEffect);
            output.PutExt(this.CrushEffect);
            output.PutExt(this.DamageEffect);
            output.PutF32(this.BodyScale);

            output.PutF32(this.BodyHeight);
            output.PutF32(this.BodySize);
            output.PutF32(this.BodyHitSize);

            output.PutF32(this.GuardRange);
            output.PutF32(this.GuardRangeLimit);
            output.PutF32(this.GuardRangeGroup);
            output.PutS32(this.RecoveryIntervalMS);
            output.PutS32(this.HealthRecoveryPoint);
            output.PutS32(this.ManaRecoveryPoint);

            output.PutS32(this.Weight);
            output.PutF32(this.MoveSpeedSEC);
            output.PutF32(this.TurnSpeedSEC);
            output.PutS32(this.DamageTimeMS);
            output.PutBool(this.IsMoveable);
            output.PutBool(this.IsTurnable);

            output.PutExt(this.BaseSkillID);

            output.PutList<LaunchSkill>(this.Skills, output.PutExt);
            output.PutList<LaunchTrigger>(this.Triggers, output.PutExt);

            output.PutF32(this.LaunchSpellHeight);
            output.PutF32(this.LaunchSpellRadius);
            output.PutExt(this.DeadLaunchSpell);
            output.PutExt(this.DeadDelayLaunchSpell);
            output.PutS32(this.HealthPoint);
            output.PutS32(this.ManaPoint);
            output.PutS32(this.StaminaPoint);
            output.PutS32(this.GenExp);

            output.PutList<DropItemList>(this.DropItemsSet, output.PutExt);

            output.PutS32(this.DropMoney);
            output.PutS32(this.InventorySize);
            output.PutList<InventoryItem>(this.InventoryList, output.PutExt);

            output.PutS32(this.LifeTimeMS);
            output.PutS32(this.SpawnTimeMS);
            output.PutS32(this.DeadTimeMS);
            output.PutS32(this.DeadDelayTimeMS);
            output.PutS32(this.DeadDelaySpellNumber);
            output.PutS32(this.DeadDelaySpellIntervalMS);
            output.PutS32(this.RebirthTimeMS);
            output.PutUTF(this.UserTag);
            output.PutBool(this.IsDynamic);

            output.PutList(this.Events, output.PutS32);
            output.PutArray(this.Attributes, output.PutUTF);

            output.PutExt(this.Properties);

			//破碎,切阵营
			output.PutExt(this.ChangeForceEffect);
			output.PutS32(this.HitBreakPrecent);
			output.PutExt(this.HitBreakEffect);

			// 随机名字
			output.PutBool(this.IsRandomName);
			output.PutEnum8(this.SumType);

            output.PutUTF(this.PreloadResources);
		}

        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.UType = input.GetEnum8<UnitType>();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.IsElite = input.GetBool();
            this.FileName = input.GetUTF();

            this.SpawnEffect = input.GetExt<LaunchEffect>();
            this.FootCircleEffect = input.GetExt<LaunchEffect>();
            this.DeadEffect = input.GetExt<LaunchEffect>();
            this.RemovedEffect = input.GetExt<LaunchEffect>();
            this.DeadAnimationEndEffect = input.GetExt<LaunchEffect>();
            this.CrushEffect = input.GetExt<LaunchEffect>();
            this.DamageEffect = input.GetExt<LaunchEffect>();
            this.BodyScale = input.GetF32();

            this.BodyHeight = input.GetF32();
            this.BodySize = input.GetF32();
            this.BodyHitSize = input.GetF32();

            this.GuardRange = input.GetF32();
            this.GuardRangeLimit = input.GetF32();
            this.GuardRangeGroup = input.GetF32();
            this.RecoveryIntervalMS = input.GetS32();
            this.HealthRecoveryPoint = input.GetS32();
            this.ManaRecoveryPoint = input.GetS32();


            this.Weight = input.GetS32();
            this.MoveSpeedSEC = input.GetF32();
            this.TurnSpeedSEC = input.GetF32();
            this.DamageTimeMS = input.GetS32();
            this.IsMoveable = input.GetBool();
            this.IsTurnable = input.GetBool();

            this.BaseSkillID = input.GetExt<LaunchSkill>();

            this.Skills = input.GetList<LaunchSkill>(input.GetExt<LaunchSkill>);
            this.Triggers = input.GetList<LaunchTrigger>(input.GetExt<LaunchTrigger>);

            this.LaunchSpellHeight = input.GetF32();
            this.LaunchSpellRadius = input.GetF32();
            this.DeadLaunchSpell = input.GetExt<LaunchSpell>();
            this.DeadDelayLaunchSpell = input.GetExt<LaunchSpell>();
            this.HealthPoint = input.GetS32();
            this.ManaPoint = input.GetS32();
            this.StaminaPoint = input.GetS32();
            this.GenExp = input.GetS32();

            this.DropItemsSet = input.GetList<DropItemList>(input.GetExt<DropItemList>);

            this.DropMoney = input.GetS32();
            this.InventorySize = input.GetS32();
            this.InventoryList = input.GetList<InventoryItem>(input.GetExt<InventoryItem>);

            this.LifeTimeMS = input.GetS32();
            this.SpawnTimeMS = input.GetS32();
            this.DeadTimeMS = input.GetS32();
            this.DeadDelayTimeMS = input.GetS32();
            this.DeadDelaySpellNumber = input.GetS32();
            this.DeadDelaySpellIntervalMS = input.GetS32();
            this.RebirthTimeMS = input.GetS32();
            this.UserTag = input.GetUTF();
            this.IsDynamic = input.GetBool();

            this.Events = input.GetList<int>(input.GetS32);
            this.Attributes = input.GetUTFArray();

            this.Properties = input.GetExt<IUnitProperties>(this.Properties);

			this.ChangeForceEffect = input.GetExt<LaunchEffect>();
			this.HitBreakPrecent = input.GetS32();
			this.HitBreakEffect = input.GetExt<LaunchEffect>();

			this.IsRandomName = input.GetBool();
			this.SumType = input.GetEnum8<SummonType>();

            this.PreloadResources = input.GetUTF();

			if (UType == UnitType.TYPE_NPC && this.RecoveryIntervalMS > 0)
			{
				log.Debug("NPC配置了恢复时间, 强行重置" + this.ID);
				this.RecoveryIntervalMS = 0;
			}
		}

        public LaunchSkill GetSkillByID(int skillID)
        {
            if (BaseSkillID != null && skillID == BaseSkillID.SkillID)
            {
                return BaseSkillID;
            }
            if (Skills != null)
            {
                for (int i = 0; i < Skills.Count; i++)
                {
                    if (Skills[i].SkillID == skillID)
                    {
                        return Skills[i];
                    }
                }
            }
            return null;
        }
    }

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


    /// <summary>
    /// 技能模板
    /// </summary>
    [MessageType(0x4003)]
    [DescAttribute("技能模板")]
    [TableClassAttribute("ID")]
    public class SkillTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        // 技能ID
        [DescAttribute("ID", "", false)]
        public int ID;

        /// <summary>
        /// 技能名字
        /// </summary>
        [LocalizationTextAttribute]
        [DescAttribute("技能名字", "基础")]
        public string Name;

        [DescAttribute("技能冷却时间(毫秒)", "基础")]
        [DependOnProperty("IsCoolDownWithAction", false)]
        public int CoolDownMS = 6000;
        //------------------------------------
        [DescAttribute("技能图标", "资源")]
        public string IconName;
        //------------------------------------
        /// <summary>
        /// 攻击动作序列
        /// </summary>
        [DescAttribute("攻击动作序列", "动作")]
        [ListAttribute(typeof(UnitActionData))]
        public List<UnitActionData> ActionQueue = new List<UnitActionData>();

        [DescAttribute("以动作时间作为冷却时间, 普攻使用", "动作")]
        //只能普攻使用
        public bool IsCoolDownWithAction = false;
        [DescAttribute("可以打断其他技能和受击状态,受身技能", "动作")]
        public bool IsCounter = false;
        [DescAttribute("技能优先级,高优先级可打断低优先级", "动作")]
        public int ActionPriority = 0;

        [DescAttribute("动作播放速率", "动作")]
        public float ActionSpeedRate = 1f;

        [DescAttribute("技能释放期间,模型缩放(仅客户端有效)", "动作")]
        public float BodyScale = 1f;

        [DescAttribute("技能释放期间,可手动撤销", "动作")]
        public bool IsManuallyCancelable = false;

        [DescAttribute("忽略本技能释放中不可打断的时间段(只用作客户端显示用)", "动作")]
        public bool IgnoreMyUncancelable = false;

        //------------------------------------
        [DescAttribute("每次释放只做一个动作,多段攻击", "多段攻击")]
        public bool IsSingleAction = false;
        [DependOnProperty("IsSingleAction")]
        [DescAttribute("多段攻击间隔有效时间", "多段攻击")]
        public int SingleActionCoolDownMS = 500;
        //------------------------------------

        [DescAttribute("攻击距离", "攻击范围")]
        public float AttackRange = 3;
        [DescAttribute("攻击角度范围(弧度)\n如果角度为0,则为单体攻击", "攻击范围")]
        public float AttackAngle = 0;
        [DescAttribute("单体攻击,只在目标处于攻击范围才产生伤害", "攻击范围")]
        public bool AttackMustBeInRange = true;
        [DescAttribute("保持攻击距离(NPC会保持一段距离再攻击)大于0启效", "攻击范围")]
        public float AttackKeepRange = 0;
		[DescAttribute("自动战斗,跟随目标", "攻击范围")]
		public bool AutoFightFollower = false;

		//------------------------------------
		[DescAttribute("技能当前储存次数")]
        public short CurUseTimes = -1;

        [DescAttribute("法术是否从自己身上发出", "释放")]
        public bool IsLaunchBody = true;

        [DescAttribute("吟唱时间(毫秒)大于0启效", "释放")]
        public int ChantTimeMS = 0;
        //------------------------------------
        public enum CastTarget : byte
        {
            [DescAttribute("敌人")]
            Enemy = 0,
            [DescAttribute("友军")]
            Alias = 1,
            [DescAttribute("自己")]
            Self = 2,
            [DescAttribute("宠物为主人")]
            PetForMaster = 3,
            [DescAttribute("任何单位")]
            EveryOne = 4,
            [DescAttribute("无类型")]
            NA = 5,
            [DescAttribute("除自己之外友军")]
            AlliesExcludeSelf = 6,
            [DescAttribute("包括自己友军")]
            AlliesIncludeSelf = 7,
            [DescAttribute("除自己之外所有")]
            EveryOneExcludeSelf = 8,
			[DescAttribute("自己和敌人")]
			EnemyAndSelf = 9,
		}
        [DescAttribute("技能期望作用目标", "释放")]
        public CastTarget ExpectTarget = CastTarget.Enemy;

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

        public enum SelectRange : byte
        {
            [DescAttribute("无")]
            NA,
            [DescAttribute("圆形区域")]
            Round,
            [DescAttribute("直线")]
            Line,
            [DescAttribute("矩形区域")]
            Rect,
            [DescAttribute("扇形区域")]
            Fan,
        }

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

        [DescAttribute("释放时是否选择方向", "释放范围")]
        public bool IsSelectRange = false;
        [DependOnProperty("IsSelectRange")]
        [DescAttribute("选择方向类型", "释放范围")]
        public SelectRange SelectRangeType = SelectRange.NA;
        [DependOnProperty("IsSelectRange")]
        [DescAttribute("选择方向尺寸", "释放范围")]
        public float SelectRangeSize;


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

        [DescAttribute("释放消耗血量", "消耗")]
        public int CostHP;
        [DescAttribute("释放消耗法力", "消耗")]
        public int CostMP;

        //------------------------------------
        /// <summary>
        /// 用户自定义扩展属性
        /// </summary>
        [DescAttribute("扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public ISkillProperties Properties = TemplateManager.Factory.CreateSkillProperties();
        //------------------------------------

        public SkillTemplate() { }
        public SkillTemplate(int id)
        {
            this.ID = id;
        }
        public int GetID()
        {
            return ID;
        }
        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }

        public UnitActionData GetActionByName(string name)
        {
            foreach (UnitActionData act in ActionQueue)
            {
                if (name.Equals(act.ActionName))
                {
                    return act;
                }
            }
            return null;
        }
        public int ToUnitSkillTotalTime(float fastCastRate)
        {
            return (int)(this.CoolDownMS * Math.Max(0, 1f - fastCastRate));
        }

        public int ActionQueueTimeMS
        {
            get
            {
                int ret = 0;
                for (int i = 0; i < ActionQueue.Count; i++)
                {
                    ret += ActionQueue[i].TotalTimeMS;
                }
                return ret;
            }
        }

        public int[] ActionQueueTimeArray
        {
            get
            {
                int[] ret = new int[ActionQueue.Count];
                for (int i = ret.Length - 1; i >= 0; --i)
                {
                    ret[i] = ActionQueue[i].TotalTimeMS;
                }
                return ret;
            }
        }

        public object Clone()
        {
            SkillTemplate ret = new SkillTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.IconName = this.IconName;
            ret.ActionQueue = CUtils.CloneList<UnitActionData>(this.ActionQueue);
            ret.IsSingleAction = this.IsSingleAction;
            ret.SingleActionCoolDownMS = this.SingleActionCoolDownMS;
            ret.IsCoolDownWithAction = this.IsCoolDownWithAction;
            ret.IsCounter = this.IsCounter;
            ret.ActionPriority = this.ActionPriority;
            ret.CoolDownMS = this.CoolDownMS;
            ret.ActionSpeedRate = this.ActionSpeedRate;
            ret.IsManuallyCancelable = this.IsManuallyCancelable;
            ret.IgnoreMyUncancelable = this.IgnoreMyUncancelable;
            ret.BodyScale = this.BodyScale;

            ret.AttackRange = this.AttackRange;
            ret.AttackAngle = this.AttackAngle;
            ret.AttackMustBeInRange = this.AttackMustBeInRange;
            ret.AttackKeepRange = this.AttackKeepRange;
			ret.AutoFightFollower = this.AutoFightFollower;

			ret.IsLaunchBody = this.IsLaunchBody;
            ret.ChantTimeMS = this.ChantTimeMS;
            ret.IsSelectRange = this.IsSelectRange;
            ret.SelectRangeType = this.SelectRangeType;
            ret.SelectRangeSize = this.SelectRangeSize;
            ret.CostHP = this.CostHP;
            ret.CostMP = this.CostMP;
            ret.ExpectTarget = this.ExpectTarget;
            //ret.CurUseTimes = this.CurUseTimes;
            ret.Properties = CUtils.TryClone<ISkillProperties>(this.Properties);
            return ret;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutUTF(this.IconName);
            output.PutList(this.ActionQueue, output.PutExt);
            output.PutBool(this.IsSingleAction);
            output.PutS32(this.SingleActionCoolDownMS);
            output.PutBool(this.IsCoolDownWithAction);
            output.PutBool(this.IsCounter);
            output.PutS32(this.ActionPriority);
            output.PutS32(this.CoolDownMS);
            output.PutF32(this.ActionSpeedRate);
            output.PutBool(this.IsManuallyCancelable);
            output.PutBool(this.IgnoreMyUncancelable);
            output.PutF32(this.BodyScale);

            output.PutF32(this.AttackRange);
            output.PutF32(this.AttackAngle);
            output.PutBool(this.AttackMustBeInRange);
            output.PutF32(this.AttackKeepRange);
			output.PutBool(this.AutoFightFollower);

            output.PutBool(this.IsLaunchBody);
            output.PutS32(this.ChantTimeMS);
            output.PutBool(this.IsSelectRange);
            output.PutEnum8(this.SelectRangeType);
            output.PutF32(this.SelectRangeSize);
            output.PutS32(this.CostHP);
            output.PutS32(this.CostMP);
            output.PutEnum8(this.ExpectTarget);
            output.PutS16(this.CurUseTimes);
            output.PutExt(this.Properties);
        }

        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.IconName = input.GetUTF();
            this.ActionQueue = input.GetList<UnitActionData>(input.GetExt<UnitActionData>);
            this.IsSingleAction = input.GetBool();
            this.SingleActionCoolDownMS = input.GetS32();
            this.IsCoolDownWithAction = input.GetBool();
            this.IsCounter = input.GetBool();
            this.ActionPriority = input.GetS32();
            this.CoolDownMS = input.GetS32();
            this.ActionSpeedRate = input.GetF32();
            this.IsManuallyCancelable = input.GetBool();
            this.IgnoreMyUncancelable = input.GetBool();
            this.BodyScale = input.GetF32();

            this.AttackRange = input.GetF32();
            this.AttackAngle = input.GetF32();
            this.AttackMustBeInRange = input.GetBool();
            this.AttackKeepRange = input.GetF32();
			this.AutoFightFollower = input.GetBool();
			
			this.IsLaunchBody = input.GetBool();
            this.ChantTimeMS = input.GetS32();
            this.IsSelectRange = input.GetBool();
            this.SelectRangeType = input.GetEnum8<SelectRange>();
            this.SelectRangeSize = input.GetF32();
            this.CostHP = input.GetS32();
            this.CostMP = input.GetS32();
            this.ExpectTarget = input.GetEnum8<CastTarget>();
            this.CurUseTimes = input.GetS16();
            this.Properties = input.GetExt<ISkillProperties>(this.Properties);
        }


    }

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

    /// <summary>
    /// 单位动作模板
    /// </summary>
    [MessageType(0x4004)]
    [DescAttribute("单位动作")]
    [Expandable]
    public class UnitActionData : ICloneable, IExternalizable
    {
        [DescAttribute("总动作时间(毫秒)", "Action")]
        public int TotalTimeMS;
        [DescAttribute("动作播放速率增加(加法)", "动作")]
        public float ActionSpeedRate = 0.0f;
        [DescAttribute("单一动作类型,0-普通, 1,蓄力动作,2-多段式动作", "Action")]
        //蓄力动作,显示倒计时圈
        //多段式动作,显示倒计时圈 + 倒计时
        public ActionEnum SigleActionType;
        [DescAttribute("动作名字, IsSingleAction=false才有效", "Action")]
        public string ActionName;
        [DependOnProperty("IsControlMoveable", true)]
        [DescAttribute("移动动作名字", "Action")]
        public string MoveActionName;
        [DescAttribute("动作对应的特效,通常用作刀光", "Action")]
        [ResourceIDAttribute]
        public string ActionEffectFileName;
        [DescAttribute("动作是否循环", "Action")]
        public bool IsCycAction;
        [DescAttribute("显示蓄力倒计时条时间(毫秒)", "Action")]
        public int ShowChargeTimeMS = 0;


        [DescAttribute("移动可取消动作", "状态")]
        public bool IsCancelable = false;
        [DescAttribute("技能可取消动作", "状态")]
        public bool IsCancelableBySkill = false;
        [DescAttribute("技能能否被下一段替代", "状态")]
        public bool IsCancelBySkillNext = false;

        [DescAttribute("是否进入霸体状态,不会被打断", "状态")]
        public bool IsNoneBlock = false;
        [DescAttribute("动作期间是否无碰撞", "状态")]
        public bool IsNoneTouch = false;
        [DescAttribute("动作中是否面向目标", "状态")]
        public bool IsFaceToTarget = true;
        [DescAttribute("动作是否隐身", "状态")]
        public bool IsInvisible = false;
        [DescAttribute("动作中是否可以控制移动", "状态")]
        public bool IsControlMoveable = false;
        [DescAttribute("动作中可以控制转向", "状态")]
        public bool IsControlFaceable = false;

        //新增动作对应Icon
        [DescAttribute("是否换图", "资源")]
        public bool IsChangeIcon = false;
        [DescAttribute("图片名字", "资源")]
        [DependOnProperty("IsChangeIcon", true)]
        public string IconName;

        [DescAttribute("攻击范围改变", "攻击范围")]
        public AttackShape OverrideAttackShape;

        /// <summary>
        /// 所有关键帧
        /// </summary>
        [ListAttribute(typeof(KeyFrame))]
        [DescAttribute("所有关键帧", "关键帧")]
        public List<KeyFrame> KeyFrames = new List<KeyFrame>();

        public bool IsBodyHit
        {
            get
            {
                if (IsMoveToTarget) return false;
                if (IsJumpToTarget) return false;
                return BodyHit != null;
            }
        }
        /// <summary>
        /// 身体攻击判定
        /// </summary>
        [DependOnProperty("IsMoveToTarget", false)]
        [DependOnProperty("IsJumpToTarget", false)]
        [DescAttribute("身体攻击判定", "身体攻击")]
        public AttackProp BodyHit;
        [DependOnProperty("IsBodyHit")]
        [DescAttribute("身体攻击判定范围", "身体攻击")]
        public float BodyHitSize;
        [DependOnProperty("IsBodyHit")]
        [DescAttribute("身体攻击后立即切换动作", "身体攻击")]
        public bool BodyHitNextAction;

        [DescAttribute("移动到目标", "位移 - 冲锋")]
        public bool IsMoveToTarget = false;
        [DescAttribute("移动到目标速度", "位移 - 冲锋")]
        [DependOnProperty("IsMoveToTarget")]
        public float MoveToTargetSpeedSEC = 10;

        [DescAttribute("移动到目标根据(距离/动作时间)来算速度,如果目标在原地,则原地跳跃。", "位移 - 跳跃")]
        [DependOnProperty("IsMoveToTarget", false)]
        public bool IsJumpToTarget = false;
        [DescAttribute("跳跃高度", "位移 - 跳跃")]
        [DependOnProperty("IsJumpToTarget")]
        public float JumpToTargetHeightZ = 1;

        [DescAttribute("在攻击范围内,如果位移则挤开目标", "碰撞")]
        [DependOnProperty("IsMoveToTarget", false)]
        [DependOnProperty("IsJumpToTarget", false)]
        public bool BodyBlockOnAttackRange = false;

        [DescAttribute("显示技能引导光圈(必须先打开扩展属性中的ShowLaunchGuide)", "控制")]
        public bool IsShowSkillGuide = false;

        [DescAttribute("增加技能按钮遮罩", "资源")]
        public bool IsAddSkillBtnBlock = false;

        [DescAttribute("动作的音效文件名", "资源")]
        public string ActionAudioName;
        [DescAttribute("动作的音效文件是否循环播放", "资源")]
        public bool IsAudioLoop;

        public UnitActionData() { }
        public override string ToString()
        {
            return "动作: " + ActionName + " (" + TotalTimeMS + "(毫秒))";
        }
        public object Clone()
        {
            UnitActionData ret = new UnitActionData();
            ret.ShowChargeTimeMS = this.ShowChargeTimeMS;
            ret.TotalTimeMS = this.TotalTimeMS;
            ret.ActionName = this.ActionName;
            ret.ActionEffectFileName = this.ActionEffectFileName;
            ret.IsCycAction = this.IsCycAction;
            ret.IsCancelable = this.IsCancelable;
            ret.IsCancelableBySkill = this.IsCancelableBySkill;
            ret.IsCancelBySkillNext = this.IsCancelBySkillNext;
            ret.IsNoneBlock = this.IsNoneBlock;
            ret.IsNoneTouch = this.IsNoneTouch;
            ret.IsFaceToTarget = this.IsFaceToTarget;
            ret.IsInvisible = this.IsInvisible;
            ret.IsControlMoveable = this.IsControlMoveable;
            ret.IsControlFaceable = this.IsControlFaceable;
            ret.IsChangeIcon = this.IsChangeIcon;
            ret.IconName = this.IconName;

            ret.OverrideAttackShape = CUtils.TryClone<AttackShape>(this.OverrideAttackShape);
            ret.KeyFrames = CUtils.CloneList<KeyFrame>(this.KeyFrames);

            ret.BodyHit = CUtils.TryClone<AttackProp>(this.BodyHit);
            ret.BodyHitSize = this.BodyHitSize;
            ret.BodyHitNextAction = this.BodyHitNextAction;

            ret.IsMoveToTarget = this.IsMoveToTarget;
            ret.MoveToTargetSpeedSEC = this.MoveToTargetSpeedSEC;
            ret.IsJumpToTarget = this.IsJumpToTarget;
            ret.JumpToTargetHeightZ = this.JumpToTargetHeightZ;

            ret.BodyBlockOnAttackRange = this.BodyBlockOnAttackRange;
            ret.IsShowSkillGuide = this.IsShowSkillGuide;
            ret.SigleActionType = this.SigleActionType;
            ret.MoveActionName = this.MoveActionName;
            ret.ActionSpeedRate = this.ActionSpeedRate;
            ret.IsAddSkillBtnBlock = this.IsAddSkillBtnBlock;
            ret.ActionAudioName = this.ActionAudioName;
            ret.IsAudioLoop = this.IsAudioLoop;

            return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutS32(this.TotalTimeMS);
            output.PutS32(this.ShowChargeTimeMS);
            output.PutUTF(this.ActionName);
            output.PutUTF(this.ActionEffectFileName);
            output.PutBool(this.IsCycAction);
            output.PutBool(this.IsCancelable);
            output.PutBool(this.IsCancelableBySkill);
            output.PutBool(this.IsCancelBySkillNext);
            output.PutBool(this.IsNoneBlock);
            output.PutBool(this.IsNoneTouch);
            output.PutBool(this.IsFaceToTarget);
            output.PutBool(this.IsInvisible);
            output.PutBool(this.IsControlMoveable);
            output.PutBool(this.IsControlFaceable);
            output.PutBool(this.IsChangeIcon);
            output.PutUTF(this.IconName);

            output.PutExt(this.OverrideAttackShape);
            output.PutList<KeyFrame>(this.KeyFrames, output.PutExt);

            output.PutExt(this.BodyHit);
            output.PutF32(this.BodyHitSize);
            output.PutBool(this.BodyHitNextAction);

            output.PutBool(this.IsMoveToTarget);
            output.PutF32(this.MoveToTargetSpeedSEC);
            output.PutBool(this.IsJumpToTarget);
            output.PutF32(this.JumpToTargetHeightZ);

            output.PutBool(this.BodyBlockOnAttackRange);
            output.PutBool(this.IsShowSkillGuide);
            output.PutEnum8(this.SigleActionType);
            output.PutUTF(this.MoveActionName);
            output.PutF32(this.ActionSpeedRate);
            output.PutBool(this.IsAddSkillBtnBlock);
            output.PutUTF(this.ActionAudioName);
            output.PutBool(this.IsAudioLoop);
        }

        public void ReadExternal(IInputStream input)
        {
            this.TotalTimeMS = input.GetS32();
            this.ShowChargeTimeMS = input.GetS32();
            this.ActionName = input.GetUTF();
            this.ActionEffectFileName = input.GetUTF();
            this.IsCycAction = input.GetBool();
            this.IsCancelable = input.GetBool();
            this.IsCancelableBySkill = input.GetBool();
            this.IsCancelBySkillNext = input.GetBool();
            this.IsNoneBlock = input.GetBool();
            this.IsNoneTouch = input.GetBool();
            this.IsFaceToTarget = input.GetBool();
            this.IsInvisible = input.GetBool();
            this.IsControlMoveable = input.GetBool();
            this.IsControlFaceable = input.GetBool();
            this.IsChangeIcon = input.GetBool();
            this.IconName = input.GetUTF();

            this.OverrideAttackShape = input.GetExt<AttackShape>();
            this.KeyFrames = input.GetList<KeyFrame>(input.GetExt<KeyFrame>);

            this.BodyHit = input.GetExt<AttackProp>();
            this.BodyHitSize = input.GetF32();
            this.BodyHitNextAction = input.GetBool();

            this.IsMoveToTarget = input.GetBool();
            this.MoveToTargetSpeedSEC = input.GetF32();
            this.IsJumpToTarget = input.GetBool();
            this.JumpToTargetHeightZ = input.GetF32();

            this.BodyBlockOnAttackRange = input.GetBool();
            this.IsShowSkillGuide = input.GetBool();
            this.SigleActionType = input.GetEnum8<ActionEnum>();
            this.MoveActionName = input.GetUTF();
            this.ActionSpeedRate = input.GetF32();
            this.IsAddSkillBtnBlock = input.GetBool();
            this.ActionAudioName = input.GetUTF();
            this.IsAudioLoop = input.GetBool();
        }
        //------------------------------------------------------------
        /// <summary>
        /// 动作状态数据
        /// </summary>
        [MessageType(0x4107)]
        [DescAttribute("动作关键帧数据")]
        [Expandable]
        public class StatusChange : ICloneable, IExternalizable
        {
            [DescAttribute("移动可取消动作", "状态")]
            public bool IsCancelable = false;
            [DescAttribute("技能可取消动作", "状态")]
            public bool IsCancelableBySkill = false;

            [DescAttribute("是否进入霸体状态,不会被打断", "状态")]
            public bool IsNoneBlock = false;
            [DescAttribute("动作期间是否无碰撞", "状态")]
            public bool IsNoneTouch = false;
            [DescAttribute("动作中是否面向目标", "状态")]
            public bool IsFaceToTarget = false;

            [DescAttribute("动作是否隐身", "状态")]
            public bool IsInvisible = false;
            [DescAttribute("动作中是否可以控制移动", "状态")]

            public bool IsControlMoveable = false;
            [DescAttribute("动作中可以控制转向", "状态")]
            public bool IsControlFaceable = false;

            public override string ToString()
            {
                return string.Format("{0}{1}{2}{3}{4}{5}{6}{7}",
                    IsCancelable ? "移动可取消," : "",
                    IsCancelableBySkill ? "技能可取消," : "",
                    IsNoneBlock ? "霸体," : "",
                    IsNoneTouch ? "无碰撞," : "",
                    IsFaceToTarget ? "面向目标," : "",
                    IsInvisible ? "隐身," : "",
                    IsControlMoveable ? "可控制移动," : "",
                    IsControlFaceable ? "可控制转向," : "");
            }

            public object Clone()
            {
                StatusChange ret = new StatusChange();
                ret.IsCancelable = this.IsCancelable;
                ret.IsCancelableBySkill = this.IsCancelableBySkill;
                ret.IsNoneBlock = this.IsNoneBlock;
                ret.IsNoneTouch = this.IsNoneTouch;
                ret.IsFaceToTarget = this.IsFaceToTarget;
                ret.IsInvisible = this.IsInvisible;
                ret.IsControlMoveable = this.IsControlMoveable;
                ret.IsControlFaceable = this.IsControlFaceable;
                return ret;
            }
            public void WriteExternal(IOutputStream output)
            {
                output.PutBool(this.IsCancelable);
                output.PutBool(this.IsCancelableBySkill);
                output.PutBool(this.IsNoneBlock);
                output.PutBool(this.IsNoneTouch);
                output.PutBool(this.IsFaceToTarget);
                output.PutBool(this.IsInvisible);
                output.PutBool(this.IsControlMoveable);
                output.PutBool(this.IsControlFaceable);
            }

            public void ReadExternal(IInputStream input)
            {
                this.IsCancelable = input.GetBool();
                this.IsCancelableBySkill = input.GetBool();
                this.IsNoneBlock = input.GetBool();
                this.IsNoneTouch = input.GetBool();
                this.IsFaceToTarget = input.GetBool();
                this.IsInvisible = input.GetBool();
                this.IsControlMoveable = input.GetBool();
                this.IsControlFaceable = input.GetBool();
            }
        }

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

        [MessageType(0x4108)]
        [DescAttribute("动作攻击范围")]
        [Expandable]
        public class AttackShape : ICloneable, IExternalizable
        {
            public enum Shape : byte
            {
                [DescAttribute("单体")]
                Single = CommonAI.Zone.Helper.AttackShape.Single,
                [DescAttribute("圆形")]
                Round = CommonAI.Zone.Helper.AttackShape.Round,
                [DescAttribute("扇形")]
                Fan = CommonAI.Zone.Helper.AttackShape.Fan,

                [DescAttribute("胶囊条状")]
                Strip = CommonAI.Zone.Helper.AttackShape.Strip,
                [DescAttribute("胶囊射线(以原点出去)")]
                StripRay = CommonAI.Zone.Helper.AttackShape.StripRay,
                [DescAttribute("胶囊射线,接触到最近")]
                StripRayTouchEnd = CommonAI.Zone.Helper.AttackShape.StripRayTouchEnd,

                [DescAttribute("方形条状")]
                RectStrip = CommonAI.Zone.Helper.AttackShape.RectStrip,
                [DescAttribute("方形射线(以原点出去)")]
                RectStripRay = CommonAI.Zone.Helper.AttackShape.RectStripRay,

                [DescAttribute("横向胶囊条状")]
                WideStrip = CommonAI.Zone.Helper.AttackShape.WideStrip,

                [DescAttribute("圆环,中间是空的")]
                Circle = CommonAI.Zone.Helper.AttackShape.Circle,
            }
            [DescAttribute("攻击范围类型", "攻击范围")]
            public Shape AShape = Shape.Round;

            [DescAttribute("半径(Round, Fan, Circle)长度(Strip, StripRay,StripRayTouchEnd, RectStrip, RectStripRay, LineToTarget, WideStrip)", "攻击范围")]
            public float AttackRange = 1;

            [DescAttribute("角度(Fan)", "攻击范围")]
            [DependOnProperty("IsShapeFan")]
            public float AttackAngle = 1;

            [DescAttribute("宽度,粗度(Strip, StripRay,StripRayTouchEnd,RectStrip, RectStripRay, WideStrip),环粗度(Circle)", "攻击范围")]
            [DependOnProperty("IsShapeStrip")]
            public float StripWide = 1;

            [DescAttribute("初始点偏移", "身体")]
            public float OffsetRadius = 0;
            public bool IsSingle { get { return AShape == Shape.Single; } }
            public bool IsShapeFan { get { return AShape == Shape.Fan; } }
            public bool IsShapeStrip
            {
                get
                {
                    switch (AShape)
                    {
                        case Shape.Strip:
                        case Shape.StripRay:
                        case Shape.StripRayTouchEnd:
                        case Shape.Circle:
                        case Shape.RectStrip:
                        case Shape.RectStripRay:
                        case Shape.WideStrip:
                            return true;
                    }
                    return false;
                }
            }

            public override string ToString()
            {
                return string.Format("重定向攻击范围:{0}", AShape);
            }

            public object Clone()
            {
                AttackShape ret = new AttackShape();
                ret.AShape = this.AShape;
                ret.AttackRange = this.AttackRange;
                ret.AttackAngle = this.AttackAngle;
                ret.StripWide = this.StripWide;
                ret.OffsetRadius = this.OffsetRadius;
                return ret;
            }
            public void WriteExternal(IOutputStream output)
            {
                output.PutEnum8(this.AShape);
                output.PutF32(this.AttackRange);
                output.PutF32(this.AttackAngle);
                output.PutF32(this.StripWide);
                output.PutF32(this.OffsetRadius);
            }

            public void ReadExternal(IInputStream input)
            {
                this.AShape = input.GetEnum8<Shape>();
                this.AttackRange = input.GetF32();
                this.AttackAngle = input.GetF32();
                this.StripWide = input.GetF32();
                this.OffsetRadius = input.GetF32();
            }

        }
        //-------------------------------------------------------------------------------------

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

        [MessageType(0x4109)]
        [DescAttribute("动作攻击范围")]
        [Expandable]
        public class ThrowTarget : ICloneable, IExternalizable
        {
            public override string ToString()
            {
                return string.Format("重定向攻击范");
            }

            public object Clone()
            {
                ThrowTarget ret = new ThrowTarget();
                return ret;
            }
            public void WriteExternal(IOutputStream output)
            {
            }

            public void ReadExternal(IInputStream input)
            {
            }

        }
        //-------------------------------------------------------------------------------------


        //------------------------------------------------------------
        /// <summary>
        /// 动作关键帧数据
        /// </summary>
        [MessageType(0x4104)]
        [DescAttribute("动作关键帧数据")]
        [Expandable]
        public class KeyFrame : BaseKeyFrame, ICloneable, IExternalizable
        {
            /// <summary>
            /// 关键帧产生的法术或远程道具单位模板ID
            /// </summary>
            [DescAttribute("关键帧产生的法术或远程道具单位模板ID")]
            public LaunchSpell Spell;

            /// <summary>
            /// 当前关键帧特效
            /// </summary>
            [DescAttribute("当前关键帧特效ID")]
            public LaunchEffect Effect;

            /// <summary>
            /// 攻击属性
            /// </summary>
            [DescAttribute("攻击属性ID")]
            public AttackProp Attack;

            /// <summary>
            /// 对自己产生BUFF
            /// </summary>
            [DescAttribute("对自己产生BUFF")]
            public LaunchBuff SelfBuff;

            /// <summary>
            /// 此动作关键帧产生的位移
            /// </summary>
            [DescAttribute("此动作关键帧产生的位移")]
            public StartMove Move;
            [DescAttribute("位移在有打击目标时是否生效")]
            public bool hitTargetMoveEnable = true;

            [DescAttribute("闪现位移")]
            public BlinkMove Blink;

            [DescAttribute("动作状态改变")]
            public StatusChange ChangeStatus;

            public KeyFrame() { }
            public override string ToString()
            {
                return "Frame: @" + FrameMS;
            }

            public object Clone()
            {
                KeyFrame ret = new KeyFrame();
                ret.FrameMS = base.FrameMS;
                ret.Spell = CUtils.TryClone<LaunchSpell>(this.Spell);
                ret.Effect = CUtils.TryClone<LaunchEffect>(this.Effect);
                ret.Attack = CUtils.TryClone<AttackProp>(this.Attack);
                ret.SelfBuff = CUtils.TryClone<LaunchBuff>(this.SelfBuff);
                ret.Move = CUtils.TryClone<StartMove>(this.Move);
                ret.hitTargetMoveEnable = this.hitTargetMoveEnable;
                ret.Blink = CUtils.TryClone<BlinkMove>(this.Blink);
                ret.ChangeStatus = CUtils.TryClone<StatusChange>(this.ChangeStatus);
                return ret;
            }


            public void WriteExternal(IOutputStream output)
            {
                output.PutS32(base.FrameMS);
                output.PutExt(this.Spell);
                output.PutExt(this.Effect);
                output.PutExt(this.Attack);
                output.PutExt(this.SelfBuff);
                output.PutExt(this.Move);
                output.PutBool(this.hitTargetMoveEnable);
                output.PutExt(this.Blink);
                output.PutExt(this.ChangeStatus);
            }

            public void ReadExternal(IInputStream input)
            {
                base.FrameMS = input.GetS32();
                this.Spell = input.GetExt<LaunchSpell>();
                this.Effect = input.GetExt<LaunchEffect>();
                this.Attack = input.GetExt<AttackProp>();
                this.SelfBuff = input.GetExt<LaunchBuff>();
                this.Move = input.GetExt<StartMove>();
                this.hitTargetMoveEnable = input.GetBool();
                this.Blink = input.GetExt<BlinkMove>();
                this.ChangeStatus = input.GetExt<StatusChange>();
            }
        }

    }
    //---------------------------------------------------------------------------------//

    /// <summary>
    /// 单位行为触发器
    /// </summary>
    [MessageType(0x4010)]
    [DescAttribute("单位行为触发器")]
    [TableClassAttribute("ID")]
    public class UnitTriggerTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        [DescAttribute("ID", "基础", false)]
        public int ID;
        [LocalizationTextAttribute]
        [DescAttribute("触发器名字", "基础")]
        public string Name;

        [DescAttribute("触发器冷却时间", "基础")]
        public int CoolDownTimeMS = 10000;

        [DescAttribute("事件触发条件(所有的关系为与)", "条件")]
        [ListAttribute(typeof(UnitTriggers.BaseTriggerEvent))]
        public List<UnitTriggers.BaseTriggerEvent> Triggers = new List<UnitTriggers.BaseTriggerEvent>();

        [DescAttribute("释放技能", "动作")]
        public LaunchSkill DoSkill;
        [DescAttribute("产生Buff", "动作")]
        public LaunchBuff DoBuff;
        [DescAttribute("产生法术", "动作")]
        public LaunchSpell DoSpell;
        [DescAttribute("产生特效(图形)", "动作")]
        public LaunchEffect DoEffect;

		/// <summary>
		/// 召唤小弟
		/// </summary>
		[DescAttribute("召唤小弟", "动作")]
		public SummonUnit DoSummon;

		//------------------------------------
		/// <summary>
		/// 用户自定义扩展属性
		/// </summary>
		[DescAttribute("扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public IUnitTriggerProperties Properties = TemplateManager.Factory.CreateTriggerProperties();

        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }

        public string FunctionText()
        {
            StringBuilder sb = new StringBuilder();
            if (Triggers.Count > 0)
            {
                foreach (UnitTriggers.BaseTriggerEvent e in Triggers)
                {
                    sb.Append(string.Format("{0}", e));
                    if (e != Triggers[Triggers.Count - 1])
                    {
                        sb.Append(" 并且 ");
                    }
                }
                if (DoSkill != null) sb.Append("," + DoSkill);
                if (DoBuff != null) sb.Append("," + DoBuff);
                if (DoSpell != null) sb.Append("," + DoSpell);
                if (DoEffect != null) sb.Append("," + DoEffect);
            }
            return sb.ToString();
        }

        public object Clone()
        {
            UnitTriggerTemplate ret = new UnitTriggerTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.CoolDownTimeMS = this.CoolDownTimeMS;
            ret.Triggers = CUtils.CloneList<UnitTriggers.BaseTriggerEvent>(this.Triggers);
            ret.DoSkill = CUtils.TryClone<LaunchSkill>(this.DoSkill);
            ret.DoBuff = CUtils.TryClone<LaunchBuff>(this.DoBuff);
            ret.DoSpell = CUtils.TryClone<LaunchSpell>(this.DoSpell);
            ret.DoEffect = CUtils.TryClone<LaunchEffect>(this.DoEffect);
			ret.DoSummon = CUtils.TryClone<SummonUnit>(this.DoSummon);
			return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutS32(this.CoolDownTimeMS);
            output.PutList<UnitTriggers.BaseTriggerEvent>(this.Triggers, output.PutExt);
            output.PutExt(this.DoSkill);
            output.PutExt(this.DoBuff);
            output.PutExt(this.DoSpell);
            output.PutExt(this.DoEffect);
			output.PutExt(this.DoSummon);
		}
        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.CoolDownTimeMS = input.GetS32();
            this.Triggers = input.GetListAny<UnitTriggers.BaseTriggerEvent>();
            this.DoSkill = input.GetExt<LaunchSkill>();
            this.DoBuff = input.GetExt<LaunchBuff>();
            this.DoSpell = input.GetExt<LaunchSpell>();
            this.DoEffect = input.GetExt<LaunchEffect>();
			this.DoSummon = input.GetExt<SummonUnit>();
		}
    }


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


    /// <summary>
    /// 法术/飞行道具模板
    /// </summary>
    [MessageType(0x4005)]
    [DescAttribute("法术/飞行道具模板")]
    [TableClassAttribute("ID")]
    public class SpellTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        public enum MotionType : byte
        {
            [DescAttribute("在原地不动")]
            Immovability = 1,
            [DescAttribute("按直线运动")]
            Straight = 2,
            [DescAttribute("跟随目标,直到击中")]
            Missile = 3,

            [DescAttribute("向周围扩散")]
            AOE = 4,

            [DescAttribute("绑定发射者")]
            Binding = 6,
            [DescAttribute("绑定被攻击者")]
            BindingTarget = 7,

            [DescAttribute("炮弹类")]
            Cannon = 8,
            [DescAttribute("直接在目标坐标生效")]
            SelectTarget = 9,

            [DescAttribute("先按直线运动,过程中锁定目标")]
            SeekerMissile = 10,

            [DescAttribute("向周围扩散,绑定发射者")]
            AOE_Binding = 11,
            [DescAttribute("向周围扩散,绑定被攻击者")]
            AOE_BindingTarget = 12,

            [DescAttribute("锁定并命中目标,和SeekerMissile区别是没有过程,直接命中。")]
            SeekerSelectTarget = 13,

            [DescAttribute("发射者和目标绑定(Distance必须在范围内)")]
            Chain = 14,

            [DescAttribute("直接在自身坐标生效")]
            SelectLauncher = 15,

            [DescAttribute("回旋镖路径1(最终返回到发射者)")]
            Boomerang1 = 16,

            [DescAttribute("回旋镖路径2(最终返回到发射点)")]
            Boomerang2 = 17,

            [DescAttribute("狐火路径")]
            Foxfire = 18,

            [DescAttribute("先直线,后停留")]
            StraightAndStop = 19,


			[DescAttribute("追踪,可击中路径上的敌人")]
			MissileAttackRoute = 20,

			[DescAttribute("曲线追踪")]
			CurveMissile = 21,
		}
        /// <summary>
        /// 往客户端推送,是否包含坐标
        /// </summary>
        public bool IsLaunchSpellEventSyncPos
        {
            get
            {
				// 脱离运动类型的一些特殊字段
				if(this.IsBindingOrbit)
				{
					return true;
				}

                switch (MType)
                {
                    case SpellTemplate.MotionType.AOE_Binding:
                    case SpellTemplate.MotionType.AOE_BindingTarget:
                    case SpellTemplate.MotionType.Binding:
                    case SpellTemplate.MotionType.BindingTarget:
                    case SpellTemplate.MotionType.Chain:
                        return false;
                    case SpellTemplate.MotionType.SelectLauncher:
                    case SpellTemplate.MotionType.SelectTarget:
                    case SpellTemplate.MotionType.Immovability:
                    case SpellTemplate.MotionType.Cannon:
                    case SpellTemplate.MotionType.AOE:
                    case SpellTemplate.MotionType.Straight:
                    case SpellTemplate.MotionType.Missile:
                    case SpellTemplate.MotionType.Boomerang1:
                    case SpellTemplate.MotionType.Boomerang2:
                    case SpellTemplate.MotionType.SeekerMissile:
                    case SpellTemplate.MotionType.SeekerSelectTarget:
                    case SpellTemplate.MotionType.StraightAndStop:
					case SpellTemplate.MotionType.MissileAttackRoute:
					case SpellTemplate.MotionType.CurveMissile:
					default:
                        return true;
                }
            }
        }

        [DescAttribute("ID", "", false)]
        public int ID;

        /// <summary>
        /// 法术名字
        /// </summary>
        [DescAttribute("法术名字")]
        [LocalizationTextAttribute]
        public string Name;

        /// <summary>
        /// 生命周期,帧
        /// </summary>
        [DescAttribute("生命周期(毫秒)")]
        public int LifeTimeMS = 1000;

        [DescAttribute("客户端是否可见")]
        public bool ClientVisible = true;
        //--------------------------------------------
        public enum Shape : byte
        {
            [DescAttribute("圆形(BodySize=半径尺寸)")]
            Round = AttackShape.Round,
            [DescAttribute("扇形(BodySize=半径尺寸;FanAngle=角度)")]
            Fan = AttackShape.Fan,

            [DescAttribute("胶囊条状(Distance=长度;RectWide=宽度)")]
            Strip = AttackShape.Strip,
            [DescAttribute("胶囊射线,以原点出去(Distance=长度;RectWide=宽度)")]
            StripRay = AttackShape.StripRay,
            [DescAttribute("胶囊射线,接触到最近(Distance=最大长度;RectWide=宽度)")]
            StripRayTouchEnd = AttackShape.StripRayTouchEnd,

            [DescAttribute("方形条状")]
            RectStrip = CommonAI.Zone.Helper.AttackShape.RectStrip,
            [DescAttribute("方形射线(以原点出去)")]
            RectStripRay = CommonAI.Zone.Helper.AttackShape.RectStripRay,

            [DescAttribute("横向胶囊条状")]
            WideStrip = CommonAI.Zone.Helper.AttackShape.WideStrip,

            [DescAttribute("连线类型(单体攻击),比如激光塔(Distance=判定距离)")]
            LineToTarget = AttackShape.LineToTarget,
            [DescAttribute("连线类型(单体攻击),比如伸出去的钩子(Distance=判定距离)")]
            LineToStart = AttackShape.LineToStart,
            [DescAttribute("圆环,中间是空的(BodySize=外环半径;BodySize-RectWide=内环半径)")]
            Circle = CommonAI.Zone.Helper.AttackShape.Circle,
        }
        public bool IsShapeFan { get { return BodyShape == Shape.Fan; } }
        public bool IsShapeStrip
        {
            get
            {
                switch (BodyShape)
                {
                    case Shape.Strip:
                    case Shape.StripRay:
                    case Shape.StripRayTouchEnd:
                    case Shape.Circle:
                    case Shape.RectStrip:
                    case Shape.RectStripRay:
                    case Shape.WideStrip:
                        return true;
                }
                return false;
            }
        }
        public bool IsShapeDistance
        {
            get
            {
                switch (BodyShape)
                {
                    case Shape.Strip:
                    case Shape.StripRay:
                    case Shape.StripRayTouchEnd:
                    case Shape.LineToTarget:
                    case Shape.LineToStart:
                    case Shape.RectStrip:
                    case Shape.RectStripRay:
                    case Shape.WideStrip:
                        return true;
                    default:
                        return false;
                }
            }
        }
        //--------------------------------------------
        [DescAttribute("攻击范围类型", "攻击范围")]
        public Shape BodyShape = Shape.Round;
        [DescAttribute("尺寸,半径(Round, Fan, Circle)", "攻击范围")]
        public float BodySize = 1;
        [DescAttribute("角度(Fan)", "攻击范围")]
        [DependOnProperty("IsShapeFan")]
        public float FanAngle;
        [DescAttribute("宽度,粗度(Strip, StripRay,StripRayTouchEnd,RectStrip, RectStripRay,Circle, WideStrip)", "攻击范围")]
        [DependOnProperty("IsShapeStrip")]
        public float RectWide = 1;
        [DescAttribute("长度(Strip, StripRay,StripRayTouchEnd, RectStrip, RectStripRay,LineToTarget, LineToStart, WideStrip)", "攻击范围")]
        [DependOnProperty("IsShapeDistance")]
        public float Distance = 10;
        //--------------------------------------------
        [DescAttribute("法术自身高度", "攻击范围")]
        public float BodyHeight = 0;
        //--------------------------------------------
        /// <summary>
        /// 运动类型
        /// </summary>
        [DescAttribute("运动类型", "运动")]
        public MotionType MType = MotionType.Straight;
        public bool IsMoveable
        {
            get
            {
                switch (MType)
                {
                    case MotionType.Cannon:
                    case MotionType.Missile:
                    case MotionType.SeekerMissile:
                    case MotionType.Straight:
                    case MotionType.AOE:
                    case MotionType.AOE_Binding:
                    case MotionType.AOE_BindingTarget:
                    case MotionType.Boomerang1:
                    case MotionType.Boomerang2:
                    case MotionType.Foxfire:
                    case MotionType.StraightAndStop:
					case MotionType.MissileAttackRoute:
					case MotionType.CurveMissile:
						return true;
                }
                return false;
            }
        }
        public bool IsBinding
        {
            get
            {
                switch (MType)
                {
                    case MotionType.Binding:
                    case MotionType.BindingTarget:
                    case MotionType.AOE_Binding:
                    case MotionType.AOE_BindingTarget:
                        return true;
                }
                return false;
            }
        }
        public bool IsSeekingTarget
        {
            get
            {
                switch (MType)
                {
                    case MotionType.SeekerMissile:
                    case MotionType.SeekerSelectTarget:
                    case MotionType.Foxfire:
					case MotionType.MissileAttackRoute:
					case MotionType.CurveMissile:
						return true;
                }
                return false;
            }
        }

        public bool IsAOE
        {
            get
            {
                switch (MType)
                {
                    case MotionType.AOE:
                    case MotionType.AOE_Binding:
                    case MotionType.AOE_BindingTarget:
                        return true;
                }
                return false;
            }
        }


        /// <summary>
        /// 运动速度,AOE或者扩散类,缩放速度
        /// </summary>
        [DescAttribute("运动速度,AOE或者扩散类,缩放速度(距离 / 每秒)", "运动")]
        [DependOnProperty("IsMoveable")]
        public float MSpeedSEC = 15f;
        [DescAttribute("加速度(距离/每秒)(最终速度=速度+加速度)", "运动")]
        [DependOnProperty("IsMoveable")]
        public float MSpeedAdd = 0f;
        [DescAttribute("阻力(每秒递减速度百分比)", "运动")]
        [DependOnProperty("IsMoveable")]
        public float MSpeedAcc = 0f;

        [DescAttribute("最大移动距离", "运动")]
        public float MaxMoveDistance = 0f;
        [DescAttribute("最大限速", "运动")]
        [DependOnProperty("IsMoveable")]
        public float MSpeed_MAX = 100f;
        [DescAttribute("最小限速", "运动")]
        [DependOnProperty("IsMoveable")]
        public float MSpeed_MIN = -100f;

        //--------------------------------------------
        public enum AoeMotionType : byte
        {
            [DescAttribute("线性,递增或递减")]
            Linear = 0,
            [DescAttribute("正弦,单次周期为PI,象限0~1")]
            Sine = 1,
        }
        [DescAttribute("AOE方式", "运动")]
        [DependOnProperty("IsAOE")]
        public AoeMotionType AOEMType = AoeMotionType.Linear;
        //--------------------------------------------
        /// <summary>
        /// 转动速度
        /// </summary>
        [DescAttribute("自转转动速度(距离/每秒)", "运动")]
        public float RotateSpeedSEC;
        [DescAttribute("抛物线高度", "运动")]
        public float ParabolaHeight = 0;

        /// <summary>
        /// 
        /// </summary>
        [DescAttribute("暂时只给灵魂出窍的技能使用", "运动")]
        public bool HeightUpdate;

        //--------------------------------------------
        public enum SeekingExpect : byte
        {
            [DescAttribute("搜索随机单位")]
            Random,
            [DescAttribute("搜索最近单位")]
            Nearest,
            [DescAttribute("搜索最远单位")]
            Farthest,
            [DescAttribute("搜索随机单位(忽略链中)")]
            RandomIgnoreInChain,
            [DescAttribute("搜索最近单位(忽略链中)")]
            NearestIgnoreInChain,
            [DescAttribute("搜索最远单位(忽略链中)")]
            FarthestIgnoreInChain,
        }
        //--------------------------------------------

        [DescAttribute("如果是自动搜敌导弹SeekerMissile,则表示搜索范围,如果是LineToTarget则表示伤害距离", "运动-自动跟踪")]
        [DependOnProperty("IsSeekingTarget")]
        public float SeekingRange = 10;
        [DescAttribute("自动锁敌冷却时间(毫秒)", "运动-自动跟踪")]
        [DependOnProperty("IsSeekingTarget")]
        public int SeekingCooldownMS = 1000;
        [DescAttribute("自动锁敌运动时,转角距离", "运动-自动跟踪")]
        [DependOnProperty("IsSeekingTarget")]
        public float SeekingTurningAngleSEC = 3f;
        [DescAttribute("自动锁敌检测方式", "运动-自动跟踪")]
        [DependOnProperty("IsSeekingTarget")]
        public SeekingExpect SeekingExpectTarget = SeekingExpect.Random;

        //--------------------------------------------
        [DescAttribute("如果是绑定则围绕绑定者公转", "运动-绑定")]
        [DependOnProperty("IsBinding")]
        public bool IsBindingOrbit = false;
        [DescAttribute("绑定公转距离", "运动-绑定")]
        [DependOnProperty("IsBinding")]
        public float OrbitDistance;
        [DescAttribute("如果为绑定,是否绑定方向", "运动-绑定")]
        [DependOnProperty("IsBinding")]
        public bool IsBindingDirection = false;
        //--------------------------------------------
        //--------------------------------------------

        [DescAttribute("最大影响(受击)单位,0表示无限制")]
        public int MaxAffectUnit = 0;
        //--------------------------------------------
        /// <summary>
        /// 击中就消失
        /// </summary>
        [DescAttribute("击中就消失(爆炸),和HitIntervalMS冲突", "关键帧")]
        public bool HitOnExplosion;
        [DescAttribute("击中就消失(爆炸)关键帧,适用于打到就爆和Cannon类法术", "关键帧")]
        [DependOnProperty("HitOnExplosion")]
        public KeyFrame HitOnExplosionKeyFrame;

        /// <summary>
        /// 每间隔多少帧就触发一次,0代表只造成一次伤害或效果
        /// </summary>
        [DescAttribute("每间隔多少时间就触发一次,0 适用于穿透性,但只对单位造成一次伤害(毫秒)", "关键帧")]
        public int HitIntervalMS;
        [DescAttribute("只造成一次触发的关键帧 or 每隔一段时间触发的关键帧", "关键帧")]
        public KeyFrame HitIntervalKeyFrame;

        /// <summary>
        /// 按顺序触发的所有关键帧
        /// </summary>
        [ListAttribute(typeof(KeyFrame))]
        [DescAttribute("按顺序触发的所有关键帧", "关键帧")]
        public List<KeyFrame> KeyFrames = new List<KeyFrame>();
        //--------------------------------------------
        /// <summary>
        /// 模型名字或者Perfab名字
        /// </summary>
        [DescAttribute("模型名字或者Perfab名字", "资源")]
        [ResourceIDAttribute]
        public string FileName;
        [DescAttribute("模型名字或者Perfab名字(起始)", "资源")]
        [ResourceIDAttribute]
        public string FileNameSpawn;
        [DescAttribute("模型名字或者Perfab名字(结束)", "资源")]
        [ResourceIDAttribute]
        public string FileNameDestory;
        [DescAttribute("是否循环播放动画", "资源")]
        public bool IsCycAnim = true;

        [DescAttribute("缩放比率", "资源")]
        public float FileBodyScale = 1;


        [DescAttribute("法术释放时,在目标点释放特效,可作为技能提示", "特效")]
        public LaunchEffect TargetEffect;

        [DescAttribute("法术期望作用目标", "目标")]
        public SkillTemplate.CastTarget ExpectTarget = SkillTemplate.CastTarget.Enemy;
        //------------------------------------
        [DescAttribute("绑定目标不可操控时移除自己", "移除条件")]
        public bool RemoveOnBindingUncontrollable = false;
        [DescAttribute("绑定目标非技能状态时移除自己", "移除条件")]
        public bool RemoveOnBindingSkillOver = false;
        [DescAttribute("当Spell被移除停止释放它的技能", "移除条件")]
        public bool StopBindingSkillOnRemoved = false;
        //------------------------------------

        //--------------------------------------------
        public enum SpecialAdditionalEffect : byte
        {
            [DescAttribute("无")]
            None = 0,
            [DescAttribute("黑洞效果,牵引碰到的怪物")]
            DarkHole = 1,
            [DescAttribute("路径牵引效果")]
            DragPath = 2,
        }

        public bool IsCanAddSpecialEffect
        {
            get
            {
                switch (MType)
                {
                    case MotionType.Missile:
                    case MotionType.SeekerMissile:
                    case MotionType.Cannon:
                    case MotionType.Chain:
                    case MotionType.Boomerang1:
                    case MotionType.Boomerang2:
					case MotionType.MissileAttackRoute:
					case MotionType.CurveMissile:
						return false;
                }
                return true;
            }
        }

        [DescAttribute("法术的特殊效果", "特殊效果")]
        [DependOnProperty("IsCanAddSpecialEffect")]
        public SpecialAdditionalEffect SpecialEffect = SpecialAdditionalEffect.None;
        [DescAttribute("特殊效果参数(黑洞的牵引速度)", "特殊效果")]
        [DependOnProperty("IsCanAddSpecialEffect")]
        public float SpecialEffectParam = 0;

        // -- 

        [DescAttribute("飞行时间(毫秒)", "运动(回旋路径, 滞空)")]
        //[DependOnProperty("IsBoomerang")]
        public int BoomerangFlyTime = 1000;
        [DescAttribute("滞空时间(毫秒)", "运动(回旋路径, 滞空)")]
        //[DependOnProperty("IsBoomerang")]
        public int BoomerangHangtime = 1000;

        public bool IsBoomerang
        {
            get
            {
                return MType == MotionType.Boomerang1 || MType == MotionType.Boomerang2;
            }
        }

        /// <summary>
        /// 用户自定义扩展属性
        /// </summary>
        [DescAttribute("扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public ISpellProperties Properties = TemplateManager.Factory.CreateSpellProperties();
        //------------------------------------

        [DescAttribute("速度(原始动画/特效速度 * 速度)", "特效加速")]
        public float EffectAddSpeed = 1;
        [DescAttribute("特效中存在的动画,动画的名字", "特效加速")]
        public string AnimtionName;

        [DescAttribute("音效文件名", "音效")]
        public string AudioName;
        [DescAttribute("音效文件是否循环", "音效")]
        public bool IsAudioLoop;

        public SpellTemplate() { }
        public int GetID()
        {
            return ID;
        }
        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }

        public object Clone()
        {
            SpellTemplate ret = new SpellTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.LifeTimeMS = this.LifeTimeMS;
            ret.ClientVisible = this.ClientVisible;
            ret.BodyShape = this.BodyShape;
            ret.BodySize = this.BodySize;
            ret.FanAngle = this.FanAngle;
            ret.RectWide = this.RectWide;
            ret.BodyHeight = this.BodyHeight;
            ret.Distance = this.Distance;

            ret.MType = this.MType;
            ret.MSpeedSEC = this.MSpeedSEC;
            ret.MSpeedAdd = this.MSpeedAdd;
            ret.MSpeedAcc = this.MSpeedAcc;
            ret.MaxMoveDistance = this.MaxMoveDistance;
            ret.MSpeed_MAX = this.MSpeed_MAX;
            ret.MSpeed_MIN = this.MSpeed_MIN;
            ret.RotateSpeedSEC = this.RotateSpeedSEC;
            ret.ParabolaHeight = this.ParabolaHeight;
            ret.HeightUpdate = this.HeightUpdate;
            ret.AOEMType = this.AOEMType;

            ret.SeekingRange = this.SeekingRange;
            ret.SeekingCooldownMS = this.SeekingCooldownMS;
            ret.SeekingTurningAngleSEC = this.SeekingTurningAngleSEC;
            ret.SeekingExpectTarget = this.SeekingExpectTarget;


            ret.MaxAffectUnit = this.MaxAffectUnit;
            ret.IsBindingOrbit = this.IsBindingOrbit;
            ret.OrbitDistance = this.OrbitDistance;
            ret.IsBindingDirection = this.IsBindingDirection;
            ret.HitOnExplosion = this.HitOnExplosion;
            ret.HitOnExplosionKeyFrame = CUtils.TryClone<KeyFrame>(this.HitOnExplosionKeyFrame);
            ret.HitIntervalMS = this.HitIntervalMS;
            ret.HitIntervalKeyFrame = CUtils.TryClone<KeyFrame>(this.HitIntervalKeyFrame);
            ret.KeyFrames = CUtils.CloneList<KeyFrame>(this.KeyFrames);
            ret.FileName = this.FileName;
            ret.FileNameSpawn = this.FileNameSpawn;
            ret.FileNameDestory = this.FileNameDestory;
            ret.IsCycAnim = this.IsCycAnim;
            ret.FileBodyScale = this.FileBodyScale;
            ret.TargetEffect = CUtils.TryClone<LaunchEffect>(this.TargetEffect);
            ret.ExpectTarget = this.ExpectTarget;
            ret.RemoveOnBindingUncontrollable = this.RemoveOnBindingUncontrollable;
            ret.RemoveOnBindingSkillOver = this.RemoveOnBindingSkillOver;
            ret.StopBindingSkillOnRemoved = this.StopBindingSkillOnRemoved;
            ret.Properties = CUtils.TryClone<ISpellProperties>(this.Properties);
            ret.SpecialEffect = this.SpecialEffect;
            ret.SpecialEffectParam = this.SpecialEffectParam;
            ret.BoomerangFlyTime = this.BoomerangFlyTime;
            ret.BoomerangHangtime = this.BoomerangHangtime;
            ret.EffectAddSpeed = this.EffectAddSpeed;
            ret.AnimtionName = this.AnimtionName;
            ret.AudioName = this.AudioName;
            ret.IsAudioLoop = this.IsAudioLoop;
            return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutS32(this.LifeTimeMS);
            output.PutBool(this.ClientVisible);
            output.PutEnum8(this.BodyShape);
            output.PutF32(this.BodySize);
            output.PutF32(this.FanAngle);
            output.PutF32(this.RectWide);
            output.PutF32(this.BodyHeight);
            output.PutF32(this.Distance);

            output.PutEnum8(this.MType);
            output.PutF32(this.MSpeedSEC);
            output.PutF32(this.MSpeedAdd);
            output.PutF32(this.MSpeedAcc);
            output.PutF32(this.MaxMoveDistance);
            output.PutF32(this.MSpeed_MAX);
            output.PutF32(this.MSpeed_MIN);
            output.PutF32(this.RotateSpeedSEC);
            output.PutF32(this.ParabolaHeight);
            output.PutBool(this.HeightUpdate);
            output.PutEnum8(this.AOEMType);

            output.PutF32(this.SeekingRange);
            output.PutS32(this.SeekingCooldownMS);
            output.PutF32(this.SeekingTurningAngleSEC);
            output.PutEnum8(this.SeekingExpectTarget);
            output.PutS32(this.MaxAffectUnit);
            output.PutBool(this.IsBindingOrbit);
            output.PutF32(this.OrbitDistance);
            output.PutBool(this.IsBindingDirection);
            output.PutBool(this.HitOnExplosion);
            output.PutExt(this.HitOnExplosionKeyFrame);
            output.PutS32(this.HitIntervalMS);
            output.PutExt(this.HitIntervalKeyFrame);
            output.PutList<KeyFrame>(this.KeyFrames, output.PutExt);
            output.PutUTF(this.FileName);
            output.PutUTF(this.FileNameSpawn);
            output.PutUTF(this.FileNameDestory);
            output.PutBool(this.IsCycAnim);
            output.PutF32(this.FileBodyScale);
            output.PutExt(this.TargetEffect);
            output.PutEnum8(this.ExpectTarget);
            output.PutBool(this.RemoveOnBindingUncontrollable);
            output.PutBool(this.RemoveOnBindingSkillOver);
            output.PutBool(this.StopBindingSkillOnRemoved);
            output.PutExt(this.Properties);
            output.PutEnum8(this.SpecialEffect);
            output.PutF32(this.SpecialEffectParam);
            output.PutS32(this.BoomerangFlyTime);
            output.PutS32(this.BoomerangHangtime);
            output.PutF32(this.EffectAddSpeed);
            output.PutUTF(this.AnimtionName);
            output.PutUTF(this.AudioName);
            output.PutBool(this.IsAudioLoop);
        }

        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.LifeTimeMS = input.GetS32();
            this.ClientVisible = input.GetBool();
            this.BodyShape = input.GetEnum8<Shape>();
            this.BodySize = input.GetF32();
            this.FanAngle = input.GetF32();
            this.RectWide = input.GetF32();
            this.BodyHeight = input.GetF32();
            this.Distance = input.GetF32();

            this.MType = input.GetEnum8<MotionType>();
            this.MSpeedSEC = input.GetF32();
            this.MSpeedAdd = input.GetF32();
            this.MSpeedAcc = input.GetF32();
            this.MaxMoveDistance = input.GetF32();
            this.MSpeed_MAX = input.GetF32();
            this.MSpeed_MIN = input.GetF32();
            this.RotateSpeedSEC = input.GetF32();
            this.ParabolaHeight = input.GetF32();
            this.HeightUpdate = input.GetBool();
            this.AOEMType = input.GetEnum8<AoeMotionType>();

            this.SeekingRange = input.GetF32();
            this.SeekingCooldownMS = input.GetS32();
            this.SeekingTurningAngleSEC = input.GetF32();
            this.SeekingExpectTarget = input.GetEnum8<SeekingExpect>();
            this.MaxAffectUnit = input.GetS32();
            this.IsBindingOrbit = input.GetBool();
            this.OrbitDistance = input.GetF32();
            this.IsBindingDirection = input.GetBool();
            this.HitOnExplosion = input.GetBool();
            this.HitOnExplosionKeyFrame = input.GetExt<KeyFrame>();
            this.HitIntervalMS = input.GetS32();
            this.HitIntervalKeyFrame = input.GetExt<KeyFrame>();
            this.KeyFrames = input.GetList<KeyFrame>(input.GetExt<KeyFrame>);
            this.FileName = input.GetUTF();
            this.FileNameSpawn = input.GetUTF();
            this.FileNameDestory = input.GetUTF();
            this.IsCycAnim = input.GetBool();
            this.FileBodyScale = input.GetF32();
            this.TargetEffect = input.GetExt<LaunchEffect>();
            this.ExpectTarget = input.GetEnum8<SkillTemplate.CastTarget>();
            this.RemoveOnBindingUncontrollable = input.GetBool();
            this.RemoveOnBindingSkillOver = input.GetBool();
            this.StopBindingSkillOnRemoved = input.GetBool();
            this.Properties = input.GetExt<ISpellProperties>(this.Properties);
            this.SpecialEffect = input.GetEnum8<SpecialAdditionalEffect>();
            this.SpecialEffectParam = input.GetF32();
            this.BoomerangFlyTime = input.GetS32();
            this.BoomerangHangtime = input.GetS32();
            this.EffectAddSpeed = input.GetF32();
            this.AnimtionName = input.GetUTF();
            this.AudioName = input.GetUTF();
            this.IsAudioLoop = input.GetBool();
        }
        //------------------------------------------------------------

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


        /// <summary>
        /// 法术伤害或特效关键帧
        /// </summary>
        [MessageType(0x4105)]
        [DescAttribute("法术伤害或特效关键帧")]
        [Expandable]
        public class KeyFrame : BaseKeyFrame, ICloneable, IExternalizable
        {
            /// <summary>
            /// 触发的特效
            /// </summary>
            [DescAttribute("触发的特效")]
            public LaunchEffect Effect;

            /// <summary>
            /// 触发新的法术
            /// </summary>
            [DescAttribute("触发新的法术")]
            public LaunchSpell Spell;

            /// <summary>
            /// 攻击伤害
            /// </summary>
            [DescAttribute("攻击伤害")]
            public AttackProp Attack;

            /// <summary>
            /// 召唤小弟
            /// </summary>
            [DescAttribute("召唤小弟")]
            public SummonUnit Summon;


            public KeyFrame() { }

            public override string ToString()
            {
                if (Spell == null)
                {
                    return "Frame: @" + FrameMS;
                }

                return "Frame: @" + FrameMS + ", " + Spell.ToString();
            }

            public object Clone()
            {
                KeyFrame ret = new KeyFrame();
                ret.FrameMS = this.FrameMS;
                ret.Spell = CUtils.TryClone<LaunchSpell>(this.Spell);
                ret.Effect = CUtils.TryClone<LaunchEffect>(this.Effect);
                ret.Attack = CUtils.TryClone<AttackProp>(this.Attack);
                ret.Summon = CUtils.TryClone<SummonUnit>(this.Summon);
                return ret;
            }


            public void WriteExternal(IOutputStream output)
            {
                output.PutS32(base.FrameMS);
                output.PutExt(this.Spell);
                output.PutExt(this.Effect);
                output.PutExt(this.Attack);
                output.PutExt(this.Summon);
            }

            public void ReadExternal(IInputStream input)
            {
                base.FrameMS = input.GetS32();
                this.Spell = input.GetExt<LaunchSpell>();
                this.Effect = input.GetExt<LaunchEffect>();
                this.Attack = input.GetExt<AttackProp>();
                this.Summon = input.GetExt<SummonUnit>();
            }
        }

    }

    //---------------------------------------------------------------------------------//
    /// <summary>
    /// BUFF类数据结构
    /// </summary>
    [MessageType(0x4006)]
    [DescAttribute("BUFF类数据结构")]
    [TableClassAttribute("ID")]
    public class BuffTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        [DescAttribute("ID", "", false)]
        public int ID;

        [LocalizationTextAttribute]
        [DescAttribute("Buff名字")]
        public string Name;
        [DescAttribute("技能图标", "资源")]
        public string IconName;

        [DescAttribute("需要同步客户端")]
        public bool ClientVisible = true;

        [DescAttribute("生命周期(毫秒)")]
        public int LifeTimeMS;

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

        [DescAttribute("每间隔多少毫秒就触发一次", "关键帧")]
        public int HitIntervalMS;

        [DescAttribute("第0帧是否有效", "关键帧")]
        public bool FirstTimeEnable = true;

        [DescAttribute("每间隔时间触发一次的时候起效", "关键帧")]
        public KeyFrame HitKeyFrame;

        [ListAttribute(typeof(KeyFrame))]
        [DescAttribute("所有关键帧", "关键帧")]
        public List<KeyFrame> KeyFrames = new List<KeyFrame>();

        [DescAttribute("Buff结束关键帧", "关键帧")]
        public KeyFrame EndKeyFrame;

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

        [DescAttribute("是否有害(Debuff),否则为有益(Buff)", "BUFF")]
        public bool IsHarmful = false;
        [DescAttribute("Buff是否可以主动取消", "BUFF")]
        public bool IsCancelBySelf = false;
        [DescAttribute("Buff是否允许多个实例", "BUFF")]
        public bool IsDuplicating = false;
        [DescAttribute("Buff施放者死亡立即移除", "BUFF")]
        public bool IsRemoveOnSenderRemoved = true;
		[DescAttribute("死亡立即移除", "BUFF")]
		public bool IsRemoveOnDead = false;

		//--------------------------------------------------------------
		[DescAttribute("BUFF期间,强制单位进入BUFF特殊动作(即不会被其他动作打断)", "状态")]
        public string LockStateAction;

        [DescAttribute("是否隐身", "状态")]
        public bool IsInvisible = false;
        [DescAttribute("是否无敌", "状态")]
        public bool IsInvincible = false;
        [DescAttribute("是否沉默(只能释放BaseSkill)", "状态")]
        public bool IsSilent = false;
        [DescAttribute("是否沉默允许移动", "状态")]
        public bool IsCanMove = true;
        [DescAttribute("产生眩晕", "状态")]
        public bool MakeStun = false;
		[DescAttribute("免疫控制", "状态")]
		public bool IgnoreControl = false;

		[DescAttribute("单位被动触发系,一直绑定关系", "状态")]
        public LaunchTrigger UnitTrigger;

        //--------------------------------------------------------------
        [DescAttribute("允许变身", "变身 - 造型")]
        public bool MakeAvatar = false;

        [DescAttribute("单位变身的模型文件名", "变身 - 造型")]
        [ResourceIDAttribute]
        [DependOnProperty("MakeAvatar")]
        public string UnitFileName;
        //--------------------------------------------------------------
        [DescAttribute("允许改变技能", "变身 - 技能")]
        public bool UnitChangeSkills = false;

        [DescAttribute("此单位变身普通攻击技能", "变身 - 技能")]
        [DependOnProperty("UnitChangeSkills")]
        public LaunchSkill UnitBaseSkillID;

        [ListAttribute(typeof(LaunchSkill))]
        [DescAttribute("此单位变身绑定的所有技能ID", "变身 - 技能")]
        [DependOnProperty("UnitChangeSkills")]
        public List<LaunchSkill> UnitSkills = new List<LaunchSkill>();

        [DescAttribute("变身改变技能时,保留的技能ID", "变身 - 技能")]
        [TemplatesIDAttribute(typeof(SkillTemplate))]
        [DependOnProperty("UnitChangeSkills")]
        public List<int> UnitKeepSkillsID = new List<int>();
        //--------------------------------------------------------------

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

        [DescAttribute("是否可堆叠层数", "堆叠")]
        public bool IsOverlay = false;
        [DescAttribute("最高可堆叠层数", "堆叠")]
        [DependOnProperty("IsOverlay")]
        public byte MaxOverlay = 1;

        [DescAttribute("是否为被动系,则不显示在面板")]
        public bool IsPassive = false;

        public enum BindingPlayType : byte
        {
            [DescAttribute("一次性全部播放,配置多少,播放多少")]
            All = 0,
            [DescAttribute("根据层数去播放,那一层播放那一个")]
            AppointOverLayer = 1,
            [DescAttribute("包含层数,如果层数是2,会同时播放1和2的特效")]
            IncludeOverLayer = 2,
        }

        [DescAttribute("BindingEffectList的播放模式(客户端用)", "特效")]
        public BindingPlayType PlayType = BindingPlayType.All;

        [DescAttribute("BUFF期间绑定特效(客户端用)", "特效")]
        public LaunchEffect BindingEffect;

        [ListAttribute(typeof(LaunchEffect))]
        [DescAttribute("BUFF期间绑定特效集合(客户端用)", "特效")]
        public List<LaunchEffect> BindingEffectList = new List<LaunchEffect>();

        [ListAttribute(typeof(LaunchEffect))]
        [DescAttribute("BUFF期间每层绑定特效(每层对应一个特效,客户端用)", "特效")]
        [DependOnProperty("IsOverlay")]
        public List<LaunchEffect> OverlayBindingEffect = new List<LaunchEffect>();
        //--------------------------------------------------------------

        [DescAttribute("互斥类型,同类型不能出现两个(非0有效)", "互斥")]
        public int ExclusiveCatgory;
        [DescAttribute("互斥优先级,如果优先级相等,则替换", "互斥")]
        public int ExclusivePriority;

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

        [DescAttribute("Buff用户自定义扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public IBuffProperties Properties;

        [DescAttribute("自定义字段", "属性", true)]
        public string[] Attributes;

		//内部使用的一些字段,
		[DescAttribute("强制加,跳过boss免控限制", "属性", true)]
		public bool forceAdd;

        public BuffTemplate()
        {
            this.Properties = TemplateManager.Factory.CreateBuffProperties();
        }
        public int GetID()
        {
            return ID;
        }
        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }

        public object Clone()
        {
            BuffTemplate ret = new BuffTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.IconName = this.IconName;
            ret.ClientVisible = this.ClientVisible;
            ret.LifeTimeMS = this.LifeTimeMS;
            ret.HitIntervalMS = this.HitIntervalMS;
            ret.FirstTimeEnable = this.FirstTimeEnable;
            ret.HitKeyFrame = CUtils.TryClone<KeyFrame>(this.HitKeyFrame);
            ret.KeyFrames = CUtils.CloneList<KeyFrame>(this.KeyFrames);

            ret.IsHarmful = this.IsHarmful;
            ret.IsCancelBySelf = this.IsCancelBySelf;
            ret.IsDuplicating = this.IsDuplicating;

            ret.EndKeyFrame = CUtils.TryClone<KeyFrame>(this.EndKeyFrame);
            ret.IsInvisible = this.IsInvisible;
            ret.IsInvincible = this.IsInvincible;
            ret.IsSilent = this.IsSilent;
            ret.IsCanMove = this.IsCanMove;
            ret.MakeStun = this.MakeStun;
			ret.IgnoreControl = this.IgnoreControl;
			ret.IsRemoveOnSenderRemoved = this.IsRemoveOnSenderRemoved;
			ret.IsRemoveOnDead = this.IsRemoveOnDead;

			ret.LockStateAction = this.LockStateAction;
            ret.UnitTrigger = CUtils.TryClone<LaunchTrigger>(this.UnitTrigger);

            ret.MakeAvatar = this.MakeAvatar;
            ret.UnitFileName = this.UnitFileName;
            ret.UnitChangeSkills = this.UnitChangeSkills;
            ret.UnitBaseSkillID = CUtils.TryClone<LaunchSkill>(this.UnitBaseSkillID);
            ret.UnitSkills = CUtils.CloneList<LaunchSkill>(this.UnitSkills);
            ret.UnitKeepSkillsID = new List<int>(this.UnitKeepSkillsID);

            ret.IsOverlay = this.IsOverlay;
            ret.MaxOverlay = this.MaxOverlay;
            ret.IsPassive = this.IsPassive;
            ret.BindingEffect = CUtils.TryClone<LaunchEffect>(this.BindingEffect);
            ret.PlayType = this.PlayType;
            ret.BindingEffectList = CUtils.CloneList<LaunchEffect>(this.BindingEffectList);
            ret.OverlayBindingEffect = CUtils.CloneList<LaunchEffect>(this.OverlayBindingEffect);

            ret.ExclusiveCatgory = this.ExclusiveCatgory;
            ret.ExclusivePriority = this.ExclusivePriority;

            ret.Properties = CUtils.TryClone<IBuffProperties>(this.Properties);
            ret.Attributes = CUtils.CloneArray<string>(this.Attributes);
			ret.forceAdd = this.forceAdd;

			return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutUTF(this.IconName);
            output.PutS32(this.LifeTimeMS);
            output.PutBool(this.ClientVisible);
            output.PutS32(this.HitIntervalMS);
            output.PutBool(this.FirstTimeEnable);
            output.PutExt(this.HitKeyFrame);
            output.PutList(this.KeyFrames, output.PutExt);
            output.PutExt(this.EndKeyFrame);

            output.PutBool(this.IsHarmful);
            output.PutBool(this.IsCancelBySelf);
            output.PutBool(this.IsDuplicating);

            output.PutBool(this.IsInvisible);
            output.PutBool(this.IsInvincible);
            output.PutBool(this.IsSilent);
            output.PutBool(this.IsCanMove);
            output.PutBool(this.MakeStun);
			output.PutBool(this.IgnoreControl);
            output.PutBool(this.IsRemoveOnSenderRemoved);
			output.PutBool(this.IsRemoveOnDead);

            output.PutUTF(this.LockStateAction);
            output.PutExt(this.UnitTrigger);

            output.PutBool(this.MakeAvatar);
            output.PutUTF(this.UnitFileName);
            output.PutBool(this.UnitChangeSkills);
            output.PutExt(this.UnitBaseSkillID);
            output.PutList(this.UnitSkills, output.PutExt);
            output.PutList(this.UnitKeepSkillsID, output.PutS32);

            output.PutBool(this.IsOverlay);
            output.PutU8(this.MaxOverlay);
            output.PutBool(this.IsPassive);
            output.PutExt(this.BindingEffect);
            output.PutEnum8(this.PlayType);
            output.PutList(this.BindingEffectList, output.PutExt);
            output.PutList(this.OverlayBindingEffect, output.PutExt);

            output.PutS32(this.ExclusiveCatgory);
            output.PutS32(this.ExclusivePriority);

            output.PutExt(this.Properties);
            output.PutArray(this.Attributes, output.PutUTF);
			output.PutBool(this.forceAdd);
        }
        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.IconName = input.GetUTF();
            this.LifeTimeMS = input.GetS32();
            this.ClientVisible = input.GetBool();
            this.HitIntervalMS = input.GetS32();
            this.FirstTimeEnable = input.GetBool();
            this.HitKeyFrame = input.GetExt<KeyFrame>();
            this.KeyFrames = input.GetList<KeyFrame>(input.GetExt<KeyFrame>);
            this.EndKeyFrame = input.GetExt<KeyFrame>();

            this.IsHarmful = input.GetBool();
            this.IsCancelBySelf = input.GetBool();
            this.IsDuplicating = input.GetBool();

            this.IsInvisible = input.GetBool();
            this.IsInvincible = input.GetBool();
            this.IsSilent = input.GetBool();
            this.IsCanMove = input.GetBool();
            this.MakeStun = input.GetBool();
			this.IgnoreControl = input.GetBool();
			this.IsRemoveOnSenderRemoved = input.GetBool();
			this.IsRemoveOnDead = input.GetBool();

			this.LockStateAction = input.GetUTF();
            this.UnitTrigger = input.GetExt<LaunchTrigger>();

            this.MakeAvatar = input.GetBool();
            this.UnitFileName = input.GetUTF();
            this.UnitChangeSkills = input.GetBool();
            this.UnitBaseSkillID = input.GetExt<LaunchSkill>();
            this.UnitSkills = input.GetList<LaunchSkill>(input.GetExt<LaunchSkill>);
            this.UnitKeepSkillsID = input.GetList<int>(input.GetS32);

            this.IsOverlay = input.GetBool();
            this.MaxOverlay = input.GetU8();
            this.IsPassive = input.GetBool();
            this.BindingEffect = input.GetExt<LaunchEffect>();
            this.PlayType = input.GetEnum8<BindingPlayType>();
            this.BindingEffectList = input.GetList<LaunchEffect>(input.GetExt<LaunchEffect>);
            this.OverlayBindingEffect = input.GetList<LaunchEffect>(input.GetExt<LaunchEffect>);

            this.ExclusiveCatgory = input.GetS32();
            this.ExclusivePriority = input.GetS32();

            this.Properties = input.GetExt<IBuffProperties>(this.Properties);
            this.Attributes = input.GetUTFArray();
			this.forceAdd = input.GetBool();
        }
        //--------------------------------------------


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

        /// <summary>
        /// BUFF伤害或特效关键帧
        /// </summary>
        [MessageType(0x4106)]
        [DescAttribute("BUFF伤害或特效关键帧")]
        [Expandable]
        public class KeyFrame : BaseKeyFrame, ICloneable, IExternalizable
        {
            /// <summary>
            /// 触发的特效
            /// </summary>
            [DescAttribute("触发的特效")]
            public LaunchEffect Effect;

            /// <summary>
            /// 触发新的法术
            /// </summary>
            [DescAttribute("触发新的法术")]
            public LaunchSpell Spell;

            /// <summary>
            /// 攻击伤害
            /// </summary>
            [DescAttribute("攻击伤害")]
            public AttackProp Attack;

            /// <summary>
            /// 直接使用道具
            /// </summary>
            [DescAttribute("直接使用道具")]
            public UseItem Item;

			/// <summary>
			/// 直接使用道具
			/// </summary>
			[DescAttribute("场景添加道具")]
			public BuffAddItem addItem;

			public KeyFrame() { }

            public override string ToString()
            {
                return "Frame: @" + FrameMS;
            }


            public object Clone()
            {
                KeyFrame ret = new KeyFrame();
                ret.FrameMS = this.FrameMS;
                ret.Spell = CUtils.TryClone<LaunchSpell>(this.Spell);
                ret.Effect = CUtils.TryClone<LaunchEffect>(this.Effect);
                ret.Attack = CUtils.TryClone<AttackProp>(this.Attack);
                ret.Item = CUtils.TryClone<UseItem>(this.Item);
				ret.addItem = CUtils.TryClone<BuffAddItem>(this.addItem);

				return ret;
            }


            public void WriteExternal(IOutputStream output)
            {
                output.PutS32(base.FrameMS);
                output.PutExt(this.Spell);
                output.PutExt(this.Effect);
                output.PutExt(this.Attack);
                output.PutExt(this.Item);
				output.PutExt(this.addItem);
            }

            public void ReadExternal(IInputStream input)
            {
                base.FrameMS = input.GetS32();
                this.Spell = input.GetExt<LaunchSpell>();
                this.Effect = input.GetExt<LaunchEffect>();
                this.Attack = input.GetExt<AttackProp>();
                this.Item = input.GetExt<UseItem>();
				this.addItem = input.GetExt<BuffAddItem>();
            }
        }

    }

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

    //---------------------------------------------------------------------------------//
    /// <summary>
    /// 掉落道具类数据结构
    /// </summary>
    [MessageType(0x4007)]
    [DescAttribute("掉落道具类数据结构")]
    [TableClassAttribute("ID")]
    public class ItemTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        [DescAttribute("ID", "", false)]
        public int ID;

        [LocalizationTextAttribute]
        [DescAttribute("掉落道具名字", "道具")]
        public string Name;
        [DescAttribute("掉落道具名字", "道具")]
        public string IconName;
        [DescAttribute("使用间隔时间(毫秒)", "道具")]
        public int UseCoolDownTimeMS = 0;
		[DescAttribute("场景显示的名字", "道具")]
		public string DisplayName;

		[DescAttribute("道具是否可被手动检取(如果手动检取,则不能直接走上去获得)", "捡取")]
        public bool Pickable = false;
        [DescAttribute("手动检取读条时间(毫秒)", "捡取")]
        [DependOnProperty("Pickable")]
        public int PickTimeMS = 2000;
        [DescAttribute("拾取后删除", "捡取")]
        public bool RemoveOnFinishPick = true;

        [DescAttribute("最大拾取人数", "捡取")]
        public byte maxPickPlayers = 0;
        [DescAttribute("单人最大拾取次数", "捡取")]
        public short maxPickTimes = 0;

        [DescAttribute("掉落后多久可以获得(毫秒)", "掉落")]
        public int GotCoolDownTimeMS = 500;
        [DescAttribute("掉落道具全阵营有效", "掉落")]
        public bool DropForAll;
        [DescAttribute("掉落道具仅玩家有效", "掉落")]
        public bool PlayerOnly = true;
        [DescAttribute("获得即使用", "掉落")]
        public bool GotOnUse = true;
        [DescAttribute("掉落金币最小值", "掉落")]
        public int DropMoneyMin = 0;
        [DescAttribute("掉落金币最大值", "掉落")]
        public int DropMoneyMax = 0;

        [DescAttribute("获得后的特效(检取者)", "特效")]
        public LaunchEffect GotEffect;
        [DescAttribute("获得后的特效(自身)", "特效")]
        public LaunchEffect GotEffectSelf;
        [DescAttribute("掉落时的特效", "特效")]
        public LaunchEffect DropEffect;

        [ResourceIDAttribute]
        [DescAttribute("资源模型名字", "资源")]
        public string FileName;
        [DescAttribute("拾取范围,半径", "资源")]
        public float BodySize = 1f;
        [DescAttribute("客户端可见", "资源")]
        public bool ClientVisible = true;
        
        [DescAttribute("道具产生后,持续时间(毫秒)", "掉落")]
        public int LifeTimeMS = 10000;

        [DescAttribute("道具产生后,显示截止时间戳(秒)", "掉落")]
        public int LifeEndTime = 0;

        [DescAttribute("道具飞到拾取者身上的插值(仅客户端表现用)", "掉落")]
        public float FlyLerp = 0.1f;

        [DescAttribute("是否显示持续时间 ", "掉落")]
        public bool showLifeTime = false;

        [DescAttribute("物品掉落的动画时长(毫秒)", "掉落")]
        public float AnimatorTime = -1.0f;

		[DescAttribute("仅做显示用", "掉落")]
		public bool OnlyForShow = false;

        [DescAttribute("模型上是否显示品质光柱", "掉落")]
        public bool ShowQualityEffect = true;

        [DescAttribute("使用后的特效", "特效")]
        public LaunchEffect UseEffect;

        [DescAttribute("使用后释放一个法术", "使用")]
        public LaunchSpell UseSpell;
        [DescAttribute("使用后召唤单位", "使用")]
        public SummonUnit UseSummon;

        [DescAttribute("使用后增加BUFF列表", "使用")]
        [ListAttribute(typeof(LaunchBuff))]
        public List<LaunchBuff> UseBuffs = new List<LaunchBuff>();
        [DescAttribute("装备后增加BUFF列表", "装备")]
        [ListAttribute(typeof(LaunchBuff))]
        public List<LaunchBuff> EquipBuffs = new List<LaunchBuff>();

        [DescAttribute("在背包内最大堆叠数量", "背包")]
        public int MaxStackCount = 100;
        [DescAttribute("最大持有数量,0表示无上限", "背包")]
        public int HoldingLimit = 0;
        [DescAttribute("是否在背包内可使用", "背包")]
        public bool IsInventoryUseable = true;
        [DescAttribute("是否可携带多个(包括堆叠)", "背包")]
        public bool IsDuplicateInventory = true;
        [DescAttribute("同步到服务器(可持久化保存的道具)", "背包")]
        public bool SyncToServer = false;
        [DescAttribute("背包道具使用读条(毫秒)", "背包")]
        public int UseInProgressTimeMS = 0;

        [DescAttribute("购买花费金币", "购买")]
        public bool BuyCostMoney;
        [DescAttribute("出售花费金币", "购买")]
        public bool SellGotMoney;

        [DescAttribute("是否弹出非物品类的二级面板", "显示")]
        public bool IsPopPanel = false;

        [ListAttribute(typeof(FlyEffect))]
        [DescAttribute("飞行特效", "妖气修为")]
        public List<FlyEffect> Fly;

        /// <summary>
        /// 道具扩展属性
        /// </summary>
        [DescAttribute("道具扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public IItemProperties Properties;

        public ItemTemplate()
        {
            this.Properties = TemplateManager.Factory.CreateItemProperties();
        }
        public ItemTemplate(int id)
        {
            this.ID = id;
            this.Properties = TemplateManager.Factory.CreateItemProperties();
        }
        public int GetID()
        {
            return ID;
        }
        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }

        public object Clone()
        {
            ItemTemplate ret = new ItemTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.IconName = this.IconName;

            ret.Pickable = this.Pickable;
            ret.PickTimeMS = this.PickTimeMS;
            ret.RemoveOnFinishPick = this.RemoveOnFinishPick;

            ret.GotCoolDownTimeMS = this.GotCoolDownTimeMS;
            ret.DropForAll = this.DropForAll;
            ret.PlayerOnly = this.PlayerOnly;
            ret.GotOnUse = this.GotOnUse;
            ret.DropMoneyMin = this.DropMoneyMin;
            ret.DropMoneyMax = this.DropMoneyMax;
            ret.GotEffect = CUtils.TryClone<LaunchEffect>(this.GotEffect);
            ret.GotEffectSelf = CUtils.TryClone<LaunchEffect>(this.GotEffectSelf);
            ret.DropEffect = CUtils.TryClone<LaunchEffect>(this.DropEffect);

            ret.FileName = this.FileName;
            ret.BodySize = this.BodySize;
            ret.ClientVisible = this.ClientVisible;
            ret.LifeTimeMS = this.LifeTimeMS;

            ret.UseEffect = CUtils.TryClone<LaunchEffect>(this.UseEffect);
            ret.UseSpell = CUtils.TryClone<LaunchSpell>(this.UseSpell);
            ret.UseSummon = CUtils.TryClone<SummonUnit>(this.UseSummon);
            ret.UseBuffs = CUtils.CloneList<LaunchBuff>(this.UseBuffs);
            ret.EquipBuffs = CUtils.CloneList<LaunchBuff>(this.EquipBuffs);

            ret.MaxStackCount = this.MaxStackCount;
            ret.HoldingLimit = this.HoldingLimit;
            ret.IsInventoryUseable = this.IsInventoryUseable;
            ret.UseCoolDownTimeMS = this.UseCoolDownTimeMS;
            ret.IsDuplicateInventory = this.IsDuplicateInventory;
            ret.SyncToServer = this.SyncToServer;
            ret.UseInProgressTimeMS = this.UseInProgressTimeMS;
            ret.FlyLerp = this.FlyLerp;
            ret.showLifeTime = this.showLifeTime;
            ret.AnimatorTime = this.AnimatorTime;
            ret.LifeEndTime = this.LifeEndTime;
            ret.IsPopPanel = this.IsPopPanel;

            ret.Fly = CUtils.CloneList<FlyEffect>(this.Fly);
            ret.Properties = CUtils.TryClone<IItemProperties>(this.Properties);

			ret.maxPickPlayers = this.maxPickPlayers;
			ret.maxPickTimes = this.maxPickTimes;

			ret.OnlyForShow = this.OnlyForShow;
			ret.DisplayName = this.DisplayName;
            ret.ShowQualityEffect = this.ShowQualityEffect;


            return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);
            output.PutUTF(this.IconName);

            output.PutBool(this.Pickable);
            output.PutS32(this.PickTimeMS);
            output.PutBool(this.RemoveOnFinishPick);

            output.PutS32(this.GotCoolDownTimeMS);
            output.PutBool(this.DropForAll);
            output.PutBool(this.PlayerOnly);
            output.PutBool(this.GotOnUse);
            output.PutS32(this.DropMoneyMin);
            output.PutS32(this.DropMoneyMax);
            output.PutExt(this.GotEffect);
            output.PutExt(this.GotEffectSelf);
            output.PutExt(this.DropEffect);

            output.PutUTF(this.FileName);
            output.PutF32(this.BodySize);
            output.PutBool(this.ClientVisible);
            output.PutS32(this.LifeTimeMS);

            output.PutExt(this.UseEffect);
            output.PutExt(this.UseSpell);
            output.PutExt(this.UseSummon);
            output.PutList(this.UseBuffs, output.PutExt);
            output.PutList(this.EquipBuffs, output.PutExt);

            output.PutS32(this.MaxStackCount);
            output.PutS32(this.HoldingLimit);
            output.PutBool(this.IsInventoryUseable);
            output.PutS32(this.UseCoolDownTimeMS);
            output.PutBool(this.IsDuplicateInventory);
            output.PutBool(this.SyncToServer);
            output.PutS32(this.UseInProgressTimeMS);

            output.PutExt(this.Properties);
            output.PutBool(this.showLifeTime);
            output.PutF32(this.FlyLerp);
            if (this.showLifeTime)
            {
                output.PutS32(this.LifeEndTime);
            }
            output.PutF32(this.AnimatorTime);
            output.PutBool(this.IsPopPanel);
            output.PutList<FlyEffect>(this.Fly, output.PutExt);
			output.PutS16(this.maxPickTimes);
			output.PutU8(this.maxPickPlayers);
			output.PutBool(this.OnlyForShow);
			output.PutUTF(this.DisplayName);
            output.PutBool(this.ShowQualityEffect);
        }
        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.IconName = input.GetUTF();

            this.Pickable = input.GetBool();
            this.PickTimeMS = input.GetS32();
            this.RemoveOnFinishPick = input.GetBool();

            this.GotCoolDownTimeMS = input.GetS32();
            this.DropForAll = input.GetBool();
            this.PlayerOnly = input.GetBool();
            this.GotOnUse = input.GetBool();
            this.DropMoneyMin = input.GetS32();
            this.DropMoneyMax = input.GetS32();
            this.GotEffect = input.GetExt<LaunchEffect>();
            this.GotEffectSelf = input.GetExt<LaunchEffect>();
            this.DropEffect = input.GetExt<LaunchEffect>();

            this.FileName = input.GetUTF();
            this.BodySize = input.GetF32();
            this.ClientVisible = input.GetBool();
            this.LifeTimeMS = input.GetS32();

            this.UseEffect = input.GetExt<LaunchEffect>();
            this.UseSpell = input.GetExt<LaunchSpell>();
            this.UseSummon = input.GetExt<SummonUnit>();
            this.UseBuffs = input.GetList<LaunchBuff>(input.GetExt<LaunchBuff>);
            this.EquipBuffs = input.GetList<LaunchBuff>(input.GetExt<LaunchBuff>);

            this.MaxStackCount = input.GetS32();
            this.HoldingLimit = input.GetS32();
            this.IsInventoryUseable = input.GetBool();
            this.UseCoolDownTimeMS = input.GetS32();
            this.IsDuplicateInventory = input.GetBool();
            this.SyncToServer = input.GetBool();
            this.UseInProgressTimeMS = input.GetS32();

            this.Properties = input.GetExt<IItemProperties>(this.Properties);
            this.showLifeTime = input.GetBool();
            this.FlyLerp = input.GetF32();
            if (this.showLifeTime)
            {
                this.LifeEndTime = input.GetS32();
            }
            this.AnimatorTime = input.GetF32();
            this.IsPopPanel = input.GetBool();
            this.Fly = input.GetList<FlyEffect>(input.GetExt<FlyEffect>);

			this.maxPickTimes = input.GetS16();
			this.maxPickPlayers = input.GetU8();
			this.OnlyForShow = input.GetBool();
			this.DisplayName = input.GetUTF();
            this.ShowQualityEffect = input.GetBool();
		}
    }

    [MessageType(0x4018)]
    [DescAttribute("物品飞行特效")]
    [Expandable]
    public class FlyEffect : ICloneable, IExternalizable
    {
        [DescAttribute("特效名字")]
        public string Name;
        [DescAttribute("特效时长(秒数)")]
        public float Time;
        [DescAttribute("以玩家为目标点")]
        public bool isActorPos;
        [DescAttribute("是否绑定在节点上")]
        public bool isBindPart;
        [DescAttribute("特效飞至的部位")]
        public string PartName;
        [DescAttribute("特效拉升的高度")]
        public float Height;
        [DescAttribute("特效速度")]
        public float Speed;
        [DescAttribute("运行方向 0:原点 1:上  2:下  3:左  4:右  5:前  6:后")]
        public byte RunDirection = 0;
        [DescAttribute("音效文件名")]
        public string AudioName;
        [DescAttribute("是否到达玩家位置就删除特效")]
        public bool IsGoToActorPosAndRemove;
        [DescAttribute("特效和玩家之间的最小距离,小于等于这个距离,这个特效将会移除")]
        [DependOnProperty("IsGoToActorPosAndRemove")]
        public float MinDistance = 0.05f;
        [DescAttribute("基于此节点激活间隔时长")]
        public bool IsBeginActiveIntervalTime = false;
        [DescAttribute("间隔时长,基于激活后的时间间隔")]
        public float IntervalTime = -1;
        [DescAttribute("此特效移除后,关联下一个特效的index")]
        public int NextEffectIndex = -1;
        [DescAttribute("此特效生成时,基于那个index特效的坐标")]
        public int PosIndex = -1;


        public object Clone()
        {
            FlyEffect ret = new FlyEffect();
            ret.Name = this.Name;
            ret.Time = this.Time;
            ret.PartName = this.PartName;
            ret.Height = this.Height;
            ret.isActorPos = this.isActorPos;
            ret.isBindPart = this.isBindPart;
            ret.Speed = this.Speed;
            ret.RunDirection = this.RunDirection;
            ret.AudioName = this.AudioName;
            ret.IsGoToActorPosAndRemove = this.IsGoToActorPosAndRemove;
            ret.IsBeginActiveIntervalTime = this.IsBeginActiveIntervalTime;
            ret.IntervalTime = this.IntervalTime;
            ret.NextEffectIndex = this.NextEffectIndex;
            ret.PosIndex = this.PosIndex;
            return ret;
        }

        public void ReadExternal(IInputStream input)
        {
            this.Name = input.GetUTF();
            this.Time = input.GetF32();
            this.PartName = input.GetUTF();
            this.Height = input.GetF32();
            this.isActorPos = input.GetBool();
            this.isBindPart = input.GetBool();
            this.Speed = input.GetF32();
            this.RunDirection = input.GetU8();
            this.AudioName = input.GetUTF();
            this.IsGoToActorPosAndRemove = input.GetBool();
            this.IsBeginActiveIntervalTime = input.GetBool();
            this.IntervalTime = input.GetF32();
            this.NextEffectIndex = input.GetS32();
            this.PosIndex = input.GetS32();
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.Name);
            output.PutF32(this.Time);
            output.PutUTF(this.PartName);
            output.PutF32(this.Height);
            output.PutBool(this.isActorPos);
            output.PutBool(this.isBindPart);
            output.PutF32(this.Speed);
            output.PutU8(this.RunDirection);
            output.PutUTF(this.AudioName);
            output.PutBool(this.IsGoToActorPosAndRemove);
            output.PutBool(this.IsBeginActiveIntervalTime);
            output.PutF32(this.IntervalTime);
            output.PutS32(this.NextEffectIndex);
            output.PutS32(this.PosIndex);
        }
    }

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

    [MessageType(0x4015)]
    [DescAttribute("单位事件触发")]
    [TableClassAttribute("ID")]
    public class UnitEventTemplate : ICloneable, ITemplateData, IExternalizable
    {
        public int TemplateID { get { return ID; } }
        [XmlSerializable()]
        public string EditorPath { get; set; }

        [DescAttribute("ID", "", false)]
        public int ID;
        [LocalizationTextAttribute]
        [DescAttribute("单位事件触发名字", "基础")]
        public string Name;

        [ListAttribute(typeof(UnitEvent))]
        [DescAttribute("所有事件", "基础", false)]
        public List<UnitEvent> Events = new List<UnitEvent>();

        public override string ToString()
        {
            return Name + "(" + ID + ")";
        }
        public object Clone()
        {
            UnitEventTemplate ret = new UnitEventTemplate();
            ret.EditorPath = this.EditorPath;
            ret.ID = this.ID;
            ret.Name = this.Name;
            ret.Events = CUtils.CloneList<UnitEvent>(this.Events);
            return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutUTF(this.EditorPath);
            output.PutS32(this.ID);
            output.PutUTF(this.Name);

            output.PutList(this.Events, output.PutExt);
        }
        public void ReadExternal(IInputStream input)
        {
            this.EditorPath = input.GetUTF();
            this.ID = input.GetS32();
            this.Name = input.GetUTF();
            this.Events = input.GetList(input.GetExt<UnitEvent>);
        }
        public UnitEvent GetEvent(string name)
        {
            foreach (var e in Events)
            {
                if (e.Name == name) { return e; }
            }
            return null;
        }
    }


    #endregion

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

    #region __Launch_Objects__

    public abstract class BaseKeyFrame
    {
        [DescAttribute("关键帧时间(毫秒)")]
        public int FrameMS;
    }

    /// <summary>
    /// 技能或者法术产生的攻击属性
    /// </summary>
    [MessageType(0x4008)]
    [DescAttribute("技能或者法术产生的攻击属性")]
    [Expandable]
    public class AttackProp : ISNData, ICloneable
    {
        /// <summary>
        /// 攻击力
        /// </summary>
        [DescAttribute("攻击力")]
        public int Attack;

        [DescAttribute("产生受击", "Mask")]
        public bool MaskDamage = false;
        [DescAttribute("产生击飞", "Mask")]
        [DependOnProperty("MaskDamage")]
        public bool MaskHitFly = false;
        [DescAttribute("产生击倒", "Mask")]
        [DependOnProperty("MaskDamage")]
        public bool MaskKnockDown = false;
        [DescAttribute("此次攻击必命中,即使命中为0", "Mask")]
        public bool MaskMustHit = false;
        [DescAttribute("此次攻击必暴击,即使暴击为0", "Mask")]
        public bool MaskMustCritical = false;


        [DescAttribute("击中力度(大于目标体重才会产生受击)", "受击")]
        [DependOnProperty("MaskDamage")]
        public int Weight = 1;
        [DescAttribute("单位被(受击、击倒、眩晕、混乱)持续时间(毫秒)" +
            "\n * 此时间如果为0,首先选用单位DamageTimeMS,否则然后采用Config环境参数OBJECT_DAMAGE_TIME_MS替代之。" +
            "\n * HitMove运动时间不计算在KnockOutTimeMS内。", "受击")]
        [DependOnProperty("MaskDamage")]
        public int KnockOutTimeMS = 0;
        [DescAttribute("受击动作名", "受击")]
        [DependOnProperty("MaskDamage")]
        public string DamageActionName = null;
        [DescAttribute("受击时,不再遭受其他攻击", "受击")]
        [DependOnProperty("MaskDamage")]
        public bool IsDamageProtect = false;


        public bool IsHitMove { get { return MaskDamage && HitMove != null; } }
        [DescAttribute("被击中后向后移动距离(包括击飞控制)", "受击")]
        [DependOnProperty("MaskDamage")]
        public StartMove HitMove;
        public enum HitMoveType : byte
        {
            [DescAttribute("根据攻击者位置(台球碰撞)")]
            BySenderPosition,
            [DescAttribute("根据攻击者朝向(单向)")]
            BySenderDirection,
            [DescAttribute("根据攻击者朝向的左右边(摩西分海)")]
            BySenderLeftRight,
            [DescAttribute("位移至攻击者中心(向里吸)")]
            ToSenderCenter,
            [DescAttribute("位移至攻击者身边(向里吸)")]
            ToSenderBodySize,
            [DescAttribute("位移至攻击固定范围外")]
            BySenderRange,
        }
        [DescAttribute("受击位移计算方式", "受击")]
        [DependOnProperty("IsHitMove")]
        public HitMoveType HitMoveMType = HitMoveType.BySenderPosition;
        [DescAttribute("受击位移时对其他单位产生伤害", "受击")]
        [DependOnProperty("IsHitMove")]
        public AttackProp HitMoveBodyAttack;
        [DescAttribute("基于被攻击者身体范围的增加范围", "受击")]
        [DependOnProperty("IsHitMove")]
        public float HitMoveBodyAttackSize = 1;
        [DescAttribute("被击飞落地后产生伤害,或者位移结束产生伤害", "受击")]
        [DependOnProperty("MaskDamage")]
        [DependOnProperty("MaskHitFly")]
        public AttackProp FlyFallenDownAttack;


        [DescAttribute("被击中特效", "被击中")]
        public LaunchEffect Effect;
        [DescAttribute("被击中触发一个BUFF", "被击中")]
        public LaunchBuff Buff;
        [DescAttribute("被击中触发一个Spell", "被击中")]
        public LaunchSpell Spell;


        [DescAttribute("百分比概率击碎目标", "击碎")]
        public float CrushPercent = 0;
        [DescAttribute("击碎目标效果", "击碎")]
        public LaunchEffect CrushEffect;

        [DescAttribute("定帧时长", "客户端")]
        public int StopFrameMS = 0;
        [DescAttribute("定帧类型", "客户端")]
        public string StopFrameAction;

        /// <summary>
        /// 用户自定义扩展属性
        /// </summary>
        [DescAttribute("用户自定义扩展属性", "扩展")]
        [Expandable]
        [NotNull]
        public IAttackProperties Properties;
        [DescAttribute("打死小怪是否击退", "受击")]
        public KillHitMove killHitMove;

        public AttackProp()
        {
            Properties = TemplateManager.Factory.CreateAttackProperties();
        }
        /// <summary>
        /// 是否产生受击
        /// </summary>
        /// <returns></returns>
        public bool IsDamage()
        {
            return MaskDamage || MaskHitFly || MaskKnockDown;
        }
        public override string ToString()
        {
            return "攻击";
        }


        public object Clone()
        {
            AttackProp ret = new AttackProp();
            ret.SerialNumber = this.SerialNumber;
            ret.Attack = this.Attack;
            ret.MaskDamage = this.MaskDamage;
            ret.MaskHitFly = this.MaskHitFly;
            ret.MaskKnockDown = this.MaskKnockDown;
            ret.MaskMustHit = this.MaskMustHit;
            ret.MaskMustCritical = this.MaskMustCritical;

            ret.Weight = this.Weight;
            ret.KnockOutTimeMS = this.KnockOutTimeMS;
            ret.IsDamageProtect = this.IsDamageProtect;
            ret.DamageActionName = this.DamageActionName;

            ret.Effect = CUtils.TryClone<LaunchEffect>(this.Effect);
            ret.Buff = CUtils.TryClone<LaunchBuff>(this.Buff);
            ret.Spell = CUtils.TryClone<LaunchSpell>(this.Spell);

            ret.HitMove = CUtils.TryClone<StartMove>(this.HitMove);
            ret.HitMoveMType = this.HitMoveMType;
            ret.HitMoveBodyAttack = CUtils.TryClone<AttackProp>(this.HitMoveBodyAttack);
            ret.HitMoveBodyAttackSize = this.HitMoveBodyAttackSize;
            ret.FlyFallenDownAttack = CUtils.TryClone<AttackProp>(this.FlyFallenDownAttack);

            ret.StopFrameMS = this.StopFrameMS;
            ret.StopFrameAction = this.StopFrameAction;

            ret.CrushPercent = this.CrushPercent;
            ret.CrushEffect = CUtils.TryClone<LaunchEffect>(this.CrushEffect);
            ret.Properties = CUtils.TryClone<IAttackProperties>(this.Properties);
            ret.killHitMove = CUtils.TryClone<KillHitMove>(this.killHitMove);
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(this.Attack);
            output.PutBool(this.MaskDamage);
            output.PutBool(this.MaskHitFly);
            output.PutBool(this.MaskKnockDown);
            output.PutBool(this.MaskMustHit);
            output.PutBool(this.MaskMustCritical);

            output.PutS32(this.Weight);
            output.PutS32(this.KnockOutTimeMS);
            output.PutBool(this.IsDamageProtect);
            output.PutUTF(this.DamageActionName);

            output.PutExt(this.Effect);
            output.PutExt(this.Buff);
            output.PutExt(this.Spell);

            output.PutExt(this.HitMove);
            output.PutEnum8(this.HitMoveMType);
            output.PutExt(this.HitMoveBodyAttack);
            output.PutF32(this.HitMoveBodyAttackSize);
            output.PutExt(this.FlyFallenDownAttack);

            output.PutS32(this.StopFrameMS);
            output.PutUTF(this.StopFrameAction);

            output.PutF32(this.CrushPercent);
            output.PutExt(this.CrushEffect);
            output.PutExt(this.Properties);
            output.PutExt(this.killHitMove);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.Attack = input.GetS32();
            this.MaskDamage = input.GetBool();
            this.MaskHitFly = input.GetBool();
            this.MaskKnockDown = input.GetBool();
            this.MaskMustHit = input.GetBool();
            this.MaskMustCritical = input.GetBool();

            this.Weight = input.GetS32();
            this.KnockOutTimeMS = input.GetS32();
            this.IsDamageProtect = input.GetBool();
            this.DamageActionName = input.GetUTF();

            this.Effect = input.GetExt<LaunchEffect>();
            this.Buff = input.GetExt<LaunchBuff>();
            this.Spell = input.GetExt<LaunchSpell>();

            this.HitMove = input.GetExt<StartMove>();
            this.HitMoveMType = input.GetEnum8<HitMoveType>();
            this.HitMoveBodyAttack = input.GetExt<AttackProp>();
            this.HitMoveBodyAttackSize = input.GetF32();
            this.FlyFallenDownAttack = input.GetExt<AttackProp>();

            this.StopFrameMS = input.GetS32();
            this.StopFrameAction = input.GetUTF();

            this.CrushPercent = input.GetF32();
            this.CrushEffect = input.GetExt<LaunchEffect>();
            this.Properties = input.GetExt<IAttackProperties>(this.Properties);
            this.killHitMove = input.GetExt<KillHitMove>();
        }
    }

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

    /// <summary>
    /// 触发特效,一般只用于显示
    /// </summary>
    [MessageType(0x4009)]
    [DescAttribute("触发特效,一般只用于显示")]
    [Expandable]
    public class LaunchEffect : ISNData, ICloneable
    {
        public enum RunType : byte
        {
            [DescAttribute("默认,沒有運行")]
            None = 0,
            [DescAttribute("圓形,以綁定點未中心")]
            Cycle_BindName = 1,
        }

        [DescAttribute("触发的特效名字")]
        [ResourceIDAttribute]
        public string Name;
        [DescAttribute("触发特效是否绑定在特定单位")]
        public bool BindBody = true;
        [DescAttribute("绑定挂载点位置")]
        public string BindPartName;
        [DescAttribute("如果不为0,则特效以该尺寸缩放")]
        public float ScaleToBodySize;
        [DescAttribute("特效高度的偏移量")]
        public float EffectHight = 0;
        [DescAttribute("特效水平方向的偏移量x")]
        public float EffectOffsetX = 0;
        [DescAttribute("特效水平方向的偏移量x")]
        public float EffectOffsetY = 0;

        [DescAttribute("音效")]
        [ResourceIDAttribute]
        public string SoundName;
        [DescAttribute("震屏时长(毫秒)", "震屏")]
        public int EarthQuakeMS;
        [DescAttribute("震屏幅度", "震屏")]
        public float EarthQuakeXYZ;
        [DescAttribute("自定义字段", "特效扩展")]
        public string Tag;
        [DescAttribute("摄像机动画名字")]
        public string CameraAnimation;

        [DescAttribute("特效持续时间,0表示只播放一次")]
        public int EffectTimeMS = 0;
        [DescAttribute("是否循环")]
        public bool IsLoop = false;
        [DescAttribute("是否同步施法者的Rotation")]
        public bool IsSyncSenderRotation = false;
        [DescAttribute("速度(原始动画/特效速度 * 速度)", "特效加速")]
        public float EffectAddSpeed = 1;
        [DescAttribute("特效中存在的动画,动画的名字", "特效加速")]
        public string AnimtionName;
        [DescAttribute("Buff運行方式")]
        public RunType RType = RunType.None;

        [DescAttribute("圆形运行参数设置")]
        public CricleSpeedMode CricleMode;

        public override string ToString()
        {
            return "触发特效:" + Name;
        }

        public object Clone()
        {
            LaunchEffect ret = new LaunchEffect();
            ret.SerialNumber = this.SerialNumber;
            ret.Name = this.Name;
            ret.BindBody = this.BindBody;
            ret.BindPartName = this.BindPartName;
            ret.ScaleToBodySize = this.ScaleToBodySize;
            ret.EffectHight = this.EffectHight;
            ret.SoundName = this.SoundName;
            ret.EarthQuakeMS = this.EarthQuakeMS;
            ret.EarthQuakeXYZ = this.EarthQuakeXYZ;
            ret.Tag = this.Tag;
            ret.CameraAnimation = this.CameraAnimation;
            ret.EffectTimeMS = this.EffectTimeMS;
            ret.IsLoop = this.IsLoop;
            ret.IsSyncSenderRotation = this.IsSyncSenderRotation;
            ret.EffectAddSpeed = this.EffectAddSpeed;
            ret.AnimtionName = this.AnimtionName;
            ret.RType = this.RType;
            ret.CricleMode = CUtils.TryClone<CricleSpeedMode>(this.CricleMode);
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutUTF(this.Name);
            output.PutBool(this.BindBody);
            output.PutUTF(this.BindPartName);
            output.PutF32(this.ScaleToBodySize);
            output.PutF32(this.EffectHight);
            output.PutUTF(this.SoundName);
            output.PutS32(this.EarthQuakeMS);
            output.PutF32(this.EarthQuakeXYZ);
            output.PutUTF(this.Tag);
            output.PutUTF(this.CameraAnimation);
            output.PutS32(this.EffectTimeMS);
            output.PutBool(this.IsLoop);
            output.PutBool(this.IsSyncSenderRotation);
            output.PutF32(this.EffectAddSpeed);
            output.PutUTF(this.AnimtionName);
            output.PutEnum8(this.RType);
            output.PutExt(this.CricleMode);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.Name = input.GetUTF();
            this.BindBody = input.GetBool();
            this.BindPartName = input.GetUTF();
            this.ScaleToBodySize = input.GetF32();
            this.EffectHight = input.GetF32();
            this.SoundName = input.GetUTF();
            this.EarthQuakeMS = input.GetS32();
            this.EarthQuakeXYZ = input.GetF32();
            this.Tag = input.GetUTF();
            this.CameraAnimation = input.GetUTF();
            this.EffectTimeMS = input.GetS32();
            this.IsLoop = input.GetBool();
            this.IsSyncSenderRotation = input.GetBool();
            this.EffectAddSpeed = input.GetF32();
            this.AnimtionName = input.GetUTF();
            this.RType = input.GetEnum8<RunType>();
            this.CricleMode = input.GetExt<CricleSpeedMode>();
        }
    }

    [MessageType(0x4017)]
    [DescAttribute("围绕中心点圆形运行(美术工程有testpos脚本,方便快速调试)")]
    [Expandable]
    public class CricleSpeedMode : ISNData, ICloneable
    {
        [DescAttribute("挂节点的半径")]
        public string[] EffectName;
        [DescAttribute("挂节点的半径")]
        public float Radius = 0;
        [DescAttribute("点间距")]
        public float Gap = 0;
        [DescAttribute("弧度")]
        public float Radian = 0;
        [DescAttribute("圆点距离")]
        public float CircleCenterDis = 0;
        [DescAttribute("圆点距离2")]
        public float CircleCenterDis2 = 0;
        [DescAttribute("互斥度")]
        public float Mutual = 0;
        [DescAttribute("高度")]
        public float Height = 0;
        [DescAttribute("x轴弧度")]
        public float X_Direction = 0;
        [DescAttribute("y轴弧度")]
        public float Y_Direction = 0;
        [DescAttribute("z轴弧度")]
        public float Z_Direction = 0;
        [DescAttribute("旋转速度")]
        public float RotationSpeed = 0;

        public object Clone()
        {
            CricleSpeedMode ret = new CricleSpeedMode();
            ret.SerialNumber = this.SerialNumber;
            ret.EffectName = CUtils.CloneArray<string>(this.EffectName);
            ret.Radius = this.Radius;
            ret.Gap = this.Gap;
            ret.Radian = this.Radian;
            ret.CircleCenterDis = this.CircleCenterDis;
            ret.CircleCenterDis2 = this.CircleCenterDis2;
            ret.Mutual = this.Mutual;
            ret.Height = this.Height;
            ret.X_Direction = this.X_Direction;
            ret.Y_Direction = this.Y_Direction;
            ret.Z_Direction = this.Z_Direction;
            ret.RotationSpeed = this.RotationSpeed;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutArray(this.EffectName, output.PutUTF);
            output.PutF32(this.Radius);
            output.PutF32(this.Gap);
            output.PutF32(this.Radian);
            output.PutF32(this.CircleCenterDis);
            output.PutF32(this.CircleCenterDis2);
            output.PutF32(this.Mutual);
            output.PutF32(this.Height);
            output.PutF32(this.X_Direction);
            output.PutF32(this.Y_Direction);
            output.PutF32(this.Z_Direction);
            output.PutF32(this.RotationSpeed);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.EffectName = input.GetUTFArray();
            this.Radius = input.GetF32();
            this.Gap = input.GetF32();
            this.Radian = input.GetF32();
            this.CircleCenterDis = input.GetF32();
            this.CircleCenterDis2 = input.GetF32();
            this.Mutual = input.GetF32();
            this.Height = input.GetF32();
            this.X_Direction = input.GetF32();
            this.Y_Direction = input.GetF32();
            this.Z_Direction = input.GetF32();
            this.RotationSpeed = input.GetF32();
        }
    }
    //---------------------------------------------------------------------------------//

    /// <summary>
    /// 发起法术或者飞行道具
    /// </summary>
    [MessageType(0x400A)]
    [DescAttribute("释技能")]
    [Expandable]
    public class LaunchSkill : ISNData, ICloneable
    {
        [DescAttribute("SkillID")]
        [TemplateIDAttribute(typeof(SkillTemplate))]
        public int SkillID;

        [DescAttribute("释放技能权值-弃用")]
        public int Priority;

        [DescAttribute("AI自动释放技能-内部使用,不能设置")]
        public bool AutoLaunch = true;

        public LaunchSkill() { }
        public LaunchSkill(int skillID, bool autoLaunch = true)
        {
            this.SkillID = skillID;
			this.AutoLaunch = autoLaunch;
        }
        public override string ToString()
        {
            return "触发技能:" + SkillID;
        }

        public object Clone()
        {
            LaunchSkill ret = new LaunchSkill();
            ret.SerialNumber = this.SerialNumber;
            ret.SkillID = this.SkillID;
            ret.Priority = this.Priority;
            ret.AutoLaunch = this.AutoLaunch;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(SkillID);
            output.PutS32(Priority);
            output.PutBool(AutoLaunch);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.SkillID = input.GetS32();
            this.Priority = input.GetS32();
            this.AutoLaunch = input.GetBool();
        }
    }

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

    /// <summary>
    /// 发起法术或者飞行道具
    /// </summary>
    [MessageType(0x400B)]
    [DescAttribute("释放法术或者飞行道具")]
    [Expandable]
    public class LaunchSpell : ISNData, ICloneable
    {
        public enum PosType : byte
        {
            /// <summary>
            /// 默认释放一个法术
            /// </summary>
            POS_TYPE_DEFAULT_SINGLE = 0,
            /// <summary>
            /// 随机范围内目标施放法术
            /// </summary>
            POS_TYPE_RANDOM_FOR_SPELL = 1,
			/// <summary>
			/// 打出圆环多个法术
			/// </summary>
			POS_TYPE_CYCLE = 2,
			/// <summary>
			/// 打出扇形多个法术
			/// </summary>
			POS_TYPE_FAN = 3,
            /// <summary>
            /// 随机方向
            /// </summary>
            POS_TYPE_RANDOM_DIRECTION = 4,
            /// <summary>
            /// 范围
            /// </summary>
            POS_TYPE_AREA = 5,
            /// <summary>
            /// 向同一个目标发射三个锁定法术
            /// </summary>
            POS_TYPE_TOSAMETARGET = 6,
			/// <summary>
			/// X型法术
			/// </summary>
			POS_TYPE_X = 7,
			/// <summary>
			/// 随机范围内施放法术
			/// </summary>
			POS_TYPE_RANDOM_POS = 8,

			/// <summary>
			/// 火箭飞弹
			/// 
			/// </summary>
			POS_TYPE_MANY_CANNON = 9,
		}

        /// <summary>
        /// 触发新的法术
        /// </summary>
        [DescAttribute("触发新的法术")]
        [TemplateIDAttribute(typeof(SpellTemplate))]
        public int SpellID;

        [DescAttribute("触发百分比")]
        public float LaunchPercent = 100f;

        /// <summary>
        /// 触发新的法术运行方式
        /// </summary>
        [DescAttribute("触发新的法术运行方式")]
        public PosType PType = PosType.POS_TYPE_DEFAULT_SINGLE;

        /// <summary>
        /// 触发新的法术数量
        /// </summary>
        [DescAttribute("法术范围")]
        [DependOnProperty("HasSpellRange")]
        public int SpellRange = 1;

        public bool MultiLaunch { get { return PType != PosType.POS_TYPE_DEFAULT_SINGLE; } }

		public bool HasSpellRange { get { return PType == PosType.POS_TYPE_RANDOM_FOR_SPELL || PType == PosType.POS_TYPE_RANDOM_POS; } }

		/// <summary>
		/// 触发新的法术数量
		/// </summary>
		[DescAttribute("触发新的法术数量")]
        [DependOnProperty("MultiLaunch")]
        public int Count = 1;

        /// <summary>
        /// 如果触发为扇形,则定义扇形范围
        /// </summary>
        [DescAttribute("扇形:扇形范围;X型:小角值")]
        [DependOnProperty("MultiLaunch")]
        public float Angle = 0;

        [DescAttribute("发射初始角度")]
        [DependOnProperty("MultiLaunch")]
        public float StartAngle = 0;

        public enum LaunchSpllSenderUnit : byte
        {
            [DescAttribute("默认")]
            Sender = 0,
            [DescAttribute("强制为施法者")]
            Launcher = 1,
            [DescAttribute("强制为被攻击者(只在Attack Launch Spell有效)")]
            DamagedUnit = 2,
			[DescAttribute("攻击者(在Buff中,buff的释加者)")]
			AttackerUnit = 3,
		}
        [DescAttribute("发射者", "发射者")]
        public LaunchSpllSenderUnit SenderUnit = LaunchSpllSenderUnit.Sender;


        [DescAttribute("填True,下面无效,发射口是否以单位炮口作为参考", "炮口")]
        public bool FromUnitBody = true;
        [DescAttribute("法术发射高度(炮口高度)", "炮口")]
        [DependOnProperty("FromUnitBody", false)]
        public float LaunchSpellHeight;
        [DescAttribute("法术发射半径(炮口半径)", "炮口")]
        [DependOnProperty("FromUnitBody", false)]
        public float LaunchSpellRadius = 0;

		[DescAttribute("位置偏移类型")]
		public XYModifyType xyModifyType = XYModifyType.Relative;
		[DescAttribute("位置偏移X")]
		public float xModify;
		[DescAttribute("位置偏移Y")]
		public float yModify;

		[DescAttribute("连锁等级,只在技能中LaunchSpell有效(闪电链类数量,如果Spell launch spell ID一致,则传递此值)", "连锁")]
        public int ChainLevel = 0;

        [DescAttribute("自动锁定范围内目标", "自动锁定")]
        public bool IsAutoSeekingTarget = false;
        [DescAttribute("自动锁定范围内目标(范围)", "自动锁定")]
        [DependOnProperty("IsAutoSeekingTarget")]
        public float SeekingTargetRange = 10f;
        [DescAttribute("自动锁定范围内目标(方式)", "自动锁定")]
        [DependOnProperty("IsAutoSeekingTarget")]
        public SpellTemplate.SeekingExpect SeekingTargetExpect = SpellTemplate.SeekingExpect.Random;


		//是否是程序创建的法师
		private bool mIsCodeCreate = false;

        public LaunchSpell() { }

		public LaunchSpell(bool codeCreate)
		{
			this.mIsCodeCreate = codeCreate;
		}

		public bool IsCodeCreate()
		{
			return this.mIsCodeCreate;
		}

		public override string ToString()
        {
            //return "( " + SpellID + ", " + Count + " )";
            return "法术:" + this.SpellID + " 数量:" + Count;
        }


        public object Clone()
        {
            LaunchSpell ret = new LaunchSpell();
            ret.SerialNumber = this.SerialNumber;
            ret.SpellID = this.SpellID;
            ret.LaunchPercent = this.LaunchPercent;
            ret.PType = this.PType;
            ret.Count = this.Count;
            ret.Angle = this.Angle;
            ret.StartAngle = this.StartAngle;
            ret.FromUnitBody = this.FromUnitBody;
            ret.LaunchSpellHeight = this.LaunchSpellHeight;
            ret.LaunchSpellRadius = this.LaunchSpellRadius;
            ret.ChainLevel = this.ChainLevel;
            ret.SenderUnit = this.SenderUnit;
            ret.IsAutoSeekingTarget = this.IsAutoSeekingTarget;
            ret.SeekingTargetRange = this.SeekingTargetRange;
            ret.SeekingTargetExpect = this.SeekingTargetExpect;
            ret.SpellRange = this.SpellRange;
			ret.xModify = this.xModify;
			ret.yModify = this.yModify;
			ret.xyModifyType = this.xyModifyType;
			ret.mIsCodeCreate = this.mIsCodeCreate;
			return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(this.SpellID);
            output.PutF32(this.LaunchPercent);
            output.PutEnum8(this.PType);
            output.PutS32(this.Count);
            output.PutF32(this.Angle);
            output.PutF32(this.StartAngle);
            output.PutBool(this.FromUnitBody);
            output.PutF32(this.LaunchSpellHeight);
            output.PutF32(this.LaunchSpellRadius);
            output.PutS32(this.ChainLevel);
            output.PutEnum8(this.SenderUnit);

            output.PutBool(this.IsAutoSeekingTarget);
            output.PutF32(this.SeekingTargetRange);
            output.PutEnum8(this.SeekingTargetExpect);
            output.PutS32(this.SpellRange);
			output.PutF32(this.xModify);
			output.PutF32(this.yModify);
			output.PutEnum8(this.xyModifyType);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.SpellID = input.GetS32();
            this.LaunchPercent = input.GetF32();
            this.PType = input.GetEnum8<PosType>();
            this.Count = input.GetS32();
            this.Angle = input.GetF32();
            this.StartAngle = input.GetF32();
            this.FromUnitBody = input.GetBool();
            this.LaunchSpellHeight = input.GetF32();
            this.LaunchSpellRadius = input.GetF32();
            this.ChainLevel = input.GetS32();
            this.SenderUnit = input.GetEnum8<LaunchSpllSenderUnit>();
            this.IsAutoSeekingTarget = input.GetBool();
            this.SeekingTargetRange = input.GetF32();
            this.SeekingTargetExpect = input.GetEnum8<SpellTemplate.SeekingExpect>();
            this.SpellRange = input.GetS32();
			this.xModify = input.GetF32();
			this.yModify = input.GetF32();
			this.xyModifyType = input.GetEnum8<XYModifyType>();

		}

		public void GetXModify(InstanceUnit launcher, out float xChange, out float yChange)
		{
			if ((this.xModify != 0 || this.yModify != 0) && this.xyModifyType == Data.XYModifyType.Relative && launcher != null)
			{
				float degree = MathVector.getDegree(this.xModify, this.yModify);
				float distance = (float)Math.Sqrt(this.xModify * this.xModify + this.yModify * this.yModify);
				xChange = (float)(distance * Math.Cos(degree + launcher.Direction));
				yChange = (float)(distance * Math.Sin(degree + launcher.Direction));
				return;
			}
			else if (this.xyModifyType == Data.XYModifyType.Absolutely)
			{
				//不做处理
			}

			xChange = this.xModify;
			yChange = this.yModify;
		}
	}

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

    /// <summary>
    /// 发起BUFF
    /// </summary>
    [MessageType(0x400C)]
    [DescAttribute("发起BUFF")]
    [Expandable]
    public class LaunchBuff : ISNData, ICloneable
    {
        /// <summary>
        /// Buff模板ID
        /// </summary>
        [DescAttribute("Buff模板ID, 一般用BindEventID")]
        [TemplateIDAttribute(typeof(BuffTemplate))]
        public int BuffID;

        [DescAttribute("触发百分比-废弃")]
        public float LaunchPercent = 100f;

		[DescAttribute("绑定事件id:有则去读事件表,忽视BuffID")]
		[DependOnProperty("IsBindEventID")]
		public int BindEventID;

		public bool IsBindEventID { get { return this.BuffID == 0; } }

		public LaunchBuff() { }
        public LaunchBuff(int buffID) { this.BuffID = buffID; }

        public override string ToString()
        {
            return "BUFF:" + BuffID + "_@_" + BindEventID;
        }

        public object Clone()
        {
            LaunchBuff ret = new LaunchBuff();
            ret.SerialNumber = this.SerialNumber;
            ret.BuffID = this.BuffID;
			ret.BindEventID = this.BindEventID;

			ret.LaunchPercent = this.LaunchPercent;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(BuffID);
            output.PutF32(LaunchPercent);
			output.PutS32(BindEventID);
		}

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.BuffID = input.GetS32();
            this.LaunchPercent = input.GetF32();
			this.BindEventID = input.GetS32();
		}
    }

    /// <summary>
    /// 召唤单位
    /// </summary>
    [MessageType(0x400D)]
    [DescAttribute("召唤单位")]
    [Expandable]
    public class SummonUnit : ISNData, ICloneable
    {
        [DescAttribute("召唤单位模板")]
        [TemplateIDAttribute(typeof(UnitInfo))]
        public int UnitTemplateID;

        [DescAttribute("召唤单位等级")]
        [TemplateLevelAttribute]
        public int UnitLevel = 0;

        [DescAttribute("召唤处产生数量")]
        public int Count = 1;

        [DescAttribute("召唤处产生特效")]
        public LaunchEffect Effect;

		public SummonUnit() { }
        public SummonUnit(int templateID)
        {
            this.UnitTemplateID = templateID;
        }

        public override string ToString()
        {
            return "召唤单位:" + UnitTemplateID + " 数量:" + Count;
        }

        public object Clone()
        {
            SummonUnit ret = new SummonUnit();
            ret.SerialNumber = this.SerialNumber;
            ret.UnitTemplateID = this.UnitTemplateID;
            ret.UnitLevel = this.UnitLevel;
            ret.Count = this.Count;
            ret.Effect = CUtils.TryClone<LaunchEffect>(this.Effect);
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(UnitTemplateID);
            output.PutS32(UnitLevel);
            output.PutS32(Count);
            output.PutExt(Effect);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.UnitTemplateID = input.GetS32();
            this.UnitLevel = input.GetS32();
            this.Count = input.GetS32();
            this.Effect = input.GetExt<LaunchEffect>();
        }
    }

    [MessageType(0x4016)]
    [DescAttribute("闪现移动")]
    [Expandable]
    public class BlinkMove : ICloneable, IExternalizable
    {
        [DescAttribute("移动距离", "移动")]
        public float Distance = 5;
        [DescAttribute("角度偏移", "移动")]
        public float DirectionOffset;
        [DescAttribute("移动方式", "移动")]
        public BlinkMoveType MType = BlinkMoveType.MoveToForward;

        [DescAttribute("忽略地形", "碰撞")]
        public bool NoneTouchMap = false;
        [DescAttribute("忽略单位", "碰撞")]
        public bool NoneTouchObj = true;

        [DescAttribute("闪现起始点特效", "特效")]
        public LaunchEffect BeginEffect;
        [DescAttribute("闪现目标点特效", "特效")]
        public LaunchEffect TargetEffect;

        public enum BlinkMoveType : byte
        {
            [DescAttribute("向身前移动")]
            MoveToForward,
            [DescAttribute("向身后移动")]
            MoveToBackward,
            [DescAttribute("向技能释放目标点移动")]
            MoveToTargetPos,
            [DescAttribute("向技能释放目标单位移动(面前)")]
            MoveToTargetUnitFace,
            [DescAttribute("向技能释放目标单位移动(背后)")]
            MoveToTargetUnitBack,
        }
        public object Clone()
        {
            BlinkMove ret = new BlinkMove();
            ret.Distance = this.Distance;
            ret.DirectionOffset = this.DirectionOffset;
            ret.MType = this.MType;
            ret.NoneTouchMap = this.NoneTouchMap;
            ret.NoneTouchObj = this.NoneTouchObj;
            ret.BeginEffect = CUtils.TryClone<LaunchEffect>(this.BeginEffect);
            ret.TargetEffect = CUtils.TryClone<LaunchEffect>(this.TargetEffect);
            return ret;
        }
        public void WriteExternal(IOutputStream output)
        {
            output.PutF32(this.Distance);
            output.PutF32(this.DirectionOffset);
            output.PutEnum8(this.MType);
            output.PutBool(this.NoneTouchMap);
            output.PutBool(this.NoneTouchObj);
            output.PutExt(this.BeginEffect);
            output.PutExt(this.TargetEffect);
        }
        public void ReadExternal(IInputStream input)
        {
            this.Distance = input.GetF32();
            this.DirectionOffset = input.GetF32();
            this.MType = input.GetEnum8<BlinkMoveType>();
            this.NoneTouchMap = input.GetBool();
            this.NoneTouchObj = input.GetBool();
            this.BeginEffect = input.GetExt<LaunchEffect>();
            this.TargetEffect = input.GetExt<LaunchEffect>();
        }
        public override string ToString()
        {
            return "闪现移动";
        }
    }

    /// <summary>
    /// 移动距离
    /// </summary>
    [MessageType(0x400E)]
    [DescAttribute("移动距离")]
    [Expandable]
    public class StartMove : ISNData, ICloneable
    {
        [DescAttribute("速度(距离/每秒)", "位移")]
        public float SpeedSEC = 10f;
        [DescAttribute("加速度(距离/每秒)(最终速度=速度+加速度)", "位移")]
        public float SpeedAdd = 0f;
        [DescAttribute("阻力(每秒递减速度百分比)", "位移")]
        public float SpeedAcc = 0f;
        [DescAttribute("被击飞的向上速度(距离/每秒)", "位移")]
        public float ZSpeedSEC = 0f;
        [DescAttribute("被击飞的向上移动最大距离(天花板高度)", "位移")]
        [DependOnProperty("HasFly")]
        public float ZLimit = 0f;
        [DescAttribute("重设重力(非0有效)", "位移")]
        [DependOnProperty("HasFly")]
        public float OverrideGravity = 0f;
        [DescAttribute("起始移动方向修正", "位移")]
        public float Direction;
        [DescAttribute("移动过程中是否忽略碰撞", "位移")]
        public bool IsNoneTouch = false;

        [DescAttribute("自身转动速度(距离/每秒)", "旋转")]
        public float RotateSpeedSEC;

        [DescAttribute("持续时间(毫秒),如果是击飞此处无效,击飞时间按落地时间计算,运动时间不计算在DamageTime内", "时间")]
        [DependOnProperty("HasFly", false)]
        public int KeepTimeMS = 500;
        [DescAttribute("", "", false)]
        public float ArgsFactor = 0;

        public bool HasFly { get { return ZSpeedSEC != 0; } }
        public StartMove() { }
        public override string ToString()
        {
            return "移动速度:" + SpeedSEC + "(每秒) 持续:" + KeepTimeMS + "(毫秒)";
        }

        public object Clone()
        {
            StartMove ret = new StartMove();
            ret.SerialNumber = this.SerialNumber;
            ret.SpeedSEC = this.SpeedSEC;
            ret.SpeedAdd = this.SpeedAdd;
            ret.SpeedAcc = this.SpeedAcc;
            ret.ZSpeedSEC = this.ZSpeedSEC;
            ret.ZLimit = this.ZLimit;
            ret.OverrideGravity = this.OverrideGravity;
            ret.Direction = this.Direction;
            ret.IsNoneTouch = this.IsNoneTouch;
            ret.RotateSpeedSEC = this.RotateSpeedSEC;
            ret.KeepTimeMS = this.KeepTimeMS;
            ret.ArgsFactor = this.ArgsFactor;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutF32(this.SpeedSEC);
            output.PutF32(this.SpeedAdd);
            output.PutF32(this.SpeedAcc);
            output.PutF32(this.ZSpeedSEC);
            output.PutF32(this.ZLimit);
            output.PutF32(this.OverrideGravity);
            output.PutF32(this.Direction);
            output.PutBool(this.IsNoneTouch);
            output.PutF32(this.RotateSpeedSEC);
            output.PutS32(this.KeepTimeMS);
            output.PutF32(this.ArgsFactor);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.SpeedSEC = input.GetF32();
            this.SpeedAdd = input.GetF32();
            this.SpeedAcc = input.GetF32();
            this.ZSpeedSEC = input.GetF32();
            this.ZLimit = input.GetF32();
            this.OverrideGravity = input.GetF32();
            this.Direction = input.GetF32();
            this.IsNoneTouch = input.GetBool();
            this.RotateSpeedSEC = input.GetF32();
            this.KeepTimeMS = input.GetS32();
            this.ArgsFactor = input.GetF32();
        }
    }


    /// <summary>
    /// 道具掉落
    /// </summary>
    [MessageType(0x400F)]
    [DescAttribute("道具掉落")]
    [Expandable]
    public class DropItem : ISNData, ICloneable
    {
        /// <summary>
        /// 掉落道具模板ID
        /// </summary>
        [DescAttribute("掉落道具模板ID")]
        [TemplateIDAttribute(typeof(ItemTemplate))]
        public int ItemTemplateID;
        /// <summary>
        /// 掉落数量
        /// </summary>
        [DescAttribute("掉落数量")]
        public int DropCount = 1;
        /// <summary>
        /// 掉落道具百分比
        /// </summary>
        [DescAttribute("掉落道具百分比")]
        public float DropPercent;

        [DescAttribute("掉落位置随机范围点")]
        public float DropPosRange = 0;

        public DropItem() { }
        public DropItem(int itemID, float percent)
        {
            this.ItemTemplateID = itemID;
            this.DropPercent = percent;
        }
        public override string ToString()
        {
            return "道具掉落:" + ItemTemplateID + " 掉率:" + DropPercent + "%";
        }

        public object Clone()
        {
            DropItem ret = new DropItem();
            ret.SerialNumber = this.SerialNumber;
            ret.ItemTemplateID = this.ItemTemplateID;
            ret.DropCount = this.DropCount;
            ret.DropPercent = this.DropPercent;
            ret.DropPosRange = this.DropPosRange;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(ItemTemplateID);
            output.PutS32(DropCount);
            output.PutF32(DropPercent);
            output.PutF32(DropPosRange);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.ItemTemplateID = input.GetS32();
            this.DropCount = input.GetS32();
            this.DropPercent = input.GetF32();
            this.DropPosRange = input.GetF32();
        }
    }


    /// <summary>
    /// 道具掉落
    /// </summary>
    [MessageType(0x4013)]
    [DescAttribute("道具掉落列表")]
    [Expandable]
    public class DropItemList : ISNData, ICloneable
    {
        /// <summary>
        /// 掉落道具模板ID
        /// </summary>
        [ListAttribute(typeof(DropItem))]
        [DescAttribute("掉落道具模板列表")]
        public List<DropItem> DropItems = new List<DropItem>();

        public DropItemList() { }

        public override string ToString()
        {
            return "道具掉落:" + DropItems.Count + "个物品中的一个";
        }

        public object Clone()
        {
            DropItemList ret = new DropItemList();
            ret.SerialNumber = this.SerialNumber;
            ret.DropItems = CUtils.CloneList<DropItem>(this.DropItems);
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutList<DropItem>(DropItems, output.PutExt);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.DropItems = input.GetList<DropItem>(input.GetExt<DropItem>);
        }
    }



    [MessageType(0x4012)]
    [DescAttribute("道具使用")]
    [Expandable]
    public class UseItem : ISNData, ICloneable
    {
        /// <summary>
        /// 掉落道具模板ID
        /// </summary>
        [DescAttribute("道具模板ID")]
        [TemplateIDAttribute(typeof(ItemTemplate))]
        public int ItemTemplateID;

        public UseItem() { }
        public UseItem(int itemID)
        {
            this.ItemTemplateID = itemID;
        }
        public override string ToString()
        {
            return "使用道具:" + ItemTemplateID;
        }

        public object Clone()
        {
            UseItem ret = new UseItem();
            ret.SerialNumber = this.SerialNumber;
            ret.ItemTemplateID = this.ItemTemplateID;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(this.ItemTemplateID);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.ItemTemplateID = input.GetS32();
        }
    }

	/// <summary>
	/// 击杀击退
	/// </summary>
	[MessageType(0x4027)]
    [DescAttribute("击杀击退")]
    [Expandable]
    public class KillHitMove : ISNData, ICloneable
    {
        [DescAttribute("是否生效")]
        public bool enable = false;
        [DependOnProperty("enable")]
        [DescAttribute("受击位移计算方式", "受击")]
        public HitMoveType moveType = HitMoveType.BySenderDirection;
        public object Clone()
        {
            KillHitMove ret = new KillHitMove();
            ret.enable = this.enable;
            ret.moveType = this.moveType;
            return ret;
        }
        public override string ToString()
        {
            return "是否生效:" + enable;
        }
        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutBool(this.enable);
            output.PutEnum8(this.moveType);
            //output.PutEnum32
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.enable = input.GetBool();
            this.moveType = input.GetEnum8<HitMoveType>();
        }
    }

	[MessageType(0x4028)]
	[DescAttribute("场景增加道具")]
	[Expandable]
	public class BuffAddItem : ISNData, ICloneable
	{
		public enum PosType : byte
		{
			[DescAttribute("buff触发位置")]
			HitBuffPos = 1,
			[DescAttribute("绑定者位置")]
			BindUnitPos = 2,
			[DescAttribute("指定位置")]
			PointPos = 3,
		}
		/// </summary>
		[DescAttribute("生成类型")]
		public PosType posType = PosType.HitBuffPos;

		/// <summary>
		/// 掉落道具模板ID
		/// </summary>
		[DescAttribute("道具模板ID")]
		[TemplateIDAttribute(typeof(ItemTemplate))]
		public int ItemTemplateID;

		public bool IsPointXY { get { return posType == PosType.PointPos; } }

		//记录位置信息
		[DependOnProperty("IsPointXY")]
		[DescAttribute("指定位置X")]
		public float pointX;
		[DependOnProperty("IsPointXY")]
		[DescAttribute("指定位置Y")]
		public float pointY;

		public BuffAddItem() { }

		public override string ToString()
		{
			return "生成道具:" + ItemTemplateID;
		}

		public object Clone()
		{
			BuffAddItem ret = new BuffAddItem();
			ret.SerialNumber = this.SerialNumber;
			ret.ItemTemplateID = this.ItemTemplateID;
			ret.pointX = this.pointX;
			ret.pointY = this.pointY;
			ret.posType = this.posType;

			return ret;
		}

		public override void WriteExternal(IOutputStream output)
		{
			base.WriteExternal(output);
			output.PutS32(this.ItemTemplateID);
			output.PutEnum8(this.posType);
			output.PutF32(this.pointX);
			output.PutF32(this.pointY);
		}

		public override void ReadExternal(IInputStream input)
		{
			base.ReadExternal(input);
			this.ItemTemplateID = input.GetS32();
			this.posType = input.GetEnum8<PosType>();
			this.pointX = input.GetF32();
			this.pointY = input.GetF32();
		}
	}

	/// <summary>
	/// 单位被动触发
	/// </summary>
	[MessageType(0x4011)]
    [DescAttribute("单位被动触发")]
    [Expandable]
    public class LaunchTrigger : ISNData, ICloneable
    {
        [DescAttribute("触发器ID")]
        [TemplateIDAttribute(typeof(UnitTriggerTemplate))]
        public int TriggerTemplateID;

        [DescAttribute("开启")]
        public bool Active = true;

        public LaunchTrigger() { }
        public LaunchTrigger(int id)
        {
            this.TriggerTemplateID = id;
        }
        public override string ToString()
        {
            return "单位触发:" + TriggerTemplateID;
        }

        public object Clone()
        {
            LaunchTrigger ret = new LaunchTrigger();
            ret.SerialNumber = this.SerialNumber;
            ret.TriggerTemplateID = this.TriggerTemplateID;
            ret.Active = this.Active;
            return ret;
        }

        public override void WriteExternal(IOutputStream output)
        {
            base.WriteExternal(output);
            output.PutS32(TriggerTemplateID);
            output.PutBool(Active);
        }

        public override void ReadExternal(IInputStream input)
        {
            base.ReadExternal(input);
            this.TriggerTemplateID = input.GetS32();
            this.Active = input.GetBool();
        }
    }

    /// <summary>
    /// 携带道具
    /// </summary>
    [MessageType(0x4014)]
    [DescAttribute("单位携带道具")]
    [Expandable]
    public class InventoryItem : IExternalizable, ICloneable
    {
        [TemplateIDAttribute(typeof(ItemTemplate))]
        [DescAttribute("道具模板ID")]
        public int ItemTemplateID;

        [DescAttribute("数量ID")]
        public int Count;

        public override string ToString()
        {
            return "携带道具:" + ItemTemplateID + "x" + Count;
        }
        public object Clone()
        {
            InventoryItem ret = new InventoryItem();
            ret.ItemTemplateID = this.ItemTemplateID;
            ret.Count = this.Count;
            return ret;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutS32(ItemTemplateID);
            output.PutS32(Count);
        }

        public void ReadExternal(IInputStream input)
        {
            this.ItemTemplateID = input.GetS32();
            this.Count = input.GetS32();
        }


    }


    [MessageType(0x410A)]
    [DescAttribute("单位组阵型")]
    [Expandable]
    public class TeamFormation : IExternalizable, ICloneable
    {
        public enum Formation : byte
        {
            [DescAttribute("随机")]
            Random = 0,
            [DescAttribute("方阵")]
            Square = 1,
            [DescAttribute("以中心点放射出去的圆阵")]
            Round = 2,
            [DescAttribute("圆环")]
            Cycle = 3,
            [DescAttribute("蜂窝阵")]
            Beehive = 4,
            [DescAttribute("直线")]
            Line = 5,
        }
        public bool IsSquare { get { return this.TFormation == Formation.Square; } }


        [DescAttribute("阵型")]
        public Formation TFormation = Formation.Random;
        [DescAttribute("单位间距,间距不计算身体半径,0表示按最大身体计算")]
        public float SpacingSize = 5;

        [DescAttribute("方阵: 每行固定人数,0表示行列相等")]
        [DependOnProperty("IsSquare")]
        public int SquareEachRowCount = 0;

        public object Clone()
        {
            TeamFormation ret = new TeamFormation();
            ret.TFormation = this.TFormation;
            ret.SpacingSize = this.SpacingSize;
            ret.SquareEachRowCount = this.SquareEachRowCount;
            return ret;
        }

        public void WriteExternal(IOutputStream output)
        {
            output.PutEnum8(this.TFormation);
            output.PutF32(this.SpacingSize);
            output.PutS32(this.SquareEachRowCount);
        }

        public void ReadExternal(IInputStream input)
        {
            this.TFormation = input.GetEnum8<Formation>();
            this.SpacingSize = input.GetF32();
            this.SquareEachRowCount = input.GetS32();
        }

        public override string ToString()
        {
            return string.Format("阵型:{0}", TFormation);
        }
    }


    #endregion

    //---------------------------------------------------------------------------------//
}