using FL.Battle.Actions; using FL.Battle.Components; using System; using UnityEngine; using XGame.Database; using XGame.Framework; using XGame.Framework.Interfaces; using XGame.Framework.Time; namespace FL.Battle.Skills { /// /// 简单的技能表现 /// internal class SimpleSkillActor : ISkillActor, IReference { public ISkillContext Context { get; set; } private ITimer _skillTimer; //技能施法时间 private ITimer _bulletTimer; private ITimer _noAniTimer; // 没有动作时受击特效的定时器 private SkillTable _skill; private SkillVfxsTable _skillVfxs; void ISkillActor.Play(int skillId, ITarget target, Action onCompleted) { _skill = SkillTableRepo.Get(skillId); _skillVfxs = SkillVfxsTableRepo.Get(skillId); var owner = Context.Entity; //if (/*_skill.BuffTie.Length == 0*/ (ESkillVfxType)_skill.SkillVfxType is ESkillVfxType.AOE or ESkillVfxType.Trap) //{ // onCompleted(skillId, _skill.RestTime); // return; //} PlayCastVfx(target); var isHaveBullet = PlayBullets(target); PlaySkillAnimation(target, isHaveBullet); //TODO 最少900毫秒 var duration = _skill.EndureTime; //var startTime = Time.realtimeSinceStartup; _skillTimer = Context.Time.AddDelayTimer(duration, () => { //Log.Debug($"SimpleSkillActor Completed. Entity:{Context.Entity.EntityId} Name:{Context.Entity.Attr.Name} SkillId:{_skill.Id} Frame:{Time.frameCount} duration:{duration} start:{startTime} used:{Time.realtimeSinceStartup - startTime}"); var skillCD = _skill.RestTime; onCompleted.SafeInvoke(skillId, skillCD); }); } public void Stop() { _skill = null; _skillVfxs = null; _skillTimer?.Cancel(); _skillTimer = null; _bulletTimer?.Cancel(); _bulletTimer = null; _noAniTimer?.Cancel(); _noAniTimer = null; } /// /// 播放施法特效 /// /// private void PlayCastVfx(ITarget target) { if (!string.IsNullOrEmpty(_skillVfxs.CastVfx)) { VfxArgs vfxArgs = ObjectPool.Acquire(); vfxArgs.vfxName = _skillVfxs.CastVfx; vfxArgs.duration = _skillVfxs.CastVfxTime; vfxArgs.followType = EVfxFollowType.CastPoint; //(EVfxFollowType)_skillVfxs.VfxFollowType; if (_skill.Id == 200705) { // TODO var actFoword = ObjectPool.Acquire(); actFoword.forword = (target.Position - Context.Position).normalized; vfxArgs.action = actFoword; } //vfxArgs.onFinish = OnSkillVfxFinish; Context.Vfx.Play(vfxArgs); } } /// /// 播放施法动作 /// /// /// 是否有子弹,没子弹才需要播放受击特效 private void PlaySkillAnimation(ITarget target, bool isHaveBullet) { var actionName = _skillVfxs.AniName; if (actionName == EAnimationName.skill && Context.Entity.EntityType != EEntityType.Partner) { //TODO 现在只有随从有施法动作 actionName = EAnimationName.attack; } //if (skill.Id == 200707) //{ // actionName = EAnimationName.skill2; //} var skillVfxTyp = (ESkillVfxType)_skill.SkillVfxType; void onTrigger() { _noAniTimer = null; if (skillVfxTyp is ESkillVfxType.Trap or ESkillVfxType.AOE or ESkillVfxType.AOENoMove or ESkillVfxType.AOETarget) { //播放召唤兽特效 SummonBeat(target); } else { // target不为空,播放受击特效 PlayHitVfx(target); } } if (actionName >= EAnimationName.attack) { // 有攻击动作 var skillId = _skillVfxs.Id; var entityName = Context.Entity.Attr.Name; var actArgs = new AnimationPlayArgs() { aniName = actionName, //onCompleted = () => //{ // Assert.IsNotNull(Context, $"技能动作回调,Context不能为空. skillId:{skillId} entityName:{entityName}"); // Context.Animator.Play(EAnimationName.idle/* + UnityEngine.Random.Range(0, 100) % 2*/); //} }; if (!isHaveBullet) { // 没有子弹,300毫秒后召唤或者播放受击特效 actArgs.triggerFrame = _skillVfxs.SkillVfxDelay; actArgs.onTrigger = onTrigger; } Context.Animator.Play(actArgs); } else if (!isHaveBullet) { // 没有子弹,300毫秒后召唤或者播放受击特效 _noAniTimer = Context.Time.AddDelayTimer(_skillVfxs.SkillVfxDelay, onTrigger); } } /// /// 播放子弹 /// /// /// private bool PlayBullets(ITarget target) { var bulletCount = _skill.SkillVfxType / 100 == 1 ? _skillVfxs.SkillVfxCount : 0; if (bulletCount > 0) { // 有子弹,设置子弹的定时器 var targetId = target?.Entity.EntityId ?? 0; var to = target?.Position ?? Vector3.zero; var randomIdx = -1; if (_skill.SkillVfxType == (int)ESkillVfxType.BulletMortar) { // 随机一个索引确保有一个目标坐标在敌人位置 randomIdx = UnityEngine.Random.Range(0, 1000) % bulletCount; } _bulletTimer = Context.Time.AddDelayLooperTimer(_skillVfxs.SkillVfxDelay, _skillVfxs.SkillVfxInterval > 0 ? _skillVfxs.SkillVfxInterval : 500, (times) => { //XGame.Log.Debug($"Skill bullet loop Id:{skillId} Count:{bulletCount} Times:{times}"); if (times == bulletCount) _bulletTimer = null; var bulletArgs = new GenBulletDto() { masterId = Context.Entity.EntityId, masterType = Context.Entity.EntityType, targetId = targetId, tableId = _skill.Id, from = Context.Position, to = to, }; if (randomIdx != -1) { bulletArgs.to = Context.Selector.RandomPosition(Context.Entity.EntityType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster, times - 1 == randomIdx); } EventSingle.Instance.Notify(EventDefine.GameMainMapGenBullet, bulletArgs); }, bulletCount); return true; } return false; } /// /// 召唤技能 /// private void SummonBeat(ITarget target) { var dto = new GenSummonsDto() { masterId = Context.Entity.EntityId, masterType = Context.Entity.EntityType, tableId = _skill.Id, from = Context.Position, count = _skillVfxs.SkillVfxCount }; if (target != null) { dto.targetId = target.Entity.EntityId; dto.targetType = target.Entity.EntityType; dto.from = target.Position; } else { //TODO 根据技能配置确定目标类型 dto.targetType = dto.masterType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster; } EventSingle.Instance.Notify(EventDefine.GameMainMapGenCommons, dto); } /// /// 受击特效 /// /// private void PlayHitVfx(ITarget target) { var to = target?.Position ?? Vector3.zero; var hitVfx = _skillVfxs.HitVfx; if (!string.IsNullOrEmpty(hitVfx)) { var actPosition = ObjectPool.Acquire(); actPosition.position = to; var hitArgs = ObjectPool.Acquire(); hitArgs.vfxName = _skillVfxs.HitVfx; hitArgs.duration = _skillVfxs.HitVfxTime; //hitEffect == "effect_sk_hit_13521" ? 3000 : 2000; hitArgs.followType = EVfxFollowType.World; hitArgs.action = actPosition; Context.Vfx.Play(hitArgs); } Assert.IsNotNull(target, $"PlayHitVfx target null. skill:{_skill.Id}"); if (!target.IsDead) { target.Calculation.Damage(-_skill.Damage, Context.Entity.EntityId, _skill); } } void IReference.Clear() { Stop(); Context = null; } } }