using System; using System.Collections.Generic; using System.Text; using CommonAI.RTS; using CommonLang.Vector; using CommonAI.Zone; using CommonAI.Zone.Instance; using CommonAI.RTS.Manhattan; using CommonLang; using CommonLang.Log; using CommonAI.Zone.Formula; using CommonAI.ZoneClient; using CommonAI.Zone.Helper; using CommonLang.Property; using CommonAI.Zone.Attributes; using CommonAI.Zone.EventTrigger; using CommonAI.Zone.Instance.Helper; using CommonAI.Data; using static CommonAI.Zone.UnitInfo; using CommonAI.data; using CommonAI.ZoneServer.JSGModule; namespace CommonAI.Zone.Instance { abstract public partial class InstanceUnit : InstanceZoneObject { private readonly UnitInfo mInfo; private readonly DropItemGenerator mDropItems; /// /// 统计信息 /// public IUnitStatistic Statistic { get; private set; } //最后一次受伤时间 public long mLastDamageTime; //最后一次攻击事件 public long mLastHitOtherTime; //上一次处理死亡回调时间 public long mProcessDeadCallbackTime; private IFormula mFormula = TemplateManager.Factory.Formula; private IVirtualUnit mUnitVirtual; private readonly string mName; private int mForce = 0; private int mAlliesForce = 0; // 服务器对战情况下,有盟友服务器 private int mLevel = 0; private int mMoney = 0; private readonly bool mIsAttackRangeIncludeBodySize; // 当前HP private readonly RangeValue __mCurrentHP; // 当前MP private readonly RangeValue __mCurrentMP; // 当前移动速度加成 private float __mCurrentMoveSpeedSEC = 0; // 攻速 private int __mCurrentAttackSpeed = 10000; // 当前技能速度加成 private float __mSkillActionSpeedRate = 0; // 当前快速施法速度 private float mFastCastRate = 0; /// /// 当前单位是否地图阻挡 /// private bool mIntersectMap = false; /// /// 当前单位是否单位阻挡 /// private bool mIntersectObj = false; // 当前技能cd减少 private float __mCurrentSkillCdReduce; // 控制减速,韧性 private float __mCurrentControledTimeReduce; // 控制增幅 private float __mControledTimeAdd; /// 单位最后死亡时间 public long mDeadTime = 0; //游戏服标记字段,怪物死亡的时候,回传给游戏服 public int gameServerFlag = 0; //回血 public int recoverHP = 0; //随从最后一次瞬移时间 public long petTeleportTime = 0; //最后一次返回召回随从操作频繁时间 public long lastTeleportTime = 0; //绑定攻击玩家 private string mBindAttackPlayerId; //主人id private long mRemoveSelfTime; private InstanceUnit mBindMaster; //检测散开间隔 protected long mCheckAttackSpread = 0; /// /// 自动恢复计数器 /// private TimeInterval mRecoveryTime = null; public InstanceUnit(InstanceZone zone, UnitInfo uinfo, string name, int force, int level, bool is_static_block = false, int alliesForce = 0) : base(zone, is_static_block) { this.mInfo = (UnitInfo)uinfo.Clone(); this.mName = string.IsNullOrEmpty(name) ? "" : name; this.mIsAttackRangeIncludeBodySize = zone.Templates.CFG.OBJECT_ATTACK_RANGE_INCLUDE_BODYSIZE; this.mForce = force; this.mAlliesForce = alliesForce; this.mLevel = level; this.mIntersectMap = true; this.mIntersectObj = !Templates.CFG.OBJECT_NONE_TOUCH && (mInfo.BodySize > 0); this.mSyncInfo = new SyncUnitInfo(zone.IsHalfSync); this.mSyncInfo.Name = mName; this.mSyncInfo.TemplateID = mInfo.ID; this.__mCurrentHP = new RangeValue(mInfo.HealthPoint, 0, mInfo.HealthPoint); this.__mCurrentMP = new RangeValue(mInfo.ManaPoint, 0, mInfo.ManaPoint); this.__mCurrentMoveSpeedSEC = mInfo.MoveSpeedSEC; this.mDropItems = new DropItemGenerator(uinfo.DropItemsSet); this.mTriggerHelper = new UnitTriggerHelper(this); if (mInfo.IdleRecover && mInfo.RecoveryIntervalMS > 0) { this.mRecoveryTime = new TimeInterval(mInfo.RecoveryIntervalMS); this.recoverHP = Info.HealthRecoveryPoint; } this.InitTimeLines(); this.InitBagSlots(); this.InitSkills(mInfo.BaseSkillID, mInfo.Skills.ToArray()); this.Statistic = this.CreateUnitStatistic(); this.mUnitVirtual = TemplateManager.Factory.CreateUnitVirtual(this); } public override bool IsNeedProcessDead() { return this.mProcessDeadTime == 0; } protected override void Disposing() { //if(this.IsMonster && (this.mProcessDeadTime == 0 || this.CurrentHP > 0 || this.mProcessDeadTime == 0)) //{ // log.Info("单位Disposing:" + this.Parent.UUID + ", " + this.Parent.GetSceneID() + ", " + this.ID + ", " + this.CurrentHP + "," + // this.IsMonster + ", " + mProcessDeadTime); //} if (mUnitVirtual != null) { mUnitVirtual.OnDispose(this); } base.Disposing(); this.clearBindEvents(); this.clearSkills(); this.clearBuffs(); this.clearItemSlots(); this.clearTriggers(); this.mNoneBlockTimeMS.Disposing(); this.mStunTimeMS.Disposing(); this.mInvisibleTimeMS.Disposing(); this.mInvincibleTimeMS.Disposing(); this.mSilentTimeMS.Disposing(); this.mCannotMoveTimeMS.Disposing(); this.mIgnoreControl.Disposing(); foreach (MultiTimeLine timeLine in mMultiTimeLineGroup) { timeLine.Disposing(); } this.mMultiTimeLineGroup.Clear(); this.mMultiTimeLineGroup = null; this.mMultiTimeLineSync = null; } protected override void onAdded(bool pointLv) { if (this.mUnitVirtual != null) { this.mUnitVirtual.OnInit(this, pointLv); if (this.mRecoveryTime != null && this.Info.UType == UnitType.TYPE_MONSTER) { this.recoverHP = Info.HealthRecoveryPoint == 0 ? (int)(this.MaxHP * 0.005f) : Info.HealthRecoveryPoint; } } this.mSyncInfo.ObjectID = base.ID; this.mSyncInfo.IsTouchObj = this.mIntersectObj; this.mSyncInfo.IsTouchMap = this.mIntersectMap; this.mSyncInfo.IsStaticBlockable = this.IsStaticBlockable; this.mSyncInfo.fateType = (byte)this.Virtual.GetUnitFateType(); if (mInfo.SpawnTimeMS > 0) { this.SetInvincibleTimeMS(mInfo.SpawnTimeMS); } if (this.mInfo.InventoryList != null) { foreach (InventoryItem item in mInfo.InventoryList) { ItemTemplate temp = Templates.getItem(item.ItemTemplateID); if (temp != null) { AddItemToEmptyInventory(temp, item.Count); } } } if (mInfo.Events != null) { foreach (int evt_id in mInfo.Events) { this.BindUnitEvent(evt_id); } } if (mOnAdded != null) { mOnAdded.Invoke(this); } } protected override void onRemoved() { if (mOnRemoved != null) { mOnRemoved.Invoke(this); } foreach (UnitEventTriggerCollection uc in mBindEvents.Values) { uc.Dispose(); } mBindEvents.Clear(); if (Info.RemovedEffect != null) { Parent.queueEvent(new AddEffectEvent(0/*this.ID*/, X, Y, Direction, Info.RemovedEffect)); } } public void BindAttackPlayer(string playerId) { this.mBindAttackPlayerId = playerId; } public string GetAttackPlayer() { return this.mBindAttackPlayerId; } public void BindMasterId(int masterId) { InstanceUnit master = this.Parent.getUnitByID(masterId); if (master == null) { log.Error("BindMasterId找不到单位:" + this.Parent.GetSceneID() + ", " + masterId); return; } if (this.mBindMaster != null) { log.Error("BindMasterId重复:" + this.Info.ID + ", " + master.PlayerUUID); return; } this.mBindMaster = master; } //-------------------------------------------------------------------------- /// /// 扩展功能绑定接口 /// public IVirtualUnit Virtual { get { return mUnitVirtual; } } /// /// 扩展数据 /// public virtual IUnitProperties Properties { get { return mInfo.Properties; } } //-------------------------------------------------------------------------- //-------------------------------------------------------------------------- public UnitInfo Info { get { return mInfo; } } public string Name { get { return mName; } } public string Alias { get { return mSyncInfo.Alias; } set { if (value != null) { mSyncInfo.Alias = value; } } } public int Force { get { return mForce; } protected set { mForce = value; } } public int AlliesForce { get { return mAlliesForce; } protected set { mAlliesForce = value; } } public float MoveSpeedSEC { get { return __mCurrentMoveSpeedSEC; } } public float SkillCdReduce { get { return __mCurrentSkillCdReduce; } } public float ControledTimeReduce { get { return __mCurrentControledTimeReduce; } } public float ControledTimeAdd { get { return __mControledTimeAdd; } } public float FastCastRate { get { return mFastCastRate; } } public override float BodyBlockSize { get { return mInfo.BodySize; } } public override float BodyHitSize { get { return mInfo.BodyHitSize; } } public override float BodyHeight { get { return mInfo.BodyHeight; } } public override float Weight { get { return mInfo.Weight; } } public override bool IntersectMap { get { return mIntersectMap; } } public override bool IntersectObj { get { return mIntersectObj && IsVisible; } } public override bool Moveable { get { return mInfo.IsMoveable; } } public override bool ClientVisible { get { return true; } } public override bool SyncPos { get { return true; } } public override bool IsDead() { return __mCurrentHP.Value <= 0; } public int DeadCount { get { return Statistic.DeadCount; } } public long LastDeadTimeMS { get { return mDeadTime; } } public bool CanWhiplashDeadBody { get { return Templates.CFG.UNIT_CAN_WHIPLASH_BODY && (LastDeadTimeMS + mInfo.DeadTimeMS > Parent.PassTimeMS); } } /// /// 是否为玩家 /// virtual public bool IsPlayer { get { return false; } } virtual public bool IsMonster { get { return false; } } virtual public bool IsPet { get { return false; } } //玩家或者宠物合集 virtual public bool IsPlayerUnit { get { return false; } } virtual public void OnUnitDead() { } /// /// 是否中立 /// virtual public bool IsNature { get { return false; } } /// /// 此单位是否能被攻击并且活着 /// virtual public bool IsActive { get { return (__mCurrentHP.Value > 0) && (base.Enable)/*&& !IsInvincible && IsVisible*/; } } /// /// 此单位是否可以被打到,包括鞭尸 /// virtual public bool IsAttackable { get { return (base.Enable) /*&& !IsInvincible && IsVisible*/; } } /// /// 此单位是否无技能 /// public bool IsNoneSkill { get { foreach (SkillState st in mSkillStatus.Skills) { if (st.LaunchSkill.AutoLaunch) return false; } return true; } } /// /// 技能可产生位移,或者多段由服务器决定 /// virtual public bool IsSkillControllableByServer { get { return true; } } /// /// 单位是否可控 /// virtual public bool IsControllable { get { return IsActive && !IsStun && CurrentActionStatus != UnitActionStatus.Damage; } } /// /// 用于显示的,单位横向数据 /// public ZoneClient.IUnitVisibleData VisibleInfo { get { return mSyncInfo.VisibleInfo; } } //-----------------------------------------------------------------------------------------------------// protected virtual IUnitStatistic CreateUnitStatistic() { return new UnitStatistic(this); } //-----------------------------------------------------------------------------------------------------// #region __StateMachine__ private bool is_init = false; private State current_state = null; private State next_state; private Queue next_state_queue = new Queue(); /// /// 当前状态机 /// public State CurrentState { get { return current_state; } } //状态机额外数据 public long chuangongTime = 0; /// /// 下一个状态 /// protected State NextState { get { return next_state; } } /// /// 死亡状态可能的状态机,一般配合使用 /// public bool IsStateDead { get { if ((current_state is StateDead) || (current_state is StateDamage) || (current_state is StateDeadFuck) || (current_state is StateDeadFuckFuck) || (current_state is StateRebirth) || (next_state is StateDead) || (next_state is StateDamage) || (next_state is StateDeadFuck) || (next_state is StateDeadFuckFuck) || (next_state is StateRebirth)) { return true; } return false; } } public void queueState(State s) { next_state_queue.Enqueue(s); } public bool TryEnqueueIdleState() { if (next_state_queue.Count > 0) { return false; } next_state_queue.Enqueue(new StateIdle(this)); return true; } public void queueCurrentState(State s) { if (current_state != null) { if (!changeState(s)) { State.StateStopHandler onstop = new State.StateStopHandler((obj, st) => { changeState(s); }); current_state.AddStopOnce(onstop); } } else { changeState(s); } } public bool changeState(State s, bool force = false) { if (s == current_state) { return true; } if (s == next_state) { return true; } if (s.unit != this) { throw new Exception("State is not Owner unit : " + s); } if (current_state == null || current_state.onBlock(s) || force) { if (next_state == null || next_state.onBlock(s) || force) { if (next_state != null) { next_state.stop(); } onNewStateBeginChange(current_state, ref s); next_state = s; return true; } } return false; } private void updateState() { //绑定主人类怪物,脱战或者主人移除自杀 if (mRemoveSelfTime == 0) { if (this.mBindMaster != null && (!this.mBindMaster.IsActive || this.mBindMaster.Virtual.GetBattleStatus() <= BattleStatus.ReadyBattle)) { //添加移除逻辑 mRemoveSelfTime = CommonLang.CUtils.localTimeMS + 3000; } } else if (mRemoveSelfTime < CommonLang.CUtils.localTimeMS) { this.kill(null, false); } if (IsDead() && (!IsStateDead)) { changeState(new StateDead(this, this, false), true); queueEvent(new UnitDeadEvent(ID, this.ID, false, mInfo.RebirthTimeMS)); if(this.mProcessDeadCallbackTime < CommonLang.CUtils.localTimeMS) { log.Warn("手动调用死亡逻辑2:" + this.PlayerUUID + ", " + this.mZone.GetSceneID() + ", templateID:" + this.Info.ID + ", state: " + this.current_state); Parent.cb_unitDeadCallBack(this, this); } } else if (next_state == null && next_state_queue.Count > 0) { // 尝试从队列中取一个状态机,有机会就执行 // while (next_state_queue.Count > 0) { State queued_state = next_state_queue.Peek(); if (changeState(queued_state)) { next_state_queue.Dequeue(); } else { break; } } } if (next_state != null && next_state != current_state) { State old_state = current_state; if (old_state != null) { old_state.stop(); } this.current_state = next_state; this.next_state = null; this.current_state.start(); this.onStateChanged(old_state, current_state); if (mOnStateChanged != null) { mOnStateChanged.Invoke(this, old_state, current_state); } } if (current_state != null) { current_state.update(); } UpdateTimeLines(Parent.UpdateIntervalMS); } #endregion protected virtual void onUpdateRecover() { //自动恢复 if (this.recoverHP > 0 && mRecoveryTime != null && this.CurrentHP < this.MaxHP && CurrentActionStatus != UnitActionStatus.Damage && this.Virtual.GetBattleStatus() == BattleStatus.None && !IsDead()) { if (mRecoveryTime.Update(Parent.UpdateIntervalMS)) { AddHP(this.recoverHP, null); //AddMP(Info.ManaRecoveryPoint); } } } override protected void onUpdate(bool slowRefresh) { if (!is_init) { is_init = true; onInit(); } if (!IsPaused) { updatePhysical(); updateState(); onUpdateRecover(); updateSkills(); updateBuffs(); updateTriggers(slowRefresh); updateItems(); if (mOnUpdate != null) { mOnUpdate.Invoke(this); } } updateSyncFields(); updateSyncSkillActives(); } internal void doAction(ObjectAction act) { this.onAction(act); if (mOnHandleObjectAction != null) { mOnHandleObjectAction.Invoke(this, act); } } private void doRebirth(int max_hp, int max_mp) { next_state_queue.Clear(); if (this.Moveable) { this.mIntersectObj = !Templates.CFG.OBJECT_NONE_TOUCH && (mInfo.BodySize > 0); } else { this.mIntersectObj = (mInfo.BodySize > 0); } if (max_hp == 0) max_hp = this.MaxHP; if (max_mp == 0) max_mp = this.MaxMP; if (max_hp != 0) this.__mCurrentHP.SetValue(max_hp); if (max_mp != 0) this.__mCurrentMP.SetValue(max_mp); syncFields(UnitFieldChangedEvent.MASK_HP | UnitFieldChangedEvent.MASK_MP); Parent.cb_unitRebirthCallBack(this); queueEvent(new UnitRebirthEvent(ID)); } public void updateContinueKills() { if (this.Statistic.continueKills < short.MaxValue) { ++this.Statistic.continueKills; } UnitContinueKillValue temp = new UnitContinueKillValue(this.ID, this.Statistic.continueKills); queueEvent(temp); } public void resetContinueKills() { this.Statistic.continueKills = 0; } private void doActivated() { next_state_queue.Clear(); if (this.Moveable) { this.mIntersectObj = !Templates.CFG.OBJECT_NONE_TOUCH && (mInfo.BodySize > 0); } else { this.mIntersectObj = (mInfo.BodySize > 0); } this.addTriggers(mInfo.Triggers); Parent.cb_unitActivatedCallBack(this); } internal void doDead(InstanceUnit killer) { if (killer != null) { this.Statistic.LogDead(killer.Statistic); } this.mIntersectObj = false; this.clearBuffs(); //吴永辉:5v5人物死亡后会重置技能CD 无论在哪个地图死亡都不能重置 你可能要指给黄鱼 bugid=2748 //this.ClearAllSkillCD(); this.resetAI(); } //------------------------------------------------------------------------------------------------------// internal void callback_onActivated(InstanceZone zone) { if (this.mOnActivated != null) this.mOnActivated.Invoke(this); } internal void callback_onAttack(InstanceZone zone, InstanceUnit target, int reduceHP, AttackSource source) { if (this.mOnAttack != null) this.mOnAttack(this, target, reduceHP, source); } internal void callback_onDamage(InstanceZone zone, InstanceUnit attacker, int reduceHP, AttackSource source) { if (this.mOnDamage != null) this.mOnDamage.Invoke(this, attacker, reduceHP, source); } public long mProcessDeadTime = 0; internal void callback_onDead(InstanceZone zone, InstanceUnit attacker) { this.mProcessDeadTime = CommonLang.TimeUtil.GetTimestampMS(); this.OnUnitDead(); if (this.mOnDead != null) this.mOnDead.Invoke(this, attacker); } internal void callback_onRebirth(InstanceZone zone) { if (this.mOnRebirth != null) this.mOnRebirth.Invoke(this); } internal void callback_onGotInstanceItem(InstanceZone zone, InstanceItem item) { if (this.mOnGotInstanceItem != null) this.mOnGotInstanceItem.Invoke(this, item); } internal void callback_onGotInventoryItem(InstanceZone zone, ItemTemplate item) { if (this.mOnGotInventoryItem != null) this.mOnGotInventoryItem.Invoke(this, item); } internal void callback_onLostInventoryItem(InstanceZone zone, ItemTemplate item) { if (this.mOnLostInventoryItem != null) this.mOnLostInventoryItem.Invoke(this, item); } internal void callback_onUseItem(InstanceZone zone, ItemTemplate item, InstanceUnit item_creater) { if (this.mOnUseItem != null) this.mOnUseItem.Invoke(this, item, item_creater); } internal void callback_onGotBuff(InstanceZone zone, InstanceUnit.BuffState buff) { if (this.mOnGotBuff != null) this.mOnGotBuff.Invoke(this, buff); } internal void callback_onLostBuff(InstanceZone zone, InstanceUnit.BuffState buff) { if (this.mOnLostBuff != null) this.mOnLostBuff.Invoke(this, buff); } //-----------------------------------------------------------------------------------------------------// private void onInit() { syncFields(UnitFieldChangedEvent.MASK_ALL); if (mInfo.SpawnTimeMS > 0) { changeState(new StateSpawn(this, mInfo.SpawnTimeMS)); } else { changeState(new StateIdle(this)); doActivated(); } } #region _Overrideable_ /// /// 收到协议 /// /// virtual protected void onAction(ObjectAction act) { } /// /// 新的状态将要切换时回调 /// virtual protected void onNewStateBeginChange(State old_state, ref State new_state) { } /// /// 状态已切换时回调 /// /// /// virtual protected void onStateChanged(State old_state, State state) { } // 被攻击时回调 virtual protected void onDamaged(InstanceUnit attacker, AttackSource source, int reduceHP) { } virtual protected void onDead(InstanceUnit killer) { } #endregion /// /// 单位被攻击核心函数,里面处理受击状态,死亡状态 /// /// /// virtual protected internal void doHitAttack(InstanceUnit attacker, AttackSource source) { if (IsDead()) { //死亡后鞭尸// if (Moveable && CanWhiplashDeadBody) { source.Begin(this); changeState(new StateDamage(this, source, attacker)); } doHitAttackEndEffect(attacker, source); return; } else { if (current_state is StateDamage) { StateDamage state_damage = current_state as StateDamage; if (state_damage.IsDamageProtect) { return; } } // TODO HP // source.Begin(this); int reduceHP = mFormula.OnHit(attacker, source, this); // 统计 // reduceHP = this.AddHP(-reduceHP, attacker, !source.OutSendEvent, source); //this.Statistic.LogDamage(attacker.Statistic, reduceHP); AttackProp attack = source.Attack; // Post Event // if (source.OutSendEvent) { UnitHitEvent evt = new UnitHitEvent(ID); evt.senderId = attacker.ID; evt.senderMasterId = attacker.Virtual.GetMasterID(); evt.hitMasterId = this.Virtual.GetMasterID(); evt.hp = reduceHP; evt.isDead = IsDead(); evt.isCritical = source.Attack.MaskMustCritical; evt.effect = source.OutHitEffect; evt.SourceAttack = source.Attack; evt.client_state = source.OutClientState; //if (source != null && attacker != null && source.FromSkillType == XmdsSkillType.cardSkill && this.IsMonster && attacker.IsMonster) //{ evt.InViewForceSend = true; //} queueEvent(evt); } if (reduceHP > 0) { source.HasHitted = true; onDamaged(attacker, source, reduceHP); Parent.cb_unitDamageCallBack(this, attacker, reduceHP, source); if (IsDead()) { //doDeadProcess(attacker, source); return; } } // 非免控,才会有受击 if (!this.IsIgnoreControl && source.OutIsDamage && Moveable) { changeState(new StateDamage(this, source, attacker)); } doHitAttackEndEffect(attacker, source); return; } } /** 执行死亡处理逻辑 */ private void doDeadProcess(InstanceUnit attacker, AttackSource source) { try { onDead(attacker); Parent.cb_unitDeadCallBack(this, attacker); if (source == null) { queueEvent(new UnitDeadEvent(ID, attacker == null ? 0 : attacker.ID, false, mInfo.RebirthTimeMS)); changeState(new StateDead(this, attacker, true)); } else { queueEvent(new UnitDeadEvent(ID, attacker.ID, source.OutIsCrush, mInfo.RebirthTimeMS)); // 被击碎,秒杀 // if (source.OutIsCrush) { Parent.queueEvent(new AddEffectEvent(this.ID, X, Y, Direction, source.Attack.CrushEffect)); changeState(new StateDead(this, attacker, true)); } else { if (!Moveable) { changeState(new StateDead(this, attacker)); } else if (source.OutHasKnockDown || source.OutHasFly) { if (!changeState(new StateDamage(this, source, attacker))) { changeState(new StateDead(this, attacker)); } } else { changeState(new StateDead(this, attacker)); } } doHitAttackEndEffect(attacker, source); } } catch (Exception e) { log.Warn("doDeadProcess catch:" + this.Info.ID + ", AttackId: " + (attacker == null ? -1 : attacker.Info.ID), e); } } /// /// 死亡后自爆或者触发法术 /// /// /// private void doHitAttackEndEffect(InstanceUnit attacker, AttackSource source) { if (source.Attack.Buff != null) { AddBuff(source.Attack.Buff, attacker); } if (source.Attack.Spell != null) { Parent.attackLaunchSpell(source.FromSkillType, attacker, this, source); } } // 单位获取道具 protected virtual bool tryGotItem(InstanceItem item, out bool removeItem) { if (!Parent.IsVisibleAOI(this, item)) { removeItem = false; return false; } if (item.Info.DropMoneyMin > 0 && item.Info.DropMoneyMax > 0) { int min = Math.Min(item.Info.DropMoneyMin, item.Info.DropMoneyMax); int max = Math.Max(item.Info.DropMoneyMin, item.Info.DropMoneyMax); int money = RandomN.Next(min, max + 1); this.CurrentMoney += money; removeItem = item.Info.RemoveOnFinishPick; return true; } if (item.Info.GotOnUse) { // 获取后立即使用 // if (UseItem(item.Info, item.ItemCreater)) { removeItem = item.Info.RemoveOnFinishPick; return true; } } else { // 获取后进背包 // if (AddItemToEmptyInventory(item.Info) > 0) { removeItem = item.Info.RemoveOnFinishPick; return true; } } removeItem = false; return false; } internal bool doGotInstanceItem(InstanceItem item, out bool removeItem, out int pickTimes) { pickTimes = 0; var ret = tryGotItem(item, out removeItem); if (ret) { pickTimes = Parent.cb_unitGotInstanceItemCallBack(this, item); if (item.Info.GotEffect != null) { queueEvent(new UnitEffectEvent(ID, item.Info.GotEffect)); } } return ret; } internal void doGotInventoryItem(InventorySlot slot, ItemTemplate item, int index, int count) { mFormula.OnGotInventoryItem(this, item); queueEvent(new UnitSyncInventoryItemEvent(ID, item.ID, index, slot.Count)); Parent.cb_unitGotInventoryItemCallBack(this, item, count); // 添加装备Buff // if (item.EquipBuffs != null) { foreach (LaunchBuff buff in item.EquipBuffs) { AddBuff(buff, this, true); } } } internal void doLostInventoryItem(InventorySlot slot, ItemTemplate item, int index, int count) { mFormula.OnLostInventoryItem(this, item); queueEvent(new UnitSyncInventoryItemEvent(ID, item.ID, index, slot.Count)); Parent.cb_unitLostInventoryItemCallBack(this, item, count); // 移除装备Buff // if (item.EquipBuffs != null) { foreach (LaunchBuff buff in item.EquipBuffs) { removeBuff(buff.BuffID); } } } // 单位获取道具 internal void doGotBuff(InstanceUnit.BuffState buff) { Parent.cb_unitGotBuffCallBack(this, buff); } internal void doLostBuff(InstanceUnit.BuffState buff) { Parent.cb_unitLostBuffCallBack(this, buff); } public bool UseItem(int itemTemplateID, InstanceUnit item_creater = null) { ItemTemplate item = Templates.getItem(itemTemplateID); if (item != null) { return UseItem(item, item_creater); } return false; } public bool UseItem(ItemTemplate item, InstanceUnit item_creater = null) { if (item_creater == null) { item_creater = this; } if (tryUseItem(item, item_creater)) { beginUseItem(item); // 如果关键帧绑定特效 if (item.UseEffect != null) { queueEvent(new UnitEffectEvent(ID, item.UseEffect)); } // 如果关键帧绑定释放法术 if (item.UseSpell != null) { Parent.unitLaunchSpell(XmdsSkillType.none, item_creater, item.UseSpell, X, Y); } if (item.UseSummon != null) { Parent.unitSummonUnit(item_creater, item.UseSummon); } // 如果关键帧绑定自己释放BUFF if (item.UseBuffs != null) { foreach (LaunchBuff buff in item.UseBuffs) { this.AddBuff(buff, item_creater); } } mFormula.OnUseItem(this, item, item_creater); queueEvent(new UnitUseItemEvent(ID, item.ID)); Parent.cb_unitUseItemCallBack(this, item, item_creater); Statistic.LogUseItem(item); return true; } return false; } //-----------------------------------------------------------------------------------------------------// /// /// 有伤害源的扣血 /// /// /// /// /// /// force: 忽略无敌效果,强制扣血 public int ReduceHP(int hp, InstanceUnit attacker, AttackSource source, bool sendHit = true, UnitHitEvent hitMessage = null, bool IngoreHealEffect = false, DamageSource dmgSrc = DamageSource.Def, bool force = false) { //1.目标无敌,不扣血 if (IsDead() || hp == 0 || (hp > 0 && this.IsInvincible && !force)) { return 0; } //2. 针对有发送者的伤害来源 if (attacker != null) { if (hp < 0 && !IngoreHealEffect) { int healedEffect = this.Virtual.GetHealedEffect(); int healEffect = (attacker.Virtual.GetUnitPro() == XmdsUnitPro.Priest) ? attacker.Virtual.GetHealEffect() : 0; if (healedEffect != 0 || healEffect != 0) { hp = CUtils.CastInt(hp * (1 + healEffect * 0.0001) * (1 + healedEffect * 0.0001)); } } if (hp < 0) { //传递加血事件 hp = attacker.Virtual.DispatchAddOtherHPEvent(hp, this, source); //2 道灵对宠物加血,需要有个系数 if (attacker.IsPlayer && this.IsPet && attacker.Virtual.GetUnitPro() == XmdsUnitPro.Priest) { hp = (int)(hp * XmdsConstConfig.PET_HEALD_RATIO); } } else { //传递单位-被其他人扣血 hp = this.Virtual.DispatchAddOtherHPEvent(hp, attacker, source); if (this.IsPlayer && attacker.IsMonster) { //2.3 如果玩家受伤,伤害来源存在且为怪物,宠物可以分担伤害 InstanceUnit petUnit = this.Virtual.GetPetUnit(); if (petUnit != null) { int petShareDmg = Math.Max(1, (int)(hp * XmdsConstConfig.PET_SHARE_MASTERDMG_RATIO)); hp = Math.Max(1, hp - petShareDmg); petUnit.PetShareDamage(petShareDmg, this); } } } this.Statistic.LogDamage(attacker.Statistic, hp); } this.DoAddHP(-hp); if (attacker != null && sendHit) { if (hitMessage == null) { hitMessage = new UnitHitEvent(); } hitMessage.object_id = this.ID; hitMessage.senderId = attacker == null ? 0 : attacker.ID; hitMessage.senderMasterId = attacker == null ? 0 : attacker.Virtual.GetMasterID(); hitMessage.hitMasterId = this.Virtual.GetMasterID(); hitMessage.hp = hp; hitMessage.isSpecialHit = dmgSrc != DamageSource.Def; hitMessage.dmgSrc = dmgSrc; hitMessage.isDead = IsDead(); queueEvent(hitMessage); } //if (IsDead()) //{ // changeState(new StateDead(this, attacker, false)); // Parent.cb_unitDeadCallBack(this, attacker); // queueEvent(new UnitDeadEvent(ID, attacker == null ? 0 : attacker.ID, false, mInfo.RebirthTimeMS)); //} if (hp > 0 && this.IsDead()) { this.doDeadProcess(attacker, source); } return hp; } public void AddMP(int mp, InstanceUnit sender, bool sendMsg = true, bool force = false, AttackSource source = null) { if (mp == 0 || this.MaxMP <= 0) { return; } //传递扣定力事件 if (mp < 0 && sender != null) { int finalHP = mp; sender.Virtual.DispatchAddMPEvent(mp, this, out finalHP, source); mp = finalHP; } this.DoAddMP(mp, force); } public void removeFromParent() { Parent.RemoveObjectByID(ID); } public void kill(InstanceUnit killer = null, bool sendHit = true, UnitHitEvent hitMessage = null) { ReduceHP(this.CurrentHP, killer, null, sendHit, hitMessage, true); } virtual protected void onResetAI() { this.doSomething(); } public void resetAI() { next_state_queue.Clear(); onResetAI(); } public override void faceTo(float d) { if (Info.IsTurnable) { base.faceTo(d); } } public override void faceTo(float x, float y) { if (Info.IsTurnable) { base.faceTo(x, y); } } /// /// 瞬移 /// /// /// public void transport(float x, float y, bool setDirction = false, float direction = 0) { this.SetActionStatus(UnitActionStatus.Transport); this.changeState(new StateTransport(this), true); setPos(x, y); if (setDirction) { this.Direction = direction; } SendForceSync(); if (mOnRegionTransport != null) { mOnRegionTransport.Invoke(); } } //-----------------------------------------------------------------------------------------------------// public virtual void doSomething() { startIdle(); } /// /// 单位待机 /// public bool startIdle(bool force = false) { if (current_state is StateIdle) { } else { return changeState(new StateIdle(this), force); } return false; } /// /// 直接复活 /// public bool startRebirth(int max_hp = 0, int max_mp = 0) { if (IsDead()) { //this.DeadHide = false; this.mProcessDeadCallbackTime = 0; return changeState(new StateRebirth(this, max_hp, max_mp)); } return false; } /// /// 单位移动 /// /// /// public bool startMoveTo(float x, float y) { return changeState(new StateMove(this, x, y)); } /// /// 调整射击位置 /// /// /// /// public bool startAdjustLaunchSkill(SkillTemplate expect_skill, InstanceUnit target, Predicate onEndAction) { if (expect_skill.AttackKeepRange > 0 && expect_skill.AttackRange > expect_skill.AttackKeepRange) { float keep_range = GetSkillAttackRange(expect_skill.AttackKeepRange) + target.BodyBlockSize; float distance = MathVector.getDistance(this.X, this.Y, target.X, target.Y); if (distance < keep_range) { var half = (expect_skill.AttackRange - expect_skill.AttackKeepRange) / 2; var md = (keep_range - distance) + (float)(RandomN.NextDouble() * half); var target_pos = new Vector2(this.X, this.Y); var rd = (CMath.PI_DIV_2 / 4); var target_direction = this.Direction + CMath.PI_F - (rd / 2 + (float)(RandomN.NextDouble() * rd)); MathVector.movePolar(target_pos, target_direction, md); if (!Parent.TryTouchMap(this, target_pos.X, target_pos.Y)) { var back_pos = new Vector2(this.X, this.Y); MathVector.movePolar(back_pos, target_direction, this.BodyBlockSize); if (!Parent.TryTouchMap(this, back_pos.X, back_pos.Y)) { var move = new StateMove(this, target_pos.X, target_pos.Y); move.MinStepCheckCount = 0; move.StopOnTouchMap = true; move.EndMoveAction = onEndAction; return this.changeState(move); } } } } return false; } /// /// 单位移动 /// /// public bool startFollowTo(InstanceZoneObject obj) { return changeState(new StateFollowObject(this, obj)); } /// /// 单位逃跑 /// /// /// public bool startEscape(int timeMS, float distance = 0) { return changeState(new StateEscape(this, timeMS, distance)); } /// /// 和自身交互(搓炉石) /// /// /// /// public StatePickObject startPickProgressSelf(int timeMS, StatePickObject.OnPickDone done, string status = null) { StatePickObject picking = new StatePickObject(this, this, timeMS, status, done); changeState(picking); return picking; } /// /// 和目标交互 /// /// /// /// /// /// public StatePickObject startPickProgressObject(InstanceZoneObject item, int timeMS, StatePickObject.OnPickDone done, string status = null) { StatePickObject picking = new StatePickObject(this, item, timeMS, status, done); changeState(picking); return picking; } //----------------------------------------------------------------------------------------------------------------- #region _SKILL_ //----------------------------------------------------------------------------------------------------------------- public float GetSkillAttackRange(SkillTemplate skill, InstanceUnit targetUnit) { return mIsAttackRangeIncludeBodySize ? (BodyBlockSize + skill.AttackRange + targetUnit.BodyBlockSize) : skill.AttackRange; //return mIsAttackRangeIncludeBodySize ? (BodyBlockSize + skill.AttackRange) : skill.AttackRange; } public float GetSkillAttackRange(SkillTemplate skill) { return mIsAttackRangeIncludeBodySize ? (BodyBlockSize + skill.AttackRange) : skill.AttackRange; } public float GetSkillAttackRange(float range) { return (mIsAttackRangeIncludeBodySize ? BodyBlockSize + range : range); } /// /// 判断当前目标在攻击范围内 /// /// /// /// public bool IsTargetInSkillRange(SkillTemplate skill, InstanceUnit unit) { float rg = GetSkillAttackRange(skill); float dr = skill.AttackAngle / 2; if (Collider.Object_HitBody_TouchFan(unit, X, Y, rg, Direction - dr, Direction + dr)) { return true; } return false; } [Obsolete] public List getSkillAttackableTargets(SkillTemplate skill, AttackReason reason) { float rg = GetSkillAttackRange(skill); List list = Parent.getObjectsRoundRange(Collider.Object_HitBody_TouchRound, X, Y, rg, this.AoiStatus); Parent.getAttackableUnits(this, list, skill.ExpectTarget, reason, skill); return list; } public void getSkillAttackableTargets(SkillTemplate skill, List list, AttackReason reason) { float rg = GetSkillAttackRange(skill); Parent.getObjectsRoundRange(Collider.Object_HitBody_TouchRound, X, Y, rg, list, this.AoiStatus); Parent.getAttackableUnits(this, list, skill.ExpectTarget, reason, skill); } /// /// 获得可用的技能 /// /// /// public SkillState getAvailableSkill(SkillTemplate.CastTarget expect) { for (int si = mAllSkills.Count - 1; si >= 0; --si) { SkillTemplate st = mAllSkills[si]; if (st.ExpectTarget == expect) { SkillState sst = mSkillStatus.Get(st.GetID()); if (sst.IsActive && sst.IsDone) { return sst; } } } return null; } /** 获取当前可自动释放的技能 */ public SkillState getAvailableAutoLaunchSkill(SkillTemplate.CastTarget expect) { for (int si = mAllSkills.Count - 1; si >= 0; --si) { SkillTemplate st = mAllSkills[si]; if (st.ExpectTarget == expect) { SkillState sst = mSkillStatus.Get(st.GetID()); if (sst.IsActive && sst.IsDone && sst.LaunchSkill.AutoLaunch && sst.CanAutoLaunch()) { return sst; } } } return null; } /// /// 获取当前最适合攻击的目标 /// /// /// /// /// public InstanceUnit getSkillAttackableFirstTarget(SkillTemplate skill, AttackReason reason, ref bool directionChange) { using (var list = ListObjectPool.AllocAutoRelease()) { getSkillAttackableTargets(skill, list, reason); if (list.Count > 0) { float rg = GetSkillAttackRange(skill); // 检测攻击范围内的单位 // float dr = skill.AttackAngle / 2; for (int i = 0; i < list.Count; i++) { InstanceUnit u = list[i]; if (Collider.Object_HitBody_TouchFan(u, X, Y, rg, Direction - dr, Direction + dr)) { directionChange = false; return u; } } // 优先当前朝向的目标 // dr = CMath.PI_DIV_2; directionChange = true; for (int i = 0; i < list.Count; i++) { InstanceUnit u = list[i]; if (Collider.Object_HitBody_TouchFan(u, X, Y, rg, Direction - dr, Direction + dr)) { return u; } } // 最后选取最近的目标 // InstanceUnit min = null; float min_len = float.MaxValue; for (int i = 0; i < list.Count; i++) { InstanceUnit u = list[i]; float len = MathVector.getDistanceSquare(u.X, u.Y, X, Y); if (min_len > len) { min_len = len; min = u; } } return min; } } return null; } public struct LaunchSkillParam { public uint TargetUnitID; public Vector2 SpellTargetPos; public bool AutoFocusNearTarget; public bool LaunchSetDirection; // 释放时是否重新设置方向 public float direction; public bool IsAutoLaunch; // 是否自动战斗时释放 public bool IsInGuard; // 是否是自动战斗 public LaunchSkillParam(uint targetUnitID = 0, Vector2 target_pos = null, bool autoFocusNearTarget = false, bool LaunchSetDirection = false, float direction = 0, bool IsAutoLaunch = false, bool IsInGuard = false) { this.TargetUnitID = targetUnitID; this.SpellTargetPos = target_pos; this.AutoFocusNearTarget = autoFocusNearTarget; this.LaunchSetDirection = LaunchSetDirection; this.direction = direction; this.IsAutoLaunch = IsAutoLaunch; this.IsInGuard = IsInGuard; } } public StateSkill launchSkill(SkillState ss, LaunchSkillParam param) { if (ss == null) return null; StateSkill current = CurrentState as StateSkill; if (current != null && current.SkillData.IsManuallyCancelable) { //如果当前技能为手动取消// if (current.Skill.ID == ss.ID) { //判断停止当前技能// if (current.IsCancelableBySkill) { current.block(); } } //手动取消技能禁止其他技能打断// return null; } if (ss.TryLaunch() && mFormula.TryLaunchSkill(this, ss, ref param)) { if (param.IsAutoLaunch && !ss.CanAutoLaunch()) { return null; } //沉默不能释放其他技能// if (IsSilent && ss != mDefaultSkill) { return null; } // 自动战斗下,释放技能要在范围内 InstanceUnit target = null; if (ss.Data.AttackMustBeInRange) { target = Parent.getUnit(param.TargetUnitID); if (!ss.checkTargetRange(target)) { return null; } } //是否在合理的攻击范围内 if (param.IsInGuard && !ss.checkTargetInAttackRange(target, param)) { return null; } if (ss.GetSkillType() == XmdsSkillType.petGiveAcitve) { mFormula.TriggerPetSkill(this, ss, ref param); return null; } StateSkill state = new StateSkill(this, ss, param, ss.ActionSpeed, (st) => { overLaunchSkill(ss, st); if (mOnLaunchSkill != null) { mOnLaunchSkill.Invoke(this, ss); } }); if (state.tryLaunch()) { this.Virtual.SetCombatState(BattleStatus.ReadyBattle); Parent.cb_unitLaunchSkill(this, ss); if (changeState(state)) { //释放成功 if (param.LaunchSetDirection) { //Console.WriteLine("launchSkill - 设置方向: " + param.direction + ", " + this.ID); //this.faceTo(param.direction); //this.SendForceSync(); } return state; } } } return null; } protected virtual void overLaunchSkill(SkillState ss, StateSkill state) { this.AddHP(-ss.Data.CostHP, null); this.AddMP(-ss.Data.CostMP, null); } /// /// 单位释放技能 /// public StateSkill launchSkill(int skillID, LaunchSkillParam param) { SkillState skill = getSkillState(skillID); return launchSkill(skill, param); } /// /// 释放随机技能,一般用于AI /// /// /// /// public virtual StateSkill launchRandomSkill(SkillTemplate.CastTarget expectTarget, LaunchSkillParam param) { StateSkill current = CurrentState as StateSkill; if (current != null && !current.IsCancelableBySkill) { return null; } int rand = RandomN.Next(0, mAllSkills.Count); for (int si = mAllSkills.Count - 1; si >= 0; --si) { SkillTemplate st = mAllSkills[CMath.cycNum(rand, si, mAllSkills.Count)]; if (st.ExpectTarget == expectTarget) { SkillState sst = mSkillStatus.Get(st.GetID()); if (sst.LaunchSkill.AutoLaunch && sst.TryLaunch()) { StateSkill state = launchSkill(sst, param); if (state != null) { return state; } } } } return null; } public virtual StateSkill launchRandomSkillForAll(LaunchSkillParam param) { StateSkill current = CurrentState as StateSkill; if (current != null && !current.IsCancelableBySkill) { return null; } int rand = RandomN.Next(0, mAllSkills.Count); for (int si = mAllSkills.Count - 1; si >= 0; --si) { SkillTemplate st = mAllSkills[CMath.cycNum(rand, si, mAllSkills.Count)]; SkillState sst = mSkillStatus.Get(st.GetID()); if (sst.LaunchSkill.AutoLaunch && sst.TryLaunch()) { StateSkill state = launchSkill(sst, param); if (state != null) { return state; } } } return null; } /// /// 尝试取消当前技能,打出连击 /// /// /// /// public virtual bool tryLaunchRandomSkillAndCancelCurrentSkill(InstanceUnit target, bool autoFocusNearTarget = false) { StateSkill current = CurrentState as StateSkill; if (current != null && !current.IsChanting && current.IsCancelableBySkill) { LaunchSkillParam param = new LaunchSkillParam(target.ID, null, autoFocusNearTarget); //优先多段攻击// if (current.SkillData.IsSingleAction) { if (IsTargetInSkillRange(current.SkillData, target)) { StateSkill st = launchSkill(current.SkillData.ID, param); if (st != null) { return true; } } } //随机其他技能// int rand = RandomN.Next(0, mAllSkills.Count); for (int si = mAllSkills.Count - 1; si >= 0; --si) { SkillTemplate st = mAllSkills[CMath.cycNum(rand, si, mAllSkills.Count)]; if (Parent.IsAttackable(this, target, st.ExpectTarget, AttackReason.Attack, st)) { SkillState sst = mSkillStatus.Get(st.GetID()); if (sst.LaunchSkill.AutoLaunch && IsTargetInSkillRange(st, target)) { StateSkill state = launchSkill(sst, param); if (state != null) { return true; } } } } } return false; } /// /// 尝试在范围内找到目标释放技能,并进入StateFollowAndAttack,自己走过去打。 /// /// /// /// public virtual bool tryFollowAndLaunchRandomSkillToTargetInRange(float range, bool skill_auto_launchable = false) { if (!IsNoneSkill) { SkillState[] skills = mSkillStatus.SkillsArray; CUtils.RandomArray(RandomN, skills); using (var list = ListObjectPool.AllocAutoRelease()) { //随机找个目标施法// Parent.getObjectsRoundRange(Collider.Object_Pos_IncludeInRound, X, Y, Info.GuardRange, list, this.AoiStatus); DoAndRemoveCollection.UpdateAndRemove(list, (InstanceUnit u) => { return !u.IsActive; }); if (list.Count == 0) { return false; } CUtils.RandomList(Parent.RandomN, list); foreach (SkillState skill in skills) { if ((!skill_auto_launchable || skill.LaunchSkill.AutoLaunch) && skill.TryLaunch()) { for (int i = 0; i < list.Count; i++) { InstanceUnit u = list[i]; if (Parent.IsAttackable(this, u, skill.Data.ExpectTarget, AttackReason.Attack, skill.Data)) { //检测是否有可释放技能// changeState(new StateFollowAndAttack(this, u, skill.Data.ExpectTarget)); return true; } } } } } } return false; } #endregion //----------------------------------------------------------------------------------------------------------------- internal void deadDropItems(int force) { foreach (KeyValuePair e in mDropItems.Drop(Parent, Parent.RandomN)) { float x; float y; CMath.RandomPosInRound(RandomN, X, Y, e.Value.DropPosRange, out x, out y); Parent.AddItem(e.Key, e.Key.Name, x, y, CMath.RandomAngle(RandomN), force, e.Key.Name, this); } } protected bool tryPickObject(InstanceUnit unit) { if (!Parent.IsVisibleAOI(this, unit)) { return false; } bool ret = true; if (mOnTryPickUnit != null) { foreach (TryPickUnitHandler trypick in mOnTryPickUnit.GetInvocationList()) { if (!trypick.Invoke(this, unit)) { ret = false; } } } return ret; } public void PickUnit(InstanceUnit pickable) { if (tryPickObject(pickable)) { Parent.cb_unitPickUnitCallBack(this, pickable); if (mOnPickUnit != null) { mOnPickUnit.Invoke(this, pickable); } } } public CommonAI.Data.SceneType GetSceneType() { return mZone.SceneType; } //-----------------------------------------------------------------------------------------------------// #region Environment //-----------------------------------------------------------------------------------------------------// private HashMap EnvironmentVarMap = new HashMap(); public void SetEnvironmentVar(string key, object value, bool syncToClient = false) { if (!string.IsNullOrEmpty(key)) { EnvironmentVar var = EnvironmentVarMap.Get(key); if (var != null) { if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || (var.SyncToClient && var.Value != value)) { queueEvent(new PlayerSyncEnvironmentVarEvent(ID, key, value)); } var.Value = value; } else { var = new EnvironmentVar(key, syncToClient, value); EnvironmentVarMap.Add(key, var); if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || var.SyncToClient) { queueEvent(new PlayerSyncEnvironmentVarEvent(ID, key, value)); } } } } public T GetEnvironmentVarAs(string key) { if (!string.IsNullOrEmpty(key)) { EnvironmentVar var = EnvironmentVarMap.Get(key); if (var != null) { try { return (T)var.Value; } catch (Exception err) { log.Warn("GetEnvironmentVarAs : " + key + ", catch: " + err); } } } return default(T); } public int ListEnvironmentVars(List list) { list.AddRange(EnvironmentVarMap.Values); return EnvironmentVarMap.Count; } public List ListEnvironmentVars() { return new List(EnvironmentVarMap.Values); } public ClientStruct.ZoneEnvironmentVar[] GetCurrentUnitVars() { ClientStruct.ZoneEnvironmentVar[] ret = new ClientStruct.ZoneEnvironmentVar[EnvironmentVarMap.Count]; int i = 0; try { foreach (EnvironmentVar var in EnvironmentVarMap.Values) { ret[i].Key = var.Key; ret[i].Value = var.Value; ret[i].SyncToClient = var.SyncToClient; i++; } } catch (Exception e) { log.Warn("GetCurrentUnitVars catch: " + ret.Length + ", " + EnvironmentVarMap.Count + ", " + i + ", e: " + e); } return ret; } #endregion //-----------------------------------------------------------------------------------------------------// //-----------------------------------------------------------------------------------------------------// #region SceneEvents //-----------------------------------------------------------------------------------------------------// private HashMap mBindEvents = new HashMap(); public void BindUnitEvent(int unit_event_id) { if (!mBindEvents.ContainsKey(unit_event_id)) { UnitEventTemplate uet = Templates.getUnitEvent(unit_event_id); if (uet != null) { uet = uet.Clone() as UnitEventTemplate; UnitEventTriggerCollection bind_event = new UnitEventTriggerCollection(this); bind_event.Bind(uet.Events); mBindEvents.Add(unit_event_id, bind_event); } } } protected virtual void clearBindEvents() { foreach (var evt in mBindEvents.Values) { evt.Dispose(); } mBindEvents.Clear(); } /** 计算并获得伤害 */ public virtual void PetShareDamage(int baseDmgValue, InstanceUnit sender) { } #endregion //-----------------------------------------------------------------------------------------------------// //-----------------------------------------------------------------------------------------------------// } }