using CommonAI.RTS; using CommonLang.Vector; using CommonAI.Zone; using CommonAI.Zone.Attributes; using CommonAI.Zone.Helper; using CommonLang; using System; using System.Collections.Generic; using System.Linq; using System.Text; using CommonAI.Zone.Formula; using System.Collections; using CommonLang.Log; namespace CommonAI.ZoneClient { public partial class ZoneUnit { //-------------------------------------------------------------------------------- #region DamageDeadFly private TimeExpire mDamageTime; private TimeExpire mDeadTime; private FallingDown mHitFlyState; public FallingDown StartFly(float z_speed, float gravity, float z_limit) { if (!Parent.IsSyncZ && z_speed != 0) { mHitFlyState = new FallingDown(this, z_speed, gravity, z_limit); return mHitFlyState; } return null; } protected virtual void DoDamage(UnitDamageEvent e) { mDamageTime = new TimeExpire(e, e.DamageTimeMS); if (e.HasFly) { StartFly(e.ZSpeedSEC, e.ZGravity, e.ZLimit); } if (Info.DamageEffect != null) { Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.DamageEffect)); } SetCurrentState(UnitActionStatus.Damage, e); } protected virtual void DoJump(UnitJumpEvent e) { StartFly(e.ZSpeed, e.ZGravity, e.ZLimit); } protected virtual void DoDead(UnitDeadEvent me) { if (me.RebirthTimeMS > 0) { mDeadTime = new TimeExpire(me, me.RebirthTimeMS); } //所有的怪都清了一遍,都是先下发UnitDeadEvent,然后在下发死亡状态,为了防止切换状态的时候清掉特效,逻辑层的死亡特效屏蔽掉,放在表现层下 //if (Info.DeadEffect != null) //{ // Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.DeadEffect)); //} if (me.Crushed) { if (Info.CrushEffect != null) { Parent.PreQueueEvent(new UnitEffectEvent(this.ObjectID, Info.CrushEffect)); } } } private void UpdateDamage(int intervalMS) { if (mDeadTime != null && mDeadTime.Update(intervalMS)) { mDeadTime = null; } if (mDamageTime != null && mDamageTime.Update(intervalMS)) { mDamageTime = null; } } /// /// 落体运动 /// public class FallingDown { private readonly ZoneUnit unit; private readonly float gravity; private readonly float z_limit; private float z_speed; public FallingDown(ZoneUnit unit, float zspeed, float zgravity, float zlimit) { this.unit = unit; this.z_limit = zlimit; this.z_speed = zspeed; this.gravity = zgravity == 0 ? unit.Templates.CFG.GLOBAL_GRAVITY : zgravity; this.ExpectTimeMS = MoveHelper.CalculateFlyTimeMS(unit.Z, zspeed, zlimit, gravity, 10); this.IsEnd = false; } public bool Update(int intervalMS) { //向上受到ZLimit限制// unit.Z += MoveHelper.GetDistance(intervalMS, z_speed); unit.Z = Math.Max(0, unit.Z); this.IsEnd = unit.Z <= 0; //骤停// if (z_limit != 0 && z_speed > 0 && unit.Z > z_limit) { z_speed = 0; } this.z_speed -= MoveHelper.GetDistance(intervalMS, gravity); return IsEnd; } public bool IsEnd { get; private set; } public int ExpectTimeMS { get; private set; } public float ZSpeedSEC { get { return z_speed; } } public float ZLimit { get { return z_limit; } } } #endregion //-------------------------------------------------------------------------------- #region Skill public float GetSkillAttackRange(SkillTemplate skill) { return IsAttackRangeIncludeBodySize ? BodySize + skill.AttackRange : skill.AttackRange; } internal void SyncSkillStatus(ClientStruct.UnitSkillStatus[] skills) { if (skills != null) { foreach (ClientStruct.UnitSkillStatus st in skills) { SkillState status = this.GetSkillState(st.SkillTemplateID); if (status != null) { status.Sync(st); } } } } public virtual void InitSkills() { mSkillStatus.Clear(); if (Info.BaseSkillID != null) { BaseSkillID = Info.BaseSkillID.SkillID; if (BaseSkillID != 0) { SkillTemplate skt = Templates.getSkill(BaseSkillID); if(skt == null) { ClientLog.LogError("Skill template({0}@{1}) Not Found.", BaseSkillID, Info.Name); return; } mSkillStatus.Put(new SkillState(skt, this)); } } else { BaseSkillID = 0; } foreach (LaunchSkill skid in Info.Skills) { if (skid.SkillID != 0) { SkillTemplate skt = Templates.getSkill(skid.SkillID); if (skt == null) { ClientLog.LogError("Skill template({0}@{1}) Not Found.", skid.SkillID, Info.Name); return; } mSkillStatus.Put(new SkillState(skt, this)); } } if (mOnSkillChanged != null) { mOnSkillChanged.Invoke(SkillOption.Init, this, BaseSkillID, mSkillStatus.Keys.ToArray()); } } protected virtual void DoSkillChanged(PlayerSkillChangedEvent e) { this.mFastCastRate = e.unitFastCastRate; //一项一项排查,是不是有不同 bool dif = false; if(e.baseSkill != null && e.baseSkill.TemplateID == BaseSkillID) { if(e.skills.Count == mSkillStatus.Count - 1) { foreach(var skt in e.skills) { if(!mSkillStatus.ContainsKey(skt.TemplateID)) { dif = true; break; } } } else { dif = true; } } else { dif = true; } if (!dif) { //YXJDebug.logDebug(">skills are exactly the same, why you post >PlayerSkillChangedEvent"); return; } mSkillStatus.Clear(); if (e.baseSkill != null) { BaseSkillID = e.baseSkill.TemplateID; mSkillStatus.Put(new SkillState(e.baseSkill, this)); } else { BaseSkillID = 0; } foreach (var skt in e.skills) { mSkillStatus.Put(new SkillState(skt, this)); } if (mOnSkillChanged != null) { mOnSkillChanged.Invoke(SkillOption.Reset, this, BaseSkillID, mSkillStatus.Keys.ToArray()); } } protected virtual void DoPlayerSkillActiveChangedEvent(PlayerSkillActiveChangedEvent e) { for (int i = e.Skills.Count - 1; i >= 0; --i) { PlayerSkillActiveChangedEvent.State sat = e.Skills[i]; SkillState ss = GetSkillState(sat.SkillTemplateID); if (ss != null) { ss.SetActive(sat.ST); } } if (mOnSkillChanged != null) { mOnSkillChanged.Invoke(SkillOption.ActiveChange, this, 0, null); } } protected void AddSkill(SkillTemplate skill, bool isDefault) { mSkillStatus.Put(new SkillState(skill, this)); if (isDefault) { BaseSkillID = skill.ID; } if (mOnSkillChanged != null) { mOnSkillChanged.Invoke(SkillOption.Add, this, skill.ID, mSkillStatus.Keys.ToArray()); } } protected void RemoveSkill(int skillTemplateID) { if (mSkillStatus.RemoveByKey(skillTemplateID) != null) { if (BaseSkillID == skillTemplateID) { BaseSkillID = 0; } if (mOnSkillChanged != null) { mOnSkillChanged.Invoke(SkillOption.Remove, this, skillTemplateID, mSkillStatus.Keys.ToArray()); } } } private void UpdateSkills(int intervalMS) { for (int i = 0; i < mSkillStatus.Count; i++) { SkillState ss = mSkillStatus.GetAt(i); ss.Update(intervalMS); } if (mChantingSkill != null && mChantingSkill.Update(intervalMS)) { mChantingSkill = null; } } //新增技能使用次数 protected virtual void DoPlayerSkillUseTimeChangedEvent(PlayerSkillUseTimeChangedEvent evt) { SkillState ss = mSkillStatus.Get(evt.SkillTemplateID); if (ss != null) { //UnityEngine.Debug.LogError("PlayerSkillUseTimeChangedEvent usetimes = " + evt.useTimes); ss.SyncSkillUseTimes(evt.useTimes); } } protected virtual void DoSkillAdded(PlayerSkillAddedEvent e) { this.AddSkill(e.Skill, e.IsDefault); } protected virtual void DoSkillRemoved(PlayerSkillRemovedEvent e) { this.RemoveSkill(e.SkillID); } protected virtual void DoLaunchSkill(UnitLaunchSkillEvent me) { //ClientLog.LogWarning(">doLaunchSkillAction>{0} @ {1}", me.action_index, me.skill_id); SkillState ss = mSkillStatus.Get(me.skill_id); if (ss != null) { this.mLastLaunchSkill = ss; ss.Launch(me); } doLaunchSkillAction(me); if (mOnLaunchSkill != null) { mOnLaunchSkill.Invoke(this, ss, me); } } protected virtual void DoSkillCDChanged(PlayerCDEvent evt) { if (evt.is_all) { for (int i = 0; i < mSkillStatus.Count; i++) { SkillState ss = mSkillStatus.GetAt(i); if (evt.is_clear) ss.ClearCD(); else if (evt.is_decrease_time) ss.DecreaseCD(evt.decrease_timeMS); else if (evt.is_decrease_pct) ss.DecreaseCD_Pct(evt.decrease_pct); } } else { SkillState ss = mSkillStatus.Get(evt.skill_template_id); if (ss != null) { if (evt.is_clear) ss.ClearCD(); else if (evt.is_decrease_time) ss.DecreaseCD(evt.decrease_timeMS); else if (evt.is_decrease_pct) ss.DecreaseCD_Pct(evt.decrease_pct); } } } protected virtual void DoChangeAction(UnitSkillActionChangeEvent e) { if (mLastLaunchSkill != null) { mLastLaunchSkill.ChangeAction(e.ActionIndex); } if (mCurrentSkillAction != null) { mCurrentSkillAction.onUnitSkillActionChangeEvent(e); } if (mLastLaunchSkill != null && mOnSkillActionChanged != null) { mOnSkillActionChanged.Invoke(this, mLastLaunchSkill, e.ActionIndex); } } protected virtual void DoPlayerSkillStopEvent(PlayerSkillStopEvent e) { SkillState ss = mSkillStatus.Get(e.SkillID); if (ss != null) { ss.PlayerStop(e); } mChantingSkill = null; clearSkillAction(); } protected virtual void DoPlayerSkillTimeChangedEvent(PlayerSkillTimeChangedEvent e) { SkillState ss = mSkillStatus.Get(e.SkillTemplateID); if (ss != null) { ss.TimeChange(e); } } public int BaseSkillID { get; private set; } internal SkillMap mSkillStatus = new SkillMap(); private SkillState mLastLaunchSkill; public class SkillMap { private HashMap Map = new HashMap(); private List For = new List(); public int Count { get { return For.Count; } } public ICollection Keys { get { return Map.Keys; } } public IEnumerable Skills { get { return For; } } public SkillState[] SkillsArray { get { return For.ToArray(); } } public SkillState GetAt(int i) { return For[i]; } public SkillState Get(int id) { return Map.Get(id); } public void Put(SkillState state) { SkillState old = Map.Get(state.Data.ID); if (old != null) { For.Remove(old); } Map.Put(state.Data.ID, state); For.Add(state); } public SkillState RemoveByKey(int id) { SkillState ret = Map.RemoveByKey(id); if (ret != null) { For.Remove(ret); } return ret; } public bool ContainsKey(int id) { return Map.ContainsKey(id); } public void Clear() { Map.Clear(); For.Clear(); } } public class SkillState { readonly public SkillTemplate Data; readonly public ZoneUnit Owner; private SkillActiveState current_state; private int all_action_time_ms; private int pass_time_ms; private int stop_time_ms; private float percent = 1f; private float action_speed = 1f; private int total_cd_time_ms; private bool is_in_mutil_time = false; private bool is_period_cd_end = false; //新增蓄力 private ActionEnum action_type; //新增使用次数 private int use_Times; private bool is_use_tiems = false; private bool is_skill_end = false; private bool is_Skill_Block = false; //新增技能描述类型 private SkillDescType skill_desc_type = SkillDescType.None; //当前技能是否还从未触发过 private bool is_skill_never_launch; //CD状态变化事件 public System.Action OnCDStateChange; //蓄力状态发生了变化 public System.Action OnChargeStateChange; //蓄力条进度 public float ChargeProgress { private set; get; } public uint ChargeTimeMS { private set; get; } internal SkillState(SkillTemplate data, ZoneUnit owner) { this.Data = data; this.Owner = owner; this.all_action_time_ms = Data.ActionQueueTimeMS; this.total_cd_time_ms = data.CoolDownMS; this.pass_time_ms = stop_time_ms = FullCDTimeMS; this.pass_time_ms = data.CoolDownMS; this.is_in_mutil_time = Data.IsSingleAction; this.use_Times = (int)data.CurUseTimes; this.is_use_tiems = this.use_Times >= 0 ? true : false; //以服务器为主,服务器那边目前没有做具体细分 if (data.ActionPriority < 0) { skill_desc_type = SkillDescType.Displacement; } this.is_skill_never_launch = true; } internal bool TryLaunch(UnitLaunchSkillAction act) { if (IsActive && Owner.MP >= Data.CostMP && Owner.HP >= Data.CostHP && (!IsCD)) { return true; } return false; } internal void Launch(UnitLaunchSkillEvent evt) { this.is_skill_never_launch = false; this.percent = 0; this.pass_time_ms = 0; this.is_skill_end = false; this.CurrentActionID = evt.action_index; this.action_type = Data.ActionQueue[evt.action_index].SigleActionType; this.is_Skill_Block = Data.ActionQueue[evt.action_index].IsAddSkillBtnBlock; //ClientLog.LogWarning(">launch SkillState >{0} @ {1}, isMulti:{2}", CurrentActionID, this.Data.TemplateID, this.is_in_mutil_time); if (CurrentActionID + 1 == Data.ActionQueue.Count) { this.is_period_cd_end = this.Data.IsSingleAction; } if (evt.IsActionSpeedUP) { this.action_speed = evt.action_speed; } else { this.action_speed = 1f; } if (evt.IsChangeTotalCDTime) { this.total_cd_time_ms = evt.TotalCDTimeMS; } else { this.total_cd_time_ms = ToFullTimeCD(); this.is_skill_end = true; //ClientLog.LogError("is_skill_end: true"); } OnCDStateChange?.Invoke(); if (OnChargeStateChange != null) { if (Data.ActionQueue[CurrentActionID].ShowChargeTimeMS > 0) { ChargeProgress = 0f; ChargeTimeMS = 0; OnChargeStateChange.Invoke(); } else if (ChargeProgress > 0 && ChargeProgress < 1) { ChargeProgress = 1f; OnChargeStateChange.Invoke(); } } } public byte NextAction() { if (Data.IsSingleAction) { int action_step = CurrentActionID; int action_time = this.FullCDTimeMS; if (Data.IsCoolDownWithAction) { action_time = Data.ActionQueue[action_step % Data.ActionQueue.Count].TotalTimeMS; } // 是放技能时,处于多段攻击连击冷却时间范围 if (pass_time_ms - stop_time_ms < Data.SingleActionCoolDownMS || pass_time_ms < (action_time + Data.SingleActionCoolDownMS)) { action_step += 1; action_step = (byte)(action_step % Data.ActionQueue.Count); return (byte)action_step; } } return 0; } private int ToFullTimeCD() { if (Data.IsCoolDownWithAction) { if (Data.IsSingleAction) { return Data.ActionQueue[CurrentActionID].TotalTimeMS; } else { return all_action_time_ms; } } return Data.ToUnitSkillTotalTime(Owner.mFastCastRate); } internal void SetActive(SkillActiveState state) { this.current_state = state; } internal void ChangeAction(int step) { var aq = Data.ActionQueue[CurrentActionID]; if (aq.ShowChargeTimeMS > 0 && ChargeProgress < 1) { ChargeProgress = 1f; OnChargeStateChange?.Invoke(); } this.CurrentActionID = step; } internal void ClearCD() { this.pass_time_ms = FullCDTimeMS; } internal void DecreaseCD(int ms) { this.pass_time_ms += ms; } internal void DecreaseCD_Pct(float pct) { this.pass_time_ms += (int)(FullCDTimeMS * pct); } internal void PlayerStop(PlayerSkillStopEvent stop) { this.stop_time_ms = pass_time_ms; this.is_skill_end = true; this.is_Skill_Block = false; if (ChargeProgress > 0 && ChargeProgress < 1) { ChargeProgress = 1f; OnChargeStateChange?.Invoke(); } } internal void Sync(ClientStruct.UnitSkillStatus syn) { this.pass_time_ms = syn.PassTime; this.use_Times = syn.useTimes; this.is_use_tiems = syn.useTimes >= 0 ? true : false; internal_update(0); } internal void TimeChange(PlayerSkillTimeChangedEvent e) { if (this.is_skill_never_launch) { //重新登录后,技能的状态会丢失,这里重新算一下 if(e.SkillPassTimeMS < e.SkillTotalTimeMS) { //CD未结束的技能,action都是在最后一段(不然不会有CD) this.CurrentActionID = Data.ActionQueue.Count - 1; } } this.pass_time_ms = e.SkillPassTimeMS; this.total_cd_time_ms = e.SkillTotalTimeMS; this.is_in_mutil_time = this.Data.IsSingleAction; this.is_period_cd_end = this.Data.IsSingleAction; this.is_Skill_Block = false; this.is_skill_end = true; internal_update(0); OnCDStateChange?.Invoke(); if(OnChargeStateChange != null && ChargeProgress > 0 && ChargeProgress < 1) { ChargeProgress = 1f; OnChargeStateChange.Invoke(); } } internal void Update(int intervalMS) { if (!IsActive && IsPauseOnDeactive) { return; } internal_update(intervalMS); if (this.isPeriodCDEnd) { SetMutilTime(); } if (OnChargeStateChange != null && CurrentActionID >= 0 && CurrentActionID < Data.ActionQueue.Count) { var aq = Data.ActionQueue[CurrentActionID]; if (aq.ShowChargeTimeMS > 0) { ChargeTimeMS += (uint)intervalMS; if (ChargeProgress < 1) { ChargeProgress = ChargeTimeMS / (float)aq.ShowChargeTimeMS; if (ChargeProgress > 1) ChargeProgress = 1f; OnChargeStateChange.Invoke(); } } else if (ChargeProgress > 0 && ChargeProgress < 1) { ChargeProgress = 1f; OnChargeStateChange.Invoke(); } } } internal void SetPassTime(int passTime) { this.pass_time_ms = passTime; internal_update(0); } void SetMutilTime() { if (CDPercent < 1) { this.is_in_mutil_time = false; } else { this.is_in_mutil_time = true; this.is_period_cd_end = false; } } private void internal_update(int intervalMS) { pass_time_ms += intervalMS; if (pass_time_ms >= FullCDTimeMS) { if (percent < 1) { percent = 1f; stop_time_ms = pass_time_ms; OnCDStateChange?.Invoke(); } } else { percent = (float)pass_time_ms / FullCDTimeMS; } } //新增技能使用次数 internal void SyncSkillUseTimes(int usetimes) { this.use_Times = usetimes; this.is_use_tiems = true; } public int FullCDTimeMS { get { return total_cd_time_ms; } } public bool isMutilTime { get { return this.is_in_mutil_time; } } public bool isPeriodCDEnd { get { return this.is_period_cd_end; } } public bool IsCD { get { if (Data.IsCoolDownWithAction) { return false; } return (percent < 1f); } } public float CDPercent { get { if (Data.IsCoolDownWithAction) { return 1f; } return percent; } } public float ActionSpeed { get { return action_speed; } } public int PassTimeMS { get { return pass_time_ms; } } public int StopTimeMS { get { return stop_time_ms; } } public int CurrentActionID { get; private set; } public SkillActiveState ActiveState { get { return current_state; } } public bool IsActive { get { return current_state == SkillActiveState.Active || current_state == SkillActiveState.ActiveAndHide; } } public bool IsPauseOnDeactive { get { return current_state == SkillActiveState.DeactiveAndPause; } } public ActionEnum ActionType { get { return action_type; } } public int UseTimes { get { return this.use_Times; } } public bool IsUseTimes { get { return this.is_use_tiems; } } //目前仅用于蓄力,后面如果有其它类型需要用到,注意TimeChange和StopSkill消息的顺序....... public bool IsSkillEnd { get { return this.is_skill_end; } } public bool IsSkillBlock { get { return this.is_Skill_Block; } } public SkillDescType SkillType { get { return this.skill_desc_type; } } } [Obsolete] public List GetSkillStatus() { return new List(mSkillStatus.Skills); } public void GetSkillStatus(List ret) { ret.AddRange(mSkillStatus.Skills); } public SkillState GetSkillState(int templateID) { return mSkillStatus.Get(templateID); } #endregion //--------------------------------------------------------------------------- // skill action //--------------------------------------------------------------------------- #region SkillAction /// /// 是否正在吟唱 /// public bool IsChanttingSkill { get { return mChantingSkill != null; } } public int ChantingSkillPassMS { get { return (mChantingSkill != null) ? mChantingSkill.PassTimeMS : 0; } } public int ChantingSkillTotalMS { get { return (mChantingSkill != null) ? mChantingSkill.TotalTimeMS : 0; } } public SkillTemplate ChantingSkillData { get { return (mChantingSkill != null) ? mChantingSkill.Tag : null; } } public SkillState ChantingSkill { get { if (mChantingSkill != null) { return GetSkillState(mChantingSkill.Tag.TemplateID); } return null; } } private TimeExpire mChantingSkill; protected virtual void DoUnitChantSkillEvent(UnitChantSkillEvent e) { var temp = Templates.getSkill(e.skill_id); mChantingSkill = new TimeExpire(temp, e.chant_ms); SkillState ss = GetSkillState(e.skill_id); if (ss != null) { if (mOnChantSkill != null) { mOnChantSkill.Invoke(this, e, mChantingSkill); } } } protected virtual void doLaunchSkillAction(UnitLaunchSkillEvent me) { clearSkillAction(); var temp = Templates.getSkill(me.skill_id); if (temp != null) { mCurrentSkillAction = new UnitSkillAction(this, temp); mCurrentSkillAction.onLaunch(me); invokeSkillActionStart(mCurrentSkillAction); } } protected virtual void invokeSkillActionStart(ISkillAction act) { if (mOnSkillActionStart != null) { mOnSkillActionStart.Invoke(this, act); } } public virtual ISkillAction CurrentSkillAction { get { return mCurrentSkillAction; } } public virtual UnitActionData CurrentSkillActionData { get { if (CurrentSkillAction != null) { return CurrentSkillAction.CurrentAction; } return null; } } //------------------------------------------------------------------------------------------- // unit impl private UnitSkillAction mCurrentSkillAction; private void updateSkillAction(int intervalMS) { if (mCurrentSkillAction != null) { //UnityEngine.Debug.LogError("2 mCurrentSkillAction = " + mCurrentSkillAction.SkillData.TemplateID + " actionIndex = " + mCurrentSkillAction.CurrentActionIndex); mCurrentSkillAction.onUpdate(intervalMS); if (mCurrentSkillAction.IsDone) { clearSkillAction(); } } } private void clearSkillAction() { if (mCurrentSkillAction != null) { mCurrentSkillAction.onStop(); mCurrentSkillAction = null; } } public abstract class ISkillAction { private BitSet8 current_action_status = new BitSet8(); public bool IsControlMoveable { get { return current_action_status.Get(0); } protected set { current_action_status.Set(0, value); } } public bool IsControlFaceable { get { return current_action_status.Get(1); } protected set { current_action_status.Set(1, value); } } public bool IsCancelableBySkill { get { return current_action_status.Get(2); } protected set { current_action_status.Set(2, value); } } public bool IsCancelableByMove { get { return current_action_status.Get(3); } protected set { current_action_status.Set(3, value); } } public bool IsNoneBlock { get { return current_action_status.Get(4); } protected set { current_action_status.Set(4, value); } } public bool IsNoneTouch { get { return current_action_status.Get(5); } protected set { current_action_status.Set(5, value); } } public bool IsFaceToTarget { get { return current_action_status.Get(6); } protected set { current_action_status.Set(6, value); } } public abstract SkillTemplate SkillData { get; } public abstract UnitLaunchSkillEvent LaunchEvent { get; } public abstract bool IsDone { get; } public abstract float ActionSpeed { get; } public abstract byte ActionStepIndex { get; } public abstract int[] ActionTimeArray { get; } public abstract int TotalTimeMS { get; } public abstract int CurrentActionIndex { get; } public abstract string CurrentActionName { get; } public abstract UnitActionData CurrentAction { get; } public abstract float ExpirePercent { get; } //把当前的执行时间抛出去给客户端用,很违和的做法,没办法数据配在扩展属性里面 //CommonAI取不出来,强行操作会引发不可控的Bug public abstract int CurPassTimeMS { get; } public abstract bool IsJumpAction { get; } internal abstract void onLaunch(UnitLaunchSkillEvent e); internal abstract void onUnitSkillActionChangeEvent(UnitSkillActionChangeEvent evt); internal abstract void onUpdate(int intervalMS); internal abstract void onStop(); internal abstract void RestJumpAction(bool isJumpAction); } public class UnitSkillAction : ISkillAction { readonly private ZoneUnit ownerUnit; readonly private SkillTemplate skill; private UnitLaunchSkillEvent launch_event; private int current_pass_time = 0; private int total_pass_time = 0; private int total_time_ms = 0; private bool is_done = false; private bool is_jump_action = false; private Queue action_queue; private int current_action_index = 0; private UnitActionData current_action = null; private PopupKeyFrames current_frames = new PopupKeyFrames(); private int current_action_total_time = 0; private int[] action_time_array; public ZoneLayer Parent { get { return ownerUnit.Parent; } } public override int[] ActionTimeArray { get { return action_time_array; } } public override int TotalTimeMS { get { return total_time_ms; } } public override byte ActionStepIndex { get { return (launch_event != null) ? launch_event.action_index : (byte)0; } } public override int CurrentActionIndex { get { return current_action_index; } } public override string CurrentActionName { get { return (current_action != null) ? current_action.ActionName : null; } } public override UnitActionData CurrentAction { get { return current_action; } } public override float ExpirePercent { get { return total_pass_time / (float)TotalTimeMS; } } public override SkillTemplate SkillData { get { return skill; } } public override UnitLaunchSkillEvent LaunchEvent { get { return launch_event; } } public override bool IsDone { get { return is_done; } } public override int CurPassTimeMS { get { return current_pass_time; } } protected internal UnitSkillAction(ZoneUnit actor, SkillTemplate skill) { this.skill = skill; this.ownerUnit = actor; this.action_time_array = skill.ActionQueueTimeArray; } public override float ActionSpeed { get { if (launch_event != null) { if(launch_event.action_speed_add != null && launch_event.action_index < launch_event.action_speed_add.Length) { return launch_event.action_speed + launch_event.action_speed_add[launch_event.action_index]; } return launch_event.action_speed; } return 1f; } } public override bool IsJumpAction { get { return is_jump_action; } } internal override void onLaunch(UnitLaunchSkillEvent e) { //UnityEngine.Debug.LogError("SkillID = " + e.skill_id + " ActionSpeed = " + e.action_speed + " action_index = " + e.action_index); this.launch_event = e; this.total_pass_time = 0; this.current_pass_time = 0; if (e.action_time_array != null && e.action_time_array.Length > 0) { this.total_time_ms = e.TotalActionTimeMS; this.action_time_array = e.action_time_array; } else if (skill.IsSingleAction) { this.total_time_ms = skill.ActionQueue[launch_event.action_index].TotalTimeMS; this.action_time_array = new int[] { TotalTimeMS }; } else { this.total_time_ms = skill.ActionQueueTimeMS; this.action_time_array = skill.ActionQueueTimeArray; } if (skill.IsSingleAction) { var sa = skill.ActionQueue[launch_event.action_index]; this.action_queue = new Queue(1); this.action_queue.Enqueue(sa); this.current_action_index = launch_event.action_index; } else { this.action_queue = new Queue(skill.ActionQueue); this.current_action_index = launch_event.action_index; } this.current_action = null; this.nextAction(this.current_action_index); } internal override void onUnitSkillActionChangeEvent(UnitSkillActionChangeEvent e) { while (this.CurrentActionIndex < e.ActionIndex) { if (!nextAction(current_action_index + 1)) { this.total_pass_time = TotalTimeMS; this.is_done = true; break; } } } internal override void onStop() { this.is_done = true; this.total_pass_time = TotalTimeMS; } internal override void onUpdate(int intervalMS) { int time_pass = (int)(intervalMS * ActionSpeed); this.total_pass_time += time_pass; this.total_pass_time = Math.Min(total_pass_time, TotalTimeMS); this.current_pass_time += time_pass; if (current_action == null) { nextAction(current_action_index + 1); } if (current_action == null) { this.is_done = true; this.total_pass_time = TotalTimeMS; } else { // 关键帧 // using (var kfs = ListObjectPool.AllocAutoRelease()) { if (current_frames.PopKeyFrames(current_pass_time, kfs) > 0) { for (int i = 0; i < kfs.Count; i++) { doKeyFrame(kfs[i]); } } } // 下段动作 // if ((current_action != null) && (current_pass_time >= current_action_total_time)) { current_action = null; if (action_queue.Count == 0) { this.is_done = true; this.total_pass_time = TotalTimeMS; } } } } private bool nextAction(int index) { if (action_queue != null && action_queue.Count > 0) { this.current_action_index = index; this.current_action = action_queue.Dequeue(); if (current_action != null) { this.current_frames.AddRange(current_action.KeyFrames); this.current_pass_time = 0; //NewAction// if (launch_event.action_time_array != null) { if (skill.IsSingleAction) { this.current_action_total_time = launch_event.TotalActionTimeMS; } else if (current_action_index < launch_event.action_time_array.Length) { this.current_action_total_time = launch_event.action_time_array[current_action_index]; } } else { this.current_action_total_time = current_action.TotalTimeMS; } this.IsCancelableBySkill = current_action.IsCancelableBySkill; this.IsNoneBlock = current_action.IsNoneBlock; this.IsNoneTouch = current_action.IsNoneTouch; this.IsFaceToTarget = current_action.IsFaceToTarget; this.IsCancelableByMove = current_action.IsCancelable; this.IsControlMoveable = current_action.IsControlMoveable; this.IsControlFaceable = current_action.IsControlFaceable; return true; } } current_action = null; return false; } private void doKeyFrame(UnitActionData.KeyFrame kf) { // 关键帧改变状态 if (kf.ChangeStatus != null) { this.IsNoneBlock = kf.ChangeStatus.IsNoneBlock; this.IsNoneTouch = kf.ChangeStatus.IsNoneTouch; this.IsFaceToTarget = kf.ChangeStatus.IsFaceToTarget; this.IsCancelableByMove = kf.ChangeStatus.IsCancelable; this.IsCancelableBySkill = kf.ChangeStatus.IsCancelableBySkill; this.IsControlMoveable = kf.ChangeStatus.IsControlMoveable; this.IsControlFaceable = kf.ChangeStatus.IsControlFaceable; } } internal override void RestJumpAction(bool isJumpAction) { is_jump_action = isJumpAction; } } //------------------------------------------------------------------------------------------- #endregion //-------------------------------------------------------------------------------- #region Buff internal BuffMap mBuffStatus = new BuffMap(); internal void SyncBuffStatus(ClientStruct.UnitBuffStatus[] buffs) { if (buffs != null) { for (int i = 0; i < buffs.Length; i++) { ClientStruct.UnitBuffStatus st = buffs[i]; TryAddBuff(st.BuffTemplateID, st.SenderID, st.IsEquip, st.TotalTime, st.OverlayLevel, st.PassTime); } } } protected void SyncBuffOverlayLevel(BuffDataNotify data) { BuffState bs = mBuffStatus.Get(data.buffId, data.senderId); if (bs != null) { bs.SyncBuffExtData(data.overlayLevel, bs.BuffExtendData); //YXJDebug.logError(data.buffId + " , level:" + bs.OverlayLevel + " time = " + bs.BuffTotalTime + ", per:"+bs.CDPercent); if (mOnBuffChanged != null) { mOnBuffChanged.Invoke(this, bs); } } } protected virtual void DoLaunchBuff(UnitLaunchBuffEvent me) { //YXJDebug.logError(me.buffTemplateID + " overlayLevel = " + me.overlayLevel + " time = " + me.buffTimeMS); TryAddBuff(me.buffTemplateID, me.senderID, me.IsEquip, me.buffTimeMS, me.overlayLevel, 0, me.buffExtData); } protected virtual void DoStopBuff(UnitStopBuffEvent e) { TryRemoveBuff(e.buffTemplateID, e.senderID); } //新增Buff接口 protected virtual void DoRemoveOverlayLevelBuff(SyncUnitBuffState e) { //UnityEngine.Debug.LogError("DoRemoveOverlayLevelBuff overlayLevel = " + e.overlayLevel + " buffExtData = " + e.buffExtData); if (e.buffExtData > 0) { var bs = mBuffStatus.Get(e.buffTemplateID, e.ObjectID); if (bs != null && mOnRemoveBuffByOverlayLeveled != null) { mOnRemoveBuffByOverlayLeveled(this, bs); bs.SyncBuffExtData(e.overlayLevel, e.buffExtData); } } else { TryRemoveBuff(e.buffTemplateID, e.ObjectID); } } private void TryAddBuff(int buffTempalteID, uint senderID, bool isEquip, int totalTime, byte overlayLevel, int passTime = 0, int buffExtData = 0) { BuffTemplate buff = Templates.getBuff(buffTempalteID); if (buff != null) { BuffState bs = mBuffStatus.Get(buffTempalteID, senderID); if (bs != null) { bs.Sync(overlayLevel, totalTime, passTime, buffExtData); if (mOnBuffChanged != null) { mOnBuffChanged.Invoke(this, bs); } } else { bs = new BuffState(buff, this, senderID, isEquip); bs.Sync(overlayLevel, totalTime, passTime, buffExtData); mBuffStatus.Put(bs); if (mOnBuffAdded != null) { mOnBuffAdded.Invoke(this, bs); } } //UnityEngine.Debug.LogError("Server buffID = " + buffTempalteID + " LockStateAction = " + buff.LockStateAction); //新增单独状态 if (!string.IsNullOrEmpty(buff.LockStateAction)) { this.SetCurrentState(UnitActionStatus.BuffLockStateAction, CurrentSubState, buff); } } } private void TryRemoveBuff(int buffTempalteID, uint senderID) { BuffState bs = mBuffStatus.RemoveByKey(buffTempalteID, senderID); if (bs != null) { bs.OnEnd(); if (mOnBuffRemoved != null) { mOnBuffRemoved.Invoke(this, bs); } //UnityEngine.Debug.LogError("RemoveBuff CurrentState= " + CurrentState); //置回单独状态 if (CurrentState == UnitActionStatus.BuffLockStateAction) { this.SetCurrentState(UnitActionStatus.Idle); } } } private void UpdateBuffs(int intervalMS) { mBuffStatus.ForEachUpdate(intervalMS); //mBuffStatus.ForEach((bs) => //{ // bs.OnUpdate(intervalMS); //}); } //返回当前buff列表中,是否含有变身buff public bool HasMakeAvatarBuff() { bool has = false; mBuffStatus.ForEach((BuffState buf) => { if (buf.Data.MakeAvatar) { has = true; } }); return has; } //返回当前buff列表中,是否含有切换技能buff public bool HasChangeSkillBuff() { bool has = false; mBuffStatus.ForEach((BuffState buf) => { if (buf.Data.UnitChangeSkills) { has = true; } }); return has; } public class BuffMap { private HashMap Map = new HashMap(); private List For = new List(); public BuffState Get(int id, uint senderID) { var list = Map.Get(id); if (list != null) { return list.Get(senderID); } return null; } internal void Put(BuffState state) { var list = Map.Get(state.BuffID); if (list == null) { list = new BuffList(state.Data); Map.Add(state.BuffID, list); For.Add(list); } list.Add(state); } internal BuffState RemoveByKey(int id, uint senderID) { var list = Map.Get(id); if (list != null) { var st = list.Get(senderID); if (st != null) { var ret = list.Remove(senderID); if (list.Count == 0) { For.Remove(list); Map.Remove(id); } return ret; } } return null; } internal void Clear() { For.Clear(); Map.Clear(); } public void ForEach(Action action) { for (int i = For.Count - 1; i >= 0; --i) { For[i].ForEach(action); } } public int GetBuffCount() { return For.Count; } public void ForEachUpdate(int intervalMS) { for (int i = For.Count - 1; i >= 0; --i) { For[i].ForEachUpdate(intervalMS); } } public class BuffList { public readonly BuffTemplate Data; private HashMap Map = new HashMap(); private List For = new List(); public int Count { get { return Map.Count; } } internal BuffList(BuffTemplate data) { this.Data = data; } internal BuffState Remove(uint senderID) { BuffState ret; if (Data.IsDuplicating) { ret = Map.RemoveByKey(senderID); } else { ret = Map.RemoveByKey(0); } if (ret != null) { For.Remove(ret); } return ret; } internal void Add(BuffState state) { if (Data.IsDuplicating) { Map.Add(state.SenderID, state); } else { Map.Add(0, state); } For.Add(state); } internal BuffState Get(uint senderID) { if (Data.IsDuplicating) { if (senderID == 0 && Map.Count > 0) { return Map.First().Value; } else { return Map.Get(senderID); } } else { return Map.Get(0); } } internal void ForEach(Action action) { for (int i = For.Count - 1; i >= 0; --i) { action(For[i]); } } public void ForEachUpdate(int intervalMS) { for (int i = For.Count - 1; i >= 0; --i) { For[i].OnUpdate(intervalMS); } } } } public class BuffState { readonly public BuffTemplate Data; readonly public bool isEquip; readonly public ZoneUnit Owner; readonly public uint SenderID; private byte overlay_level; private int total_time; private int pass_time = 0; private float percent = 1f; private int buff_ext_data = 0; private int buff_ext_data_index = -1; private TimeInterval interval; private PopupKeyFrames keyframes; internal BuffState(BuffTemplate data, ZoneUnit owner, uint senderID, bool isEquip) { this.Data = data; this.isEquip = isEquip; this.Owner = owner; this.SenderID = senderID; } internal void Sync(byte overlayLevel, int totalTime, int passTime, int buffExtData = 0) { this.overlay_level = overlayLevel; this.total_time = totalTime; if (passTime < this.pass_time || this.pass_time == 0) { this.interval = new TimeInterval(Data.HitIntervalMS); this.interval.Tag = Data.HitKeyFrame; this.keyframes = new PopupKeyFrames(); this.keyframes.AddRange(Data.KeyFrames); this.pass_time = 0; } if (passTime > this.pass_time) { this.interval.SetPassTime(passTime); this.keyframes.PopKeyFrames(passTime, null); this.pass_time = passTime; } this.percent = Math.Min(1, pass_time / (float)total_time); this.buff_ext_data = buffExtData; SetBuffExtendDataIndex(buffExtData); } internal void OnEnd() { if (Data.EndKeyFrame != null && Data.EndKeyFrame.Effect != null) { Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, Data.EndKeyFrame.Effect)); } } internal void OnUpdate(int intervalMS) { using (var kfs = ListObjectPool.AllocAutoRelease()) { if (keyframes.PopKeyFrames(pass_time, kfs) > 0) { for (int i = 0; i < kfs.Count; i++) { BuffTemplate.KeyFrame kf = kfs[i]; if (kf.Effect != null) { Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, kf.Effect)); } } } } if (interval.Update(intervalMS)) { if (Data.HitKeyFrame != null && Data.HitKeyFrame.Effect != null) { Owner.Parent.PreQueueEvent(new UnitEffectEvent(Owner.ObjectID, Data.HitKeyFrame.Effect)); } } this.pass_time += intervalMS; if (percent < 1f) { this.percent = Math.Min(1, pass_time / (float)total_time); } } public void SyncBuffExtData(byte overlayLevel, int buffExtData) { overlay_level = overlayLevel; buff_ext_data = buffExtData; } private void SetBuffExtendDataIndex(int curBuffExtData) { if (curBuffExtData < 1) return; if (curBuffExtData < 10) { buff_ext_data_index = curBuffExtData; } else { string curString = Convert.ToString(curBuffExtData); var str = curString.Substring(curString.Length - 1, 1); if (!string.IsNullOrEmpty(str)) { int.TryParse(str, out buff_ext_data_index); } } } public int BuffID { get { return Data.ID; } } public bool IsDone { get { return (isEquip) ? false : (percent >= 1f); } } public float CDPercent { get { return percent; } } public byte OverlayLevel { get { return overlay_level; } } public int BuffTotalTime { get { return total_time; } } public int BuffLeftTime { get { return total_time - pass_time; } } public int BuffExtendData { get { return buff_ext_data; } } public int BuffExtendDataIndex { get { return buff_ext_data_index; } } } [Obsolete] public List GetBuffStatus() { var list = new List(); GetBuffStatus(list); return list; } public void GetBuffStatus(List ret) { mBuffStatus.ForEach((bs) => { ret.Add(bs); }); } public int GetBuffStatusCount() { return mBuffStatus.GetBuffCount(); } public BuffState GetBuff(int tempID, uint senderID) { return mBuffStatus.Get(tempID, senderID); } public BuffState GetBuff(int tempID) { return mBuffStatus.Get(tempID, 0); } #endregion //-------------------------------------------------------------------------------- #region PickObject private TimeExpire mPickEvent; public TimeExpire PickEvent { get { return mPickEvent; } } protected virtual void DoStartPick(UnitStartPickObjectEvent pick) { mPickEvent = new TimeExpire(pick, pick.PickTimeMS); SetCurrentState(UnitActionStatus.Pick, pick); if (mOnStartPickObject != null) { mOnStartPickObject.Invoke(this, mPickEvent); } } protected virtual void DoStopPick(UnitStopPickObjectEvent pick) { if (mPickEvent != null) { mPickEvent.End(); mPickEvent = null; } if (mOnStopPickObject != null) { mOnStopPickObject.Invoke(this, pick); } } private void UpdatePickEvent(int intervalMS) { if (mPickEvent != null) { mPickEvent.Update(intervalMS); } } #endregion //-------------------------------------------------------------------------------- #region 本地技能预演 protected virtual void DoJumpToTargetEnd() { if (mOnClientSkillSimulation != null) { mOnClientSkillSimulation.Invoke(this, SimulationSkillEnum.JumpEnd); } } #endregion 本地技能预演 #region MultiTimeLine /// /// 是否可被发现 /// virtual public bool IsVisible { get { return !IsTimeLineEnable(2); } } /// /// 是否无敌 /// virtual public bool IsInvincible { get { return IsTimeLineEnable(3); } } /// /// 此单位是否霸体 /// virtual public bool IsNoneBlock { get { return IsTimeLineEnable(0); } } /// /// 是否为眩晕 /// virtual public bool IsStun { get { return IsTimeLineEnable(1); } } /// /// 是否沉默 /// virtual public bool IsSilent { get { return IsTimeLineEnable(4); } } //-------------------------------------------------------------------------------- private UnitSyncMultiTimeLine mMultiTimeLine; private void DoUnitSyncMultiTimeLine(UnitSyncMultiTimeLine e) { this.mMultiTimeLine = e; } /// /// 指定TimeLine是否还有任务 /// /// /// public bool IsTimeLineEnable(int index) { if (mMultiTimeLine != null && mMultiTimeLine.timelines != null && index < mMultiTimeLine.timelines.Count && index >= 0) { return mMultiTimeLine.timelines[index]; } return false; } public void GetMultiTimeLineStatus(List list) { if (mMultiTimeLine != null && mMultiTimeLine.timelines != null) { list.AddRange(mMultiTimeLine.timelines); } } #endregion } }