using DG.Tweening; using FL.Battle.Vfxs; using FL.Nodes.GameMain; using System; using UnityEngine; using XGame; using XGame.Database; using XGame.Framework; using XGame.Framework.Components; using XGame.Framework.Interfaces; using XGame.Framework.Map; using XGame.Framework.Time; namespace FL.Battle { public interface IAttackContext { IEntity Entity { get; } Transform ActionRoot { get; } Vector3 Position { get; } ITimeModule Time { get; } SpineComponent Spine { get; } VfxComponent Vfx { get; } void FindTarget(int skillId); void EnqueueSkill(int skillId); } /// /// 普通攻击 /// public class AttackComponent : Component, IReset { public override int Order => 0; private IEntityView _targetView; private IEntity _target; private IAlarm _cdAlarm; private long _lastAtkTime; public void SetTarget(IEntityView targetView, IEntity target) { _targetView = targetView; _target = target; } public void Start(Action onCompleted) { var owner = Context.Entity; //if (_target == null) //{ // Log.Error($"OnStartAttack Error: Target is null. TableId:{Context.Owner.TableId} State:{Context.Owner.State}"); // return; //} if (_target == null || _target.State == EEntityState.Dead) { Stop(); onCompleted.SafeInvoke(); Context.FindTarget(0); return; } if (owner.State == EEntityState.Dead) { Stop(); onCompleted.SafeInvoke(); return; } if (Context.Entity.EntityType != EEntityType.Partner) { // 玩家或者怪物 PlaySkillAtk(onCompleted); //Context.Spine.Play(ActionName.attack, () => //{ // OnHitFrame(); // OnAttackEnd(); // Context.Spine.Play(ActionName.idle); // onCompleted?.Invoke(); //}); return; } // 随从 PartnerType partnerType = (PartnerType)((Context.Entity.TableId / 100) % 10); //单手剑、双手剑、法杖使用法术类型攻击 var isSkill = partnerType == PartnerType.Sword || partnerType == PartnerType.SwordTH || partnerType == PartnerType.Staff; if (isSkill) { PlaySkillAtk(onCompleted); } else { if (partnerType == PartnerType.Axe) { Log.Debug($"Axe Attack. Frame:{UnityEngine.Time.frameCount}"); } PartnerNormalAtk(onCompleted); } } private void PlaySkillAtk(Action onCompleted) { Context.Spine.Play(ActionName.attack, () => { Context.Spine.Play(ActionName.idle/* + UnityEngine.Random.Range(0, 100) % 2*/); if (_target == null || _target.State == EEntityState.Dead) { OnAttackEnd(); onCompleted.SafeInvoke(); return; } var hpValue = _targetView.GetComponent().CalculateHit(-Context.Entity.Attr.Attack); var to = _targetView.Position; void PushHpAdd() { var hpAdd = new HpAddDto() { position = to, hpValue = hpValue, }; EventSingle.Instance.Notify(EventDefine.GameMainMapHpAdd, hpAdd); } var actMove = ObjectPool.Acquire(); actMove.from = Context.Position; actMove.to = to; actMove.duration = 500; var bulletArgs = ObjectPool.Acquire(); bulletArgs.vfxName = Context.Entity.EntityType == EEntityType.Partner ? "effect_atk_bullet_20181" : "atk_007"; bulletArgs.duration = 500; bulletArgs.followType = EVfxFollowType.World; bulletArgs.action = actMove; bulletArgs.onFinish = PushHpAdd; var actPosition = ObjectPool.Acquire(); actPosition.position = to; var hitArgs = ObjectPool.Acquire(); hitArgs.vfxName = Context.Entity.EntityType == EEntityType.Partner ? "effect_atk_hit_20181" : "hit_007"; hitArgs.duration = 2000; hitArgs.followType = EVfxFollowType.World; hitArgs.action = actPosition; bulletArgs.next = hitArgs; Context.Vfx.Play(bulletArgs); //var dto = new SkillEffectDto() //{ // skillEffect = "", // skillTime = 667, // from = Context.Position, // to = _targetView.Position, // hpAdd = new HpAddDto() // { // position = _targetView.Position, // hpValue = hpValue, // } //}; //if (Context.Entity.EntityType == EEntityType.Partner) //{ // dto.hitEffect = "effect_atk_hit_20181"; // dto.bulletEffect = "effect_atk_bullet_20181"; //} //else //{ // dto.hitEffect = "hit_007"; // dto.bulletEffect = "atk_007"; //} //EventSingle.Instance.Notify(EventDefine.GameMainMapSkillEffect, dto); OnAttackEnd(); onCompleted.SafeInvoke(); }); } private void PartnerNormalAtk(Action onCompleted) { var from = Context.Position; var to = _targetView.Position; var distance = Vector3.Distance(from, to); var atkRange = 0;// _target.Attr.Radius; if (distance > atkRange) { // 大于攻击距离 //Log.Info($"PlayAttack:Owner:{Handle.Owner.EntityId} Target:{_target.EntityId}"); var dir = (to - from).normalized; var moveDis = distance - atkRange; var atkPosition = Context.ActionRoot.localPosition + dir * moveDis; var moveTime = 0.3f;// Mathf.Abs(moveDis) / 5; //Context.ActionRoot.localPosition = atkPosition; //Context.Spine.Play(ActionName.attack, () => //{ // Context.Spine.Play(ActionName.idle/* + UnityEngine.Random.Range(0, 100) % 2*/); // Context.ActionRoot.localPosition = Vector3.zero; // var endArgs = ObjectPool.Acquire(); // endArgs.vfxName = "effect_melee_appear"; // endArgs.duration = 500; // Context.Vfx.Play(endArgs); // OnHitFrame(); // OnAttackEnd(); // onCompleted?.Invoke(); //}); var startArgs = ObjectPool.Acquire(); startArgs.vfxName = "effect_trail"; startArgs.duration = 300; startArgs.followType= EVfxFollowType.CastPoint; Context.Vfx.Play(startArgs); MoveTo(atkPosition, moveTime, 0, () => { OnHitFrame(); //等待拖尾消失 MoveTo(Vector3.zero, moveTime, 0.2f, () => { OnAttackEnd(); onCompleted?.Invoke(); }); }); } else { OnHitFrame(); OnAttackEnd(); onCompleted?.Invoke(); } } public void Stop() { Context.ActionRoot.DOKill(); Context.ActionRoot.localPosition = Vector3.zero; _cdAlarm?.Cancel(); _cdAlarm = null; _targetView = null; _target = null; } protected override void OnDispose() { Stop(); } private void MoveTo(Vector3 to, float duration, float delay, Action callback) { var tween = Context.ActionRoot.DOLocalMove(to, duration); if (delay > 0) { tween.SetDelay(delay); } tween.onComplete = () => { callback.SafeInvoke(); }; } private void OnHitFrame() { // 攻击帧回调 //TODO 目标受击 if (_target == null || _target.State == EEntityState.Dead) return; var position = _targetView.Position; var hpValue = _targetView.GetComponent().CalculateHit(-Context.Entity.Attr.Attack); EventSingle.Instance.Notify(EventDefine.GameMainMapHpAdd, new HpAddDto() { position = position, hpValue = hpValue }); } private void OnAttackEnd() { _lastAtkTime = Context.Time.GetNowTime(); //冷却计时 var time = _lastAtkTime + Context.Entity.Attr.AttackCD; _cdAlarm = Context.Time.AddAlarm(() => { Context.EnqueueSkill(0); }, time); } void IReset.Reset() { Stop(); } } }