123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- using FL.Battle.Skills;
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using XGame;
- using XGame.Database;
- using XGame.Framework;
- using XGame.Framework.Components;
- using XGame.Framework.Interfaces;
- using XGame.Framework.Time;
- namespace FL.Battle.Components
- {
- public interface ISkillContext
- {
- IEntity Entity { get; }
- Transform ActionRoot { get; }
- Vector3 Position { get; }
- ITimeModule Time { get; }
- IAnimator Animator { get; }
- VfxComponent Vfx { get; }
- ITargetSelector Selector { get; }
- void FindTargetAsync(Action<ITarget> onSelected);
- void OnSkillStart(int skillId);
- void OnSkillStop(int skillId);
- }
- public class SkillComponent : Component<ISkillContext>, IReset
- {
- private const int NO_SKILL = -1;
- private Dictionary<int, ISkillActor> _usingActors;
- public ITarget Target { get; private set; }
- private int _playingSkill = NO_SKILL;
- /// <summary>
- /// 技能冷却定时器
- /// key: skillId
- /// value: ITimer
- /// </summary>
- private Dictionary<int, ITimer> _skillCDTimers = new Dictionary<int, ITimer>();
- /// <summary>
- /// 技能队列
- /// 冷却时间到的技能进入队列等待播放
- /// </summary>
- private List<int> _skillQueue = new List<int>();
- private Dictionary<int, long> _buffSkillInfos;
- protected override void OnDisable()
- {
- Stop(false);
- }
- protected override void OnDispose()
- {
- Stop(true);
- }
- void IReset.Reset()
- {
- Stop(false);
- }
- #region 目标选择
- ///// <summary>
- ///// 默认的目标类型
- ///// </summary>
- //private EEntityType DefaultTargetType => Context.Entity.EntityType == EEntityType.Monster ? EEntityType.Player : EEntityType.Monster;
- //public void FindTargetAsync(int skillId = 0)
- //{
- // Context.Selector.FindAsync(this, DefaultTargetType, (target) =>
- // {
- // Context.Entity.State = EEntityState.Battle;
- // Target = target;
- // EnqueueSkill(skillId);
- // StartSkill();
- // });
- //}
- private void OnSelectedTarget(ITarget target)
- {
- Context.Entity.AddState(EEntityState.Battle);
- Target = target;
- StartSkill();
- }
- public void Prepare()
- {
- //初始化技能
- foreach (var skillId in Context.Entity.ActiveSkillIds)
- {
- EnqueueSkill(skillId);
- }
- //选择目标
- Context.FindTargetAsync(OnSelectedTarget);
- }
- #endregion
- public bool FindSkillByElementType(EElementType elementType, out int skillId)
- {
- foreach (var item in Context.Entity.ActiveSkillIds)
- {
- var table = SkillTableRepo.Get(item);
- if (table.Element == elementType)
- {
- skillId = item;
- return true;
- }
- }
- skillId = 0;
- return false;
- }
- /// <summary>
- /// 返回技能的最小剩余时间
- /// </summary>
- /// <returns></returns>
- public int GetMinCooldown()
- {
- if (_skillCDTimers.Count == 0)
- return 0;
- var remain = int.MaxValue;
- foreach (var skillId in Context.Entity.ActiveSkillIds)
- { // 有主动技能在队列里
- if (_skillQueue.Contains(skillId))
- return 0;
- if (_skillCDTimers.TryGetValue(skillId, out var timer))
- {
- var temp = (timer as Timer).RemainTime;
- if (temp < remain)
- remain = temp;
- }
- }
- return remain;
- }
- /// <summary>
- /// 冷却时间到的技能进入队列等待播放
- /// </summary>
- /// <param name="skillId"></param>
- private bool EnqueueSkill(int skillId)
- {
- if (Context.Entity.IsDead)
- return false;
- //var entityName = Context.Entity.Attr.Name;
- if (_playingSkill == skillId)
- {
- //Log.Debug($"技能正在施法. Entity:{entityName} Skill:{skillId}");
- return false;
- }
- if (_skillCDTimers.ContainsKey(skillId))
- { // 技能还在cd中
- //Log.Debug($"技能还在冷却中. Entity:{entityName} Skill:{skillId}");
- return false;
- }
- if (_skillQueue.Contains(skillId))
- {
- //Log.Debug($"技能队列重复. Entity:{entityName} Skill:{skillId}");
- return false;
- }
- //Log.Debug($"EnqueueSkill Entity:{entityName} Skill:{skillId} _playingSkill:{_playingSkill}");
- _skillQueue.Add(skillId);
- return true;
- }
- private void StartSkill()
- {
- if (_playingSkill != -1)
- return;
- // 没有技能 || 已死亡 || 移动中
- if (_skillQueue.Count == 0 || (Context.Entity.IsState(EEntityState.SkillBroken | EEntityState.Moving)))
- return;
- var skillId = _skillQueue[0];
- var isNoTarget = IsNoTarget(skillId);
- // 召唤类技能,不需要选目标
- if (isNoTarget == false && (Target == null || Target.IsDead))
- { // 没有目标或目标已死亡
- Context.FindTargetAsync(OnSelectedTarget);
- return;
- }
- _skillQueue.RemoveAt(0);
- _playingSkill = skillId;
- //Log.Debug($"StartSkill Entity:{Entity.Attr.Name} Skill:{skillId}");
- Context.OnSkillStart(skillId);
- GetActor(skillId).Play(skillId, isNoTarget ? null : Target, OnSkillCompleted);
- }
- private void OnSkillCompleted(int skillId, int skillCD)
- {
- RemoveActor(skillId);
- Context.OnSkillStop(skillId);
- //var uid = UIDDefine.New();
- //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} start");
- if (skillCD > 0)
- {
- var cdTimer = Context.Time.AddDelayTimer(skillCD, () =>
- {
- //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} remove");
- _skillCDTimers.Remove(skillId);
- if (RemoveTickSkill(skillId) == false)
- { // 非触发类技能,加入技能队列
- EnqueueSkill(skillId);
- }
- StartSkill();
- });
- _skillCDTimers.Add(skillId, cdTimer);
- }
- _playingSkill = NO_SKILL;
- StartSkill();
- }
- public void Stop(bool isDispose)
- {
- if (_usingActors != null)
- {
- foreach (var actor in _usingActors.Values)
- {
- ObjectPool.Recycle(actor);
- }
- _usingActors.Clear();
- }
- _buffSkillInfos?.Clear();
- _skillQueue.Clear();
- _playingSkill = NO_SKILL;
- foreach (var item in _skillCDTimers)
- {
- item.Value.Cancel();
- }
- _skillCDTimers.Clear();
- Target = null;
- }
- /// <summary>
- /// 技能是否不需要目标
- /// </summary>
- /// <param name="skillId"></param>
- /// <returns></returns>
- private bool IsNoTarget(int skillId)
- {
- var skillTable = SkillTableRepo.Get(skillId);
- if ((ESkillVfxType)skillTable.SkillVfxType is ESkillVfxType.BulletMortar or ESkillVfxType.Trap or ESkillVfxType.AOE or ESkillVfxType.AOENoMove)
- return true;
- return false;
- }
- private ISkillActor GetActor(int skillId)
- {
- //if (skillId == 0)
- //{
- // return _attackActor ??= new AttackActor() { Context = Context };
- //}
- //return _skillActor ??= new SimpleSkillActor() { Context = Context };
- if (_usingActors == null)
- {
- _usingActors = new Dictionary<int, ISkillActor>();
- }
- var actor = ObjectPool.Acquire<SimpleSkillActor>();
- actor.Context = Context;
- _usingActors.Add(skillId, actor);
- return actor;
- }
- private void RemoveActor(int skillId)
- {
- if (_usingActors?.TryGetValue(skillId, out var actor) ?? false)
- {
- _usingActors.Remove(skillId);
- ObjectPool.Recycle(actor);
- }
- }
- #region 触发类技能
- public void TryTickSkill(int skillId, long targetId, int probability = 10000)
- {
- if (_buffSkillInfos == null)
- _buffSkillInfos = new Dictionary<int, long>();
- if (_buffSkillInfos.ContainsKey(skillId))
- { // 已经触发 或 还在cd中
- return;
- }
- if (MathUtils.RandomTick(probability) == false)
- {
- return;
- }
- // 触发技能
- var skillVfxs = SkillVfxsTableRepo.Get(skillId);
- if (skillVfxs.AniName >= EAnimationName.attack)
- { // 有动作,昏迷等状态不触发
- if (Context.Entity.IsState(EEntityState.SkillBroken))
- {
- return;
- }
- // 加到技能播放队列
- if (EnqueueSkill(skillId))
- {
- _buffSkillInfos.Add(skillId, targetId);
- }
- }
- else
- { // 无动作,直接播放
- var target = Context.Selector.GetTarget(targetId);
- var isNoTarget = IsNoTarget(skillId);
- // 召唤类技能,不需要选目标
- if (isNoTarget == false && (target == null || target.IsDead))
- { // 没有目标或目标已死亡
- return;
- }
- Log.Debug($"触发技能 SkillId:{skillId} entity:{Context.Entity.EntityId} targetId:{targetId}");
- _buffSkillInfos.Add(skillId, targetId);
- GetActor(skillId).Play(skillId, isNoTarget ? null : target, OnTickSkillCompleted);
- }
- }
- private void OnTickSkillCompleted(int skillId, int skillCD)
- {
- RemoveActor(skillId);
- //var uid = UIDDefine.New();
- //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} start");
- if (skillCD > 0)
- {
- var cdTimer = Context.Time.AddDelayTimer(skillCD, () =>
- {
- //Log.Debug($"OnSkillCompleted Entity:{Entity.Attr.Name} Skill:{skillId} CD:{skillCD} UID:{uid} remove");
- _skillCDTimers.Remove(skillId);
- RemoveTickSkill(skillId);
- });
- _skillCDTimers.Add(skillId, cdTimer);
- }
- }
- /// <summary>
- /// 移除触发类技能
- /// </summary>
- /// <param name="skillId"></param>
- /// <returns></returns>
- private bool RemoveTickSkill(int skillId)
- {
- return _buffSkillInfos?.Remove(skillId) ?? false;
- }
- #endregion
- #region 被动技能
- //public void TryTickPassiveSkills(Func<SkillTable, bool> extenCondition)
- //{
- // if (Context.Entity.PassiveSkillIds == null) return;
- // foreach(var skillId in Context.Entity.PassiveSkillIds)
- // {
- // var skill = SkillTableRepo.Get(skillId);
- // if (skill == null) continue;
- // if (!extenCondition(skill)) continue;
- // TryTickSkill(skillId, Context.Entity.EntityId);
- // }
- //}
- #endregion
- #region IPauseable 实现
- public void Pause(EEntityState state)
- {
- if (state is EEntityState.Palsy)
- {
- foreach (var item in _skillCDTimers.Values)
- { //TODO 冷却中的技能cd回退到80%
- var timer = item as Timer;
- timer.ResetForAxis(Mathf.RoundToInt(timer.TimeAxis * KeyValue.ParalysisDelayFac.ToRealFloat()));
- }
- }
- if (_playingSkill != NO_SKILL)
- { // 有技能正在施法,直接进入cd
- var skillId = _playingSkill;
- var skill = SkillTableRepo.Get(skillId);
- OnSkillCompleted(skillId, skill.RestTime);
- }
- if (_buffSkillInfos != null)
- { // 移除队列里的触发类技能
- for (var i = _skillQueue.Count - 1; i >= 0; i--)
- {
- var skillId = _skillQueue[i];
- if (_buffSkillInfos.ContainsKey(skillId))
- {
- _buffSkillInfos.Remove(skillId);
- _skillQueue.RemoveAt(i);
- }
- }
- }
- }
- public void Resume()
- {
- StartSkill();
- }
- #endregion
- }
- }
|