using System; using System.Collections.Generic; using ActionGameFramework.Audio; using ActionGameFramework.Health; using Core.Health; using TowerDefense.Targetting; using TowerDefense.Towers; using TowerDefense.Towers.Projectiles; using UnityEngine; using KTGMGemClient; using TowerDefense.Agents; using TowerDefense.Level; namespace TowerDefense.Affectors { /// /// The common effect for handling firing projectiles to attack /// /// Requires an ILauncher but it is not automatically added /// Add an ILauncher implementation to this GameObject before you add this script /// [RequireComponent(typeof(ILauncher))] public class AttackAffector : Affector, ITowerRadiusProvider { /// /// The projectile used to attack /// public GameObject projectile; /// /// 木塔最后一击是特殊攻击,需要替换projectile /// public GameObject woodProjectile_SP; /// /// The list of points to launch the projectiles from /// public Transform[] projectilePoints; /// /// The reference to the center point where the tower will search from /// public Transform epicenter; /// /// 如果是多目标攻击,最多攻击目标 /// public int maxAttackNum = 1; /// /// 是否木属性数据 /// public bool bWoodAffector = false; /// /// The audio source to play when firing /// public RandomAudioSource randomAudioSource; /// /// Gets the targetter /// public Targetter towerTargetter; /// /// Color of effect radius visualization /// public Color radiusEffectColor; /// /// Search condition /// public Filter searchCondition; /// /// Fire condition /// public Filter fireCondition; /// /// The reference to the attached launcher /// protected ILauncher m_Launcher; /// /// The time before firing is possible /// protected float m_FireTimer; protected float freezeBreathTimer; /// /// Reference to the current tracked enemy /// protected Targetable m_TrackingEnemy; public TowerLevel towerLevel; /// /// 处理装弹时间. /// protected float fillBulletTime = 0.0f; /// /// 火精灵充能时间 /// protected float energyCalTime = 0; protected float fInEnergy = 0; protected float fBackupTimer = 0.0f; /// /// 水精灵的充能时间 /// protected float freezeBreathCallTime = 0; protected float inFreezeBreath; protected float freezeBreathBackTimer = 0; private int towerAttributeId; /// /// 火精灵技能固定攻击倍速 /// /// protected float fireSpeed { get; set; } = 5f; /// /// 木属性精灵蓄力时间 /// protected float woodChargeTime { get; set; } = 1.5f; protected float woodRemainChargeTime { get; set; } /// /// 蓄力特效时间 /// protected float woodChargeEffectTime { get; set; } /// /// 木属性精灵蓄力特效 /// public ParticleSystem WoodChargeEffect; public Transform WoodChargeTransform; /// /// 木属性正在瞄准的Agent /// protected Agent woodAimAgent; /// /// 火精灵攻击最终攻击倍速,里面计算了buff增加的倍速 /// /// public float finalFireSpeed { get { FireRateAdd fireRateAdd = (FireRateAdd)EndlessBuffManager.instance.GetBuffInstanceByType(EndlessBuffEffectType.FireRateAdd); float rateAdd = 0; if (fireRateAdd != null) rateAdd = fireRateAdd.GetFireSpeedAdd(towerPtr.ElfId); return rateAdd > 1 ? rateAdd : fireSpeed; } } /// /// Gets the search rate from the targetter /// public float searchRate { get { return towerTargetter.searchRate; } set { towerTargetter.searchRate = value; } } /// /// Gets the targetable /// public Targetable trackingEnemy { get { return m_TrackingEnemy; } } /// /// Gets or sets the attack radius /// public float effectRadius { get { return towerTargetter.effectRadius; } } public Color effectColor { get { return radiusEffectColor; } } public Targetter targetter { get { return towerTargetter; } } /// /// Initializes the attack affector /// public override void Initialize(IAlignmentProvider affectorAlignment) { Initialize(affectorAlignment, -1); } /// /// 返回可能存在的Targetter. /// /// public override TowerDefense.Targetting.Targetter GetTargetter() { return targetter; } /// /// Initialises the attack affector with a layer mask /// public override void Initialize(IAlignmentProvider affectorAlignment, LayerMask mask) { base.Initialize(affectorAlignment, mask); SetUpTimers(); towerTargetter.ResetTargetter(); towerTargetter.alignment = affectorAlignment; towerTargetter.acquiredTarget += OnAcquiredTarget; // towerTargetter.lostTarget += OnLostTarget; GetAudioEnum(); myTower = transform.parent.GetComponent(); } private AudioEnum audioEnum;//当前音乐的种类 void GetAudioEnum() { if (transform.parent.name.StartsWith("GrowUpTower")) { //火元素 audioEnum = AudioEnum.FireTAttack; } else if (transform.parent.name.StartsWith("BlinkTower")) { //木元素 audioEnum = AudioEnum.WoodTAttack; } else if (transform.parent.name.StartsWith("CopyCatTower")) { //水元素 audioEnum = AudioEnum.WaterTAttack; } } void OnDestroy() { towerTargetter.acquiredTarget -= OnAcquiredTarget; // towerTargetter.lostTarget -= OnLostTarget; } void OnAcquiredTarget(Targetable acquiredTarget) { // m_TrackingEnemy = acquiredTarget; } public Damager damagerProjectile { get { return projectile == null ? null : projectile.GetComponent(); } } public Damager damagerProjectile1 { get { return projectile == null ? null : projectile.GetComponent(); } } public Damager damagerProjectile2 { get { return projectile == null ? null : projectile.GetComponent(); } } /// /// Initialise the RepeatingTimer /// protected virtual void SetUpTimers() { m_Launcher = GetComponent(); } TowerLevel myTower; bool fireState = false; protected void updateTowerSkillData() { HandleBullet(); HandleEnergy(); HandleFreezeBreath(); } // 处理木精灵装填子弹 private void HandleBullet() { if (woodRemainChargeTime > 0f) woodRemainChargeTime -= Time.deltaTime; if (woodChargeEffectTime > 0f) { woodChargeEffectTime -= Time.deltaTime; UpdateWoodAim(); if (woodChargeEffectTime <= 0) { towerPtr.IsWoodCharge = false; CancelWoodAim(); WoodChargeEffect.Stop(); } } // 预留出来装填子弹的时间. if (fillBulletTime > 0) { fillBulletTime -= Time.deltaTime; if (fillBulletTime <= 0.3f) { if (towerPtr && towerPtr.bulletCtl) towerPtr.bulletCtl.ResetToMaxBullet(); } if (fillBulletTime <= 0) { fillBulletTime = 0; } } } /// /// 更新木属性瞄准 /// private void UpdateWoodAim() { // 离得最近的 Agent Agent agent = AgentInsManager.instance.GetMinDisAgent(waveLineID, false); if (agent != null) { // 还没有瞄准目标,直接分配 if (woodAimAgent == null) { woodAimAgent = agent; towerPtr.WoodAimAgent = agent; if (agent.WoodAimCount == 0) agent.PlayWoodAimEffect(); ++agent.WoodAimCount; } // 有小怪走到之前瞄准目标的前面 或者 之前瞄准的目标死亡,切换瞄准目标 else if (woodAimAgent.Id != agent.Id) { if (woodAimAgent.WoodAimCount > 0) { --woodAimAgent.WoodAimCount; if (woodAimAgent.WoodAimCount == 0) woodAimAgent.StopWoodAimEffect(); } woodAimAgent = agent; towerPtr.WoodAimAgent = agent; if (agent.WoodAimCount == 0) agent.PlayWoodAimEffect(); ++agent.WoodAimCount; } } } /// /// 取消木属性瞄准 /// private void CancelWoodAim() { if (woodAimAgent != null) { if (woodAimAgent.WoodAimCount > 0) { --woodAimAgent.WoodAimCount; if (woodAimAgent.WoodAimCount == 0) woodAimAgent.StopWoodAimEffect(); } } woodAimAgent = null; } // 处理火精灵充能 private void HandleEnergy() { // 充能时间的处理 if (towerPtr && towerPtr.energyCtl) { if (fInEnergy <= 0) { energyCalTime += Time.deltaTime; float process = energyCalTime % 11.0f; int proint = (int)Math.Floor(process); proint += towerPtr.uiProOffset; towerPtr.energyCtl.SetEnergyProcessFloat(process); if (proint == 10) { fireState = true; fInEnergy = finalFireSpeed; // 设置多倍攻击速度 fBackupTimer = m_FireTimer; m_FireTimer = m_FireTimer / finalFireSpeed; towerPtr.uiProOffset = 0; towerPtr.PlayEnergyEffect(true); } } else { fInEnergy -= Time.deltaTime; if (fInEnergy <= 0) { fireState = false; //EventCenter.Ins.BroadCast((int)KTGMGemClient.EventType.FireTowerChargeEnd); fInEnergy = 0.0f; energyCalTime = 0.0f; towerPtr.energyCtl.SetEnergyProgress(0); // 恢复正常攻击速度 m_FireTimer = fBackupTimer; towerPtr.PlayEnergyEffect(false); } } } } // 处理水精灵的充能 private void HandleFreezeBreath() { if (towerPtr && towerPtr.FreezeBreathCtrl) { Damager damager = projectile.gameObject.GetComponent(); float finalDamage = damager.damage; List list = EndlessBuffManager.instance.GetBuffListByEffectType(EndlessBuffEffectType.AttackAdd, towerPtr.ElfId); float ratio = 0; float add = 0; if (list.Count > 0) { for (int i = 0; i < list.Count; ++i) { ratio += list[i].Config.buff_effect[1]; add += list[i].Config.buff_effect[2]; } } finalDamage += (ratio / 100f) * finalDamage + add; if (inFreezeBreath <= 0) { freezeBreathCallTime += Time.deltaTime; float process = freezeBreathCallTime % (FreezeBreath.ChargeTime + 1); int processInt = (int)Mathf.Floor(process); processInt += towerPtr.FreezeBreathProgressOffset; towerPtr.FreezeBreathCtrl.SetProgress(process); if (processInt == (int)Mathf.Floor(FreezeBreath.ChargeTime)) { inFreezeBreath = towerPtr.FreezeBreathCtrl.SkillTime; towerPtr.FreezeBreathProgressOffset = 0; towerPtr.PlayFreezeBreathEffect(true); towerPtr.FreezeBreathCtrl.ReleaseCount = 1; towerPtr.FreezeBreathCtrl.PlayFreezeEffect(waveLineID); towerPtr.FreezeBreathCtrl.ReleaseFreeze(waveLineID, finalDamage, damager.alignmentProvider); } } else { inFreezeBreath -= Time.deltaTime; int time = Mathf.FloorToInt(towerPtr.FreezeBreathCtrl.EffectTime / (towerPtr.FreezeBreathCtrl.DamageCount - 1) * 10); int interval = Mathf.FloorToInt(inFreezeBreath * 10); int offset = Mathf.FloorToInt(towerPtr.FreezeBreathCtrl.SkillTime * 10) - Mathf.FloorToInt(towerPtr.FreezeBreathCtrl.EffectTime * 10); if (interval == time * (towerPtr.FreezeBreathCtrl.DamageCount - towerPtr.FreezeBreathCtrl.ReleaseCount - 1) + offset && towerPtr.FreezeBreathCtrl.ReleaseCount < towerPtr.FreezeBreathCtrl.DamageCount) { ++towerPtr.FreezeBreathCtrl.ReleaseCount; towerPtr.FreezeBreathCtrl.ReleaseFreeze(waveLineID, finalDamage, damager.alignmentProvider); } if (inFreezeBreath <= 0) { inFreezeBreath = 0; freezeBreathCallTime = 0; towerPtr.FreezeBreathCtrl.SetProgress(0); towerPtr.PlayFreezeBreathEffect(false); } } } } /// /// This function is called when the object becomes enabled and active. /// void OnEnable() { if (towerPtr && towerPtr.energyCtl) { if (fireState) { towerPtr.PlayEnergyEffect(true); } } // 如果在木属性蓄力期间,让瞄准动画播放 if (towerPtr && towerPtr.IsWoodCharge && woodAimAgent != null) woodAimAgent.PlayWoodAimEffect(); } /// /// This function is called when the behaviour becomes disabled or inactive. /// void OnDisable() { if (towerPtr && towerPtr.energyCtl) { towerPtr.PlayEnergyEffect(false, false); } if (towerPtr && towerPtr.FreezeBreathCtrl) towerPtr.PlayFreezeBreathEffect(false, false); } /// /// Update the timers /// protected virtual void Update() { if (m_Launcher == null || towerPtr != null && !towerPtr.CanAttack) return; // 处理当前Affector所在Tower对应的技能 updateTowerSkillData(); m_FireTimer -= Time.deltaTime; m_TrackingEnemy = targetter.GetTarget(waveLineID, bWoodAffector); if (m_TrackingEnemy != null && m_FireTimer < 0) { m_FireTimer = towerLevel.GetFireRate(); if (fInEnergy > 0) m_FireTimer /= finalFireSpeed; towerLevel.FireSpeed = fInEnergy > 0 ? finalFireSpeed : 1f; if (towerPtr && towerPtr.bulletCtl != null) { int bnum = towerPtr.bulletCtl.GetCtlProgress(); // 蓄力时间内不攻击 if (bnum == 0 || woodRemainChargeTime > 0f) return; } if (towerPtr && towerPtr.FreezeBreathCtrl != null) { // 冷冻气息期间不攻击 if (inFreezeBreath > 0.0001f) return; } towerLevel.ChangeState(TowerActionState.Attack); } } /// /// Common logic when attacking /// 调用攻击的核心函数,由这个函数发起真正的攻击,多目标或者单目标 /// public virtual void FireProjectile() { m_TrackingEnemy = targetter.GetTarget(waveLineID, bWoodAffector); GameObject go = damagerProjectile.gameObject; if (m_TrackingEnemy == null || fillBulletTime > 0) return; Damager goDamager = go.GetComponent(); goDamager.IsEnhancedBullet = false; goDamager.TowerPtr = towerPtr; // 处理子弹充能相关的内容 if (towerPtr && towerPtr.bulletCtl != null) { int bnum = towerPtr.bulletCtl.decBullet(); // 暴击子弹的数量,如果获得相应buff可能会修改暴击子弹数量 int critBulletNum = towerPtr.bulletCtl.CritBulletNum; if (bnum < critBulletNum) { if (bnum == 0) // 不需要装填时间 fillBulletTime = 0.1f; //这里需要替换特效 var poolable = Core.Utilities.Poolable.TryGetPoolable(woodProjectile_SP); go = poolable.gameObject; Damager tmpDamager = go.GetComponent(); tmpDamager.damageMulti = 10.0f; tmpDamager.damage = damagerProjectile.damage; tmpDamager.IsEnhancedBullet = true; tmpDamager.TowerPtr = towerPtr; } // 下一颗子弹是强化子弹,然后直接蓄力 if (bnum - 1 >= 0 && bnum - 1 < critBulletNum) { woodRemainChargeTime = woodChargeTime; towerPtr.IsWoodCharge = true; DecreaseWoodChargeTime decreaseWoodChargeTime = (DecreaseWoodChargeTime)EndlessBuffManager.instance.GetBuffInstanceByType(EndlessBuffEffectType.DecreaseWoodChargeTime); if (decreaseWoodChargeTime != null) woodRemainChargeTime = decreaseWoodChargeTime.GetWoodChargeTime(woodChargeTime); woodChargeEffectTime = woodRemainChargeTime + 0.5f / towerLevel.ActionAnimator.speed; WoodChargeEffect.Play(); } } if (Targetter.bSearchTarget) { m_Launcher.Launch(m_TrackingEnemy, go, projectilePoints); if (AudioSourceManager.Ins) AudioSourceManager.Ins.Play(audioEnum); } } /// /// A delegate to compare distances of components /// /// /// protected virtual int ByDistance(Targetable first, Targetable second) { float firstSqrMagnitude = Vector3.SqrMagnitude(first.position - epicenter.position); float secondSqrMagnitude = Vector3.SqrMagnitude(second.position - epicenter.position); return firstSqrMagnitude.CompareTo(secondSqrMagnitude); } #if UNITY_EDITOR /// /// Draws the search area /// void OnDrawGizmosSelected() { Gizmos.DrawWireSphere(epicenter.position, towerTargetter.effectRadius); } #endif } /// /// A delegate for boolean calculation logic /// public delegate bool Filter(); }