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();
}
}
}